Android:Extending CardView 可以展开哪些内容
Android: Extending CardView which content can be expanded
我将 android CardView 扩展为可扩展版本,带有 header 标题和可旋转的图标。
代码
这个文件(view_cardview_header.xml)包含ExpandableCardView的header,它应该是第一个child并且没有折叠。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" />
</RelativeLayout>
</LinearLayout>
具有自定义 xml 参数的 attrs.xml 文件
<resources>
<declare-styleable name="ExpandableCardView">
<attr name="expanded" format="boolean" />
<attr name="headerTitle" format="string" />
<attr name="headerIcon" format="integer" />
</declare-styleable>
</resources>
ExpandableCardView.java class
public class ExpandableCardView extends CardView {
private static final float ROTATION_NORMAL = 0.0f;
private static final float ROTATION_ROTATED = 180f;
private static final float PIVOT_VALUE = 0.5f;
private static final long ROTATE_DURATION = 200;
private boolean isExpanded;
public ExpandableCardView(Context context) {
this(context, null);
}
public ExpandableCardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ExpandableCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ExpandableCardView, 0, 0);
String titleText = a.getString(R.styleable.ExpandableCardView_headerTitle);
final Drawable drawable = a.getDrawable(R.styleable.ExpandableCardView_headerIcon);
a.recycle();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_cardview_header, this, true);
final LinearLayout parent = (LinearLayout) getChildAt(0);
final RelativeLayout header = (RelativeLayout) parent.getChildAt(0);
final TextView titleTextView = (TextView) header.getChildAt(0);
titleTextView.setText(titleText);
final ImageView toggle = (ImageView) header.getChildAt(1);
if(drawable != null) {
toggle.setImageDrawable(drawable);
}
header.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setExpanded(toggle, !isExpanded);
onExpansionToggled(toggle);
}
});
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if(getChildAt(0) == null || getChildAt(0).equals(child)) {
super.addView(child, index, params);
} else {
((LinearLayout) getChildAt(0)).addView(child);
}
}
private void setExpanded(ImageView toggle, boolean expanded) {
isExpanded = expanded;
final LinearLayout parent = (LinearLayout) getChildAt(0);
final int childCount = parent.getChildCount();
if(expanded) {
toggle.setRotation(ROTATION_ROTATED);
for(int i = childCount - 1; i > 0; i--) {
parent.getChildAt(i).setVisibility(VISIBLE);
}
} else {
toggle.setRotation(ROTATION_NORMAL);
for(int i = 1; i < childCount; i++) {
parent.getChildAt(i).setVisibility(GONE);
}
}
}
private void onExpansionToggled(ImageView toggle) {
RotateAnimation rotateAnimation = new RotateAnimation(ROTATION_ROTATED, ROTATION_NORMAL,
RotateAnimation.RELATIVE_TO_SELF, PIVOT_VALUE, RotateAnimation.RELATIVE_TO_SELF,
PIVOT_VALUE);
rotateAnimation.setDuration(ROTATE_DURATION);
rotateAnimation.setFillAfter(true);
toggle.startAnimation(rotateAnimation);
}
}
用于测试新 CardView 的片段布局(fragment_test.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tecdroid.views.ExpandableCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/card_margin"
android:layout_marginBottom="@dimen/card_margin"
android:layout_marginLeft="@dimen/card_margin"
android:layout_marginRight="@dimen/card_margin"
app:headerTitle="TITLE"
app:headerIcon="@mipmap/ic_action_expand">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="First inner child"/>
</com.tecdroid.views.ExpandableCardView>
</LinearLayout>
Result on Emulator
问题
正如您在图片上看到的,第一个内部 child(TextView) 没有放置在相对布局 (Header) 下。
首先我认为我必须重写 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法,但是 ExpandableCardView 间接扩展了 ViewGroup,其中 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法为我做了这一切。
也许有人知道我忘记了什么,或者做错了什么。
更新
问题已解决,查看变化。
CardView 扩展了 FrameLayout,您的页眉和第一个子视图只是不相关的子视图。要在子项之间建立空间关系,您需要使用带有 align-* 参数的 LinearLayout 或 RelativeLayout。
为什么不用你的卡片视图和你选择的包含子视图的布局创建一个复合视图?
我将 android CardView 扩展为可扩展版本,带有 header 标题和可旋转的图标。
代码
这个文件(view_cardview_header.xml)包含ExpandableCardView的header,它应该是第一个child并且没有折叠。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" />
</RelativeLayout>
</LinearLayout>
具有自定义 xml 参数的 attrs.xml 文件
<resources>
<declare-styleable name="ExpandableCardView">
<attr name="expanded" format="boolean" />
<attr name="headerTitle" format="string" />
<attr name="headerIcon" format="integer" />
</declare-styleable>
</resources>
ExpandableCardView.java class
public class ExpandableCardView extends CardView {
private static final float ROTATION_NORMAL = 0.0f;
private static final float ROTATION_ROTATED = 180f;
private static final float PIVOT_VALUE = 0.5f;
private static final long ROTATE_DURATION = 200;
private boolean isExpanded;
public ExpandableCardView(Context context) {
this(context, null);
}
public ExpandableCardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ExpandableCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ExpandableCardView, 0, 0);
String titleText = a.getString(R.styleable.ExpandableCardView_headerTitle);
final Drawable drawable = a.getDrawable(R.styleable.ExpandableCardView_headerIcon);
a.recycle();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_cardview_header, this, true);
final LinearLayout parent = (LinearLayout) getChildAt(0);
final RelativeLayout header = (RelativeLayout) parent.getChildAt(0);
final TextView titleTextView = (TextView) header.getChildAt(0);
titleTextView.setText(titleText);
final ImageView toggle = (ImageView) header.getChildAt(1);
if(drawable != null) {
toggle.setImageDrawable(drawable);
}
header.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setExpanded(toggle, !isExpanded);
onExpansionToggled(toggle);
}
});
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if(getChildAt(0) == null || getChildAt(0).equals(child)) {
super.addView(child, index, params);
} else {
((LinearLayout) getChildAt(0)).addView(child);
}
}
private void setExpanded(ImageView toggle, boolean expanded) {
isExpanded = expanded;
final LinearLayout parent = (LinearLayout) getChildAt(0);
final int childCount = parent.getChildCount();
if(expanded) {
toggle.setRotation(ROTATION_ROTATED);
for(int i = childCount - 1; i > 0; i--) {
parent.getChildAt(i).setVisibility(VISIBLE);
}
} else {
toggle.setRotation(ROTATION_NORMAL);
for(int i = 1; i < childCount; i++) {
parent.getChildAt(i).setVisibility(GONE);
}
}
}
private void onExpansionToggled(ImageView toggle) {
RotateAnimation rotateAnimation = new RotateAnimation(ROTATION_ROTATED, ROTATION_NORMAL,
RotateAnimation.RELATIVE_TO_SELF, PIVOT_VALUE, RotateAnimation.RELATIVE_TO_SELF,
PIVOT_VALUE);
rotateAnimation.setDuration(ROTATE_DURATION);
rotateAnimation.setFillAfter(true);
toggle.startAnimation(rotateAnimation);
}
}
用于测试新 CardView 的片段布局(fragment_test.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tecdroid.views.ExpandableCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/card_margin"
android:layout_marginBottom="@dimen/card_margin"
android:layout_marginLeft="@dimen/card_margin"
android:layout_marginRight="@dimen/card_margin"
app:headerTitle="TITLE"
app:headerIcon="@mipmap/ic_action_expand">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="First inner child"/>
</com.tecdroid.views.ExpandableCardView>
</LinearLayout>
Result on Emulator
问题
正如您在图片上看到的,第一个内部 child(TextView) 没有放置在相对布局 (Header) 下。
首先我认为我必须重写 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法,但是 ExpandableCardView 间接扩展了 ViewGroup,其中 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法为我做了这一切。
也许有人知道我忘记了什么,或者做错了什么。
更新 问题已解决,查看变化。
CardView 扩展了 FrameLayout,您的页眉和第一个子视图只是不相关的子视图。要在子项之间建立空间关系,您需要使用带有 align-* 参数的 LinearLayout 或 RelativeLayout。
为什么不用你的卡片视图和你选择的包含子视图的布局创建一个复合视图?