RecyclerView 内的 CardView 在扩展时未调整到正确的高度
CardView inside RecyclerView not resizing to correct height when expanded
创建 RecyclerView 后,我注意到我的 CardView 在展开时没有调整到正确的高度(Item A,Item B 和 项目 C)。它应该与 txtSubtitle TextView 的高度相同。我认为 txtSubtitleHeight
可能是罪魁祸首,但有谁知道导致此问题的原因以及如何解决它?
public class MyFragmentRV extends android.support.v4.app.Fragment {
public int mGridViewHeight;
// public int txtSubtitleHeight;
private static final int ITEM_TYPE = 100;
private static final int HEADER_TYPE = 101;
private static final int HEADER_TYPE_2 = 102;
private static final int GRID_TYPE = 103;
// GridView mGridViewA;
ValueAnimator mAnimatorGV, mAnimatorTV;
TextView txtArrowGV, txtTitle;
//
static final String[] frenchVowels = new String[]{
"a", "e", "i", "o", "u", "y"
};
public MyFragmentRV.MyAdapter adapterGV;
public MyFragmentRV() {}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_rv, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
View v = getView();
assert v != null;
recyclerView = v.findViewById(R.id.my_recyclerview);
// set the linear layout manager
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
// SpannableStrings
// dynamically change SpannableString colour using defined attribute
int[] attrS = {R.attr.spannablestringtextColor};
TypedArray ta = getActivity().getTheme().obtainStyledAttributes(attrS);
int colorSS = ta.getColor(0, Color.BLACK); //Color.BLACK - default value (colour will change automatically depending on chosen theme)
Log.d(TAG, "clickMethod 1) " + Integer.toHexString(colorSS));
ta.recycle();
// SpannableString (start)
SpannableStringBuilder ssb = new SpannableStringBuilder();
SpannableString str1 = new SpannableString(" Item A ");
str1.setSpan(new BackgroundColorSpan(Color.BLACK), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str1.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.yellow)), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(str1);
SpannableString str2 = new SpannableString(" Hello World");
str2.setSpan(new ForegroundColorSpan(colorSS), 0, str2.length(), 0);
ssb.append(str2);
// SpannableString (end)
// init data
data = new ArrayList<>();
data.add(ssb);
data.add("Item B");
data.add("Item C");
subdata = new ArrayList<>();
subdata.add("\u2022 a");
subdata.add("\u2022 b\n\u2022 bb");
subdata.add("\u2022 c\n\u2022 cc\n\u2022 ccc");
adapter = createAdapter();
recyclerView.setAdapter(adapter);
super.onActivityCreated(savedInstanceState);
}
RecyclerView recyclerView;
ArrayList<CharSequence> data;
ArrayList<String> subdata;
RecyclerView.Adapter<ViewHolder> adapter;
// creates the adapter
private RecyclerView.Adapter<ViewHolder> createAdapter() {
return new RecyclerView.Adapter<ViewHolder>() {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
switch (type) {
case HEADER_TYPE:
return new ViewHolder(inflateHelper(R.layout.header, parent));
case HEADER_TYPE_2:
return new ViewHolder(inflateHelper(R.layout.header, parent));
case ITEM_TYPE:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));
case GRID_TYPE:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_gv, parent));
default:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));
}
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
final Typeface iconFont = FontManager.getTypeface(getContext(), FontManager.FONTAWESOME); // FontManager class must be accessed first before text views can be set as image views
switch (getItemViewType(position)) {
case HEADER_TYPE:
Button expandButton = viewHolder.itemView.findViewById(R.id.button);
expandButton.setText("Expand all");
expandButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
break;
case HEADER_TYPE_2:
Button collapseButton = viewHolder.itemView.findViewById(R.id.button);
collapseButton.setText("Collapse all");
collapseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
break;
case ITEM_TYPE:
// get the current item
CharSequence itemA = data.get(position - 3);
String itemB = subdata.get(position - 3);
txtTitle = viewHolder.itemView.findViewById(R.id.tv_tv_A);
txtTitle.setText(itemA);
final TextView txtSubtitle = viewHolder.itemView.findViewById(R.id.tv_tv_B);
txtSubtitle.setText(itemB);
txtSubtitle.setVisibility(View.GONE);
//Add onPreDrawListener
txtSubtitle.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
txtSubtitle.getViewTreeObserver().removeOnPreDrawListener(this);
txtSubtitle.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
txtSubtitle.measure(widthSpec, heightSpec);
txtSubtitleHeight = txtSubtitle.getMeasuredHeight();
return true;
}
});
final TextView txtArrowTV = viewHolder.itemView.findViewById(R.id.tv_tv_expandcollapse);
txtArrowTV.setText(R.string.fa_icon_chevron_down);
txtArrowTV.setTypeface(iconFont);
//
CardView cardView = viewHolder.itemView.findViewById(R.id.cv_tv);
LinearLayout mLinearLayoutTV = viewHolder.itemView.findViewById(R.id.cardview_tv_titlerow);
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Toast.makeText(getActivity(),"CardView clicked", Toast.LENGTH_SHORT).show();
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
mLinearLayoutTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
txtArrowTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
break;
case GRID_TYPE:
TextView titleG = viewHolder.itemView.findViewById(R.id.tv_gv_A);
titleG.setText("French vowels");
final TextView txtArrowGV = viewHolder.itemView.findViewById(R.id.tv_gv_expandcollapse);
txtArrowGV.setText(R.string.fa_icon_chevron_down);
txtArrowGV.setTypeface(iconFont);
final GridView mGridViewA = viewHolder.itemView.findViewById(R.id.gv);
mGridViewA.setVisibility(View.GONE);
mGridViewA.setEnabled(false);
mGridViewA.setVerticalScrollBarEnabled(false);
adapterGV = new MyFragmentRV.MyAdapter(getActivity().getApplicationContext(), 0);
mGridViewA.setAdapter(adapterGV);
for (String frenchVowel : frenchVowels) {
adapterGV.addAdapterItem(new MyFragmentRV.AdapterItem(frenchVowel));
}
//Add onPreDrawListener
mGridViewA.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mGridViewA.getViewTreeObserver().removeOnPreDrawListener(this);
mGridViewA.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mGridViewA.measure(widthSpec, heightSpec);
mGridViewHeight = mGridViewA.getMeasuredHeight();
return true;
}
});
CardView cardViewG = viewHolder.itemView.findViewById(R.id.cv_gv);
LinearLayout mLinearLayoutGV = viewHolder.itemView.findViewById(R.id.cardview_gv_titlerow);
cardViewG.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
mLinearLayoutGV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
txtArrowGV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
break;
}
}
@Override
public int getItemCount() {
return data.size() + 3;
}
@Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return HEADER_TYPE;
case 1:
return HEADER_TYPE_2;
case 2:
return GRID_TYPE;
default: return ITEM_TYPE;
}
}
};
}
private View inflateHelper(int resId, ViewGroup parent) {
return LayoutInflater.from(getActivity()).inflate(resId, parent, false);
}
// inner class for viewholder to use,
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
}
public void expandGV(final GridView mGridViewA) {
mGridViewA.setVisibility(View.VISIBLE);
txtArrowGV.setText(R.string.fa_icon_chevron_up);
ValueAnimator mAnimatorGV = slideAnimator(0, mGridViewHeight, mGridViewA);
mAnimatorGV.start();
}
public void collapseGV(final GridView mGridViewA) {
txtArrowGV.setText(R.string.fa_icon_chevron_down);
int finalGVHeight = mGridViewA.getHeight();
mAnimatorGV = slideAnimator(finalGVHeight, 0, mGridViewA);
mAnimatorGV.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
mGridViewA.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimatorGV.start();
}
public void expandTV(final TextView txtSubtitle, final TextView txtArrowTV) {
txtSubtitle.setVisibility(View.VISIBLE);
txtArrowTV.setText(R.string.fa_icon_chevron_up);
mAnimatorTV = slideAnimator(0, txtSubtitleHeight, txtSubtitle);
mAnimatorTV.start();
}
public void collapseTV(final TextView txtSubtitle, final TextView txtArrowTV) {
txtArrowTV.setText(R.string.fa_icon_chevron_down);
int finalTVHeight = txtSubtitle.getHeight();
mAnimatorTV = slideAnimator(finalTVHeight, 0, txtSubtitle);
mAnimatorTV.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
txtSubtitle.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimatorTV.start();
}
public ValueAnimator slideAnimator(int start, int end, final View txtSubtitle) {
final ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// update height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParamsTV = txtSubtitle.getLayoutParams();
layoutParamsTV.height = value;
txtSubtitle.setLayoutParams(layoutParamsTV);
// ViewGroup.LayoutParams layoutParamsGV = mGridViewA.getLayoutParams();
// layoutParamsGV.height = value;
// mGridViewA.setLayoutParams(layoutParamsGV);
}
});
return animator;
}
}
XML
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:id="@+id/cv_tv"
android:layout_marginBottom="20dp"
>
<LinearLayout
android:id="@+id/cardview_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:animateLayoutChanges="true">
<LinearLayout
android:id="@+id/cardview_tv_titlerow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="2dp"
android:weightSum="100">
<TextView
android:id="@+id/tv_tv_A"
android:layout_weight="90"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
style="@android:style/TextAppearance.Medium" />
<TextView
android:id="@+id/tv_tv_expandcollapse"
android:clickable="true"
android:focusable="true"
android:layout_weight="10"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="?android:attr/textColorPrimary"
style="@android:style/TextAppearance.Large" />
</LinearLayout>
<RelativeLayout
android:id="@+id/relativelayout_tv"
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_tv_B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
style="@android:style/TextAppearance.Large" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
麦克米斯特的建议
好的,看来你的功能是获取不正确的高度。
实际上,通过重读您的代码,我想我可能已经了解了正在发生的事情。通过您完成的测试,我们确定问题出在检索大小的函数方面。现在,在您的演示 gif 上,所有展开的视图都具有相同的大小,对于最后一张卡片,它似乎具有您想要的大小,文本后没有任何 space。
根据这些信息,我的猜测如下:
- 您确定高度的函数确实有效
- 但是您只有一个定义该大小的变量 (txtSubtitleHeight)
- 因此对于每张卡片,计算高度并将 txtSubtitleHeight 替换为该卡片的高度
- 最后,txtSubtitleHeight 是最后看到的卡片的高度。当您使用此变量扩展每张卡片时,每张卡片的高度都与最后一张卡片的高度相同。
因此,基于此,如果我没记错的话,一个简单的解决方法是为每张卡片设置一个高度变量,也就是为适配器中的每个取景器设置一个高度变量。
你可以这样做:
// inner class for viewholder to use,
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
int height;
}
然后您可以像这样更新您的 onPreDraw 函数:
@Override
public boolean onPreDraw() {
txtSubtitle.getViewTreeObserver().removeOnPreDrawListener(this);
txtSubtitle.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
txtSubtitle.measure(widthSpec, heightSpec);
viewHolder.height = txtSubtitle.getMeasuredHeight();
return true;
}
最后在您的 expandTV 中
public void expandTV(final TextView txtSubtitle, final TextView txtArrowTV, final ViewHolder holder) {
txtSubtitle.setVisibility(View.VISIBLE);
txtArrowTV.setText(R.string.fa_icon_chevron_up);
mAnimatorTV = slideAnimator(0, holder.height, txtSubtitle);
mAnimatorTV.start();
}
而且我相信它应该可以解决您的问题:)
创建 RecyclerView 后,我注意到我的 CardView 在展开时没有调整到正确的高度(Item A,Item B 和 项目 C)。它应该与 txtSubtitle TextView 的高度相同。我认为 txtSubtitleHeight
可能是罪魁祸首,但有谁知道导致此问题的原因以及如何解决它?
public class MyFragmentRV extends android.support.v4.app.Fragment {
public int mGridViewHeight;
// public int txtSubtitleHeight;
private static final int ITEM_TYPE = 100;
private static final int HEADER_TYPE = 101;
private static final int HEADER_TYPE_2 = 102;
private static final int GRID_TYPE = 103;
// GridView mGridViewA;
ValueAnimator mAnimatorGV, mAnimatorTV;
TextView txtArrowGV, txtTitle;
//
static final String[] frenchVowels = new String[]{
"a", "e", "i", "o", "u", "y"
};
public MyFragmentRV.MyAdapter adapterGV;
public MyFragmentRV() {}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_rv, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
View v = getView();
assert v != null;
recyclerView = v.findViewById(R.id.my_recyclerview);
// set the linear layout manager
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
// SpannableStrings
// dynamically change SpannableString colour using defined attribute
int[] attrS = {R.attr.spannablestringtextColor};
TypedArray ta = getActivity().getTheme().obtainStyledAttributes(attrS);
int colorSS = ta.getColor(0, Color.BLACK); //Color.BLACK - default value (colour will change automatically depending on chosen theme)
Log.d(TAG, "clickMethod 1) " + Integer.toHexString(colorSS));
ta.recycle();
// SpannableString (start)
SpannableStringBuilder ssb = new SpannableStringBuilder();
SpannableString str1 = new SpannableString(" Item A ");
str1.setSpan(new BackgroundColorSpan(Color.BLACK), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str1.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.yellow)), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(str1);
SpannableString str2 = new SpannableString(" Hello World");
str2.setSpan(new ForegroundColorSpan(colorSS), 0, str2.length(), 0);
ssb.append(str2);
// SpannableString (end)
// init data
data = new ArrayList<>();
data.add(ssb);
data.add("Item B");
data.add("Item C");
subdata = new ArrayList<>();
subdata.add("\u2022 a");
subdata.add("\u2022 b\n\u2022 bb");
subdata.add("\u2022 c\n\u2022 cc\n\u2022 ccc");
adapter = createAdapter();
recyclerView.setAdapter(adapter);
super.onActivityCreated(savedInstanceState);
}
RecyclerView recyclerView;
ArrayList<CharSequence> data;
ArrayList<String> subdata;
RecyclerView.Adapter<ViewHolder> adapter;
// creates the adapter
private RecyclerView.Adapter<ViewHolder> createAdapter() {
return new RecyclerView.Adapter<ViewHolder>() {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
switch (type) {
case HEADER_TYPE:
return new ViewHolder(inflateHelper(R.layout.header, parent));
case HEADER_TYPE_2:
return new ViewHolder(inflateHelper(R.layout.header, parent));
case ITEM_TYPE:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));
case GRID_TYPE:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_gv, parent));
default:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));
}
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
final Typeface iconFont = FontManager.getTypeface(getContext(), FontManager.FONTAWESOME); // FontManager class must be accessed first before text views can be set as image views
switch (getItemViewType(position)) {
case HEADER_TYPE:
Button expandButton = viewHolder.itemView.findViewById(R.id.button);
expandButton.setText("Expand all");
expandButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
break;
case HEADER_TYPE_2:
Button collapseButton = viewHolder.itemView.findViewById(R.id.button);
collapseButton.setText("Collapse all");
collapseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
break;
case ITEM_TYPE:
// get the current item
CharSequence itemA = data.get(position - 3);
String itemB = subdata.get(position - 3);
txtTitle = viewHolder.itemView.findViewById(R.id.tv_tv_A);
txtTitle.setText(itemA);
final TextView txtSubtitle = viewHolder.itemView.findViewById(R.id.tv_tv_B);
txtSubtitle.setText(itemB);
txtSubtitle.setVisibility(View.GONE);
//Add onPreDrawListener
txtSubtitle.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
txtSubtitle.getViewTreeObserver().removeOnPreDrawListener(this);
txtSubtitle.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
txtSubtitle.measure(widthSpec, heightSpec);
txtSubtitleHeight = txtSubtitle.getMeasuredHeight();
return true;
}
});
final TextView txtArrowTV = viewHolder.itemView.findViewById(R.id.tv_tv_expandcollapse);
txtArrowTV.setText(R.string.fa_icon_chevron_down);
txtArrowTV.setTypeface(iconFont);
//
CardView cardView = viewHolder.itemView.findViewById(R.id.cv_tv);
LinearLayout mLinearLayoutTV = viewHolder.itemView.findViewById(R.id.cardview_tv_titlerow);
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Toast.makeText(getActivity(),"CardView clicked", Toast.LENGTH_SHORT).show();
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
mLinearLayoutTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
txtArrowTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
break;
case GRID_TYPE:
TextView titleG = viewHolder.itemView.findViewById(R.id.tv_gv_A);
titleG.setText("French vowels");
final TextView txtArrowGV = viewHolder.itemView.findViewById(R.id.tv_gv_expandcollapse);
txtArrowGV.setText(R.string.fa_icon_chevron_down);
txtArrowGV.setTypeface(iconFont);
final GridView mGridViewA = viewHolder.itemView.findViewById(R.id.gv);
mGridViewA.setVisibility(View.GONE);
mGridViewA.setEnabled(false);
mGridViewA.setVerticalScrollBarEnabled(false);
adapterGV = new MyFragmentRV.MyAdapter(getActivity().getApplicationContext(), 0);
mGridViewA.setAdapter(adapterGV);
for (String frenchVowel : frenchVowels) {
adapterGV.addAdapterItem(new MyFragmentRV.AdapterItem(frenchVowel));
}
//Add onPreDrawListener
mGridViewA.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mGridViewA.getViewTreeObserver().removeOnPreDrawListener(this);
mGridViewA.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mGridViewA.measure(widthSpec, heightSpec);
mGridViewHeight = mGridViewA.getMeasuredHeight();
return true;
}
});
CardView cardViewG = viewHolder.itemView.findViewById(R.id.cv_gv);
LinearLayout mLinearLayoutGV = viewHolder.itemView.findViewById(R.id.cardview_gv_titlerow);
cardViewG.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
mLinearLayoutGV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
txtArrowGV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
break;
}
}
@Override
public int getItemCount() {
return data.size() + 3;
}
@Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return HEADER_TYPE;
case 1:
return HEADER_TYPE_2;
case 2:
return GRID_TYPE;
default: return ITEM_TYPE;
}
}
};
}
private View inflateHelper(int resId, ViewGroup parent) {
return LayoutInflater.from(getActivity()).inflate(resId, parent, false);
}
// inner class for viewholder to use,
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
}
public void expandGV(final GridView mGridViewA) {
mGridViewA.setVisibility(View.VISIBLE);
txtArrowGV.setText(R.string.fa_icon_chevron_up);
ValueAnimator mAnimatorGV = slideAnimator(0, mGridViewHeight, mGridViewA);
mAnimatorGV.start();
}
public void collapseGV(final GridView mGridViewA) {
txtArrowGV.setText(R.string.fa_icon_chevron_down);
int finalGVHeight = mGridViewA.getHeight();
mAnimatorGV = slideAnimator(finalGVHeight, 0, mGridViewA);
mAnimatorGV.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
mGridViewA.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimatorGV.start();
}
public void expandTV(final TextView txtSubtitle, final TextView txtArrowTV) {
txtSubtitle.setVisibility(View.VISIBLE);
txtArrowTV.setText(R.string.fa_icon_chevron_up);
mAnimatorTV = slideAnimator(0, txtSubtitleHeight, txtSubtitle);
mAnimatorTV.start();
}
public void collapseTV(final TextView txtSubtitle, final TextView txtArrowTV) {
txtArrowTV.setText(R.string.fa_icon_chevron_down);
int finalTVHeight = txtSubtitle.getHeight();
mAnimatorTV = slideAnimator(finalTVHeight, 0, txtSubtitle);
mAnimatorTV.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
txtSubtitle.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimatorTV.start();
}
public ValueAnimator slideAnimator(int start, int end, final View txtSubtitle) {
final ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// update height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParamsTV = txtSubtitle.getLayoutParams();
layoutParamsTV.height = value;
txtSubtitle.setLayoutParams(layoutParamsTV);
// ViewGroup.LayoutParams layoutParamsGV = mGridViewA.getLayoutParams();
// layoutParamsGV.height = value;
// mGridViewA.setLayoutParams(layoutParamsGV);
}
});
return animator;
}
}
XML
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:id="@+id/cv_tv"
android:layout_marginBottom="20dp"
>
<LinearLayout
android:id="@+id/cardview_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:animateLayoutChanges="true">
<LinearLayout
android:id="@+id/cardview_tv_titlerow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="2dp"
android:weightSum="100">
<TextView
android:id="@+id/tv_tv_A"
android:layout_weight="90"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
style="@android:style/TextAppearance.Medium" />
<TextView
android:id="@+id/tv_tv_expandcollapse"
android:clickable="true"
android:focusable="true"
android:layout_weight="10"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="?android:attr/textColorPrimary"
style="@android:style/TextAppearance.Large" />
</LinearLayout>
<RelativeLayout
android:id="@+id/relativelayout_tv"
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_tv_B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
style="@android:style/TextAppearance.Large" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
麦克米斯特的建议
好的,看来你的功能是获取不正确的高度。 实际上,通过重读您的代码,我想我可能已经了解了正在发生的事情。通过您完成的测试,我们确定问题出在检索大小的函数方面。现在,在您的演示 gif 上,所有展开的视图都具有相同的大小,对于最后一张卡片,它似乎具有您想要的大小,文本后没有任何 space。
根据这些信息,我的猜测如下:
- 您确定高度的函数确实有效
- 但是您只有一个定义该大小的变量 (txtSubtitleHeight)
- 因此对于每张卡片,计算高度并将 txtSubtitleHeight 替换为该卡片的高度
- 最后,txtSubtitleHeight 是最后看到的卡片的高度。当您使用此变量扩展每张卡片时,每张卡片的高度都与最后一张卡片的高度相同。
因此,基于此,如果我没记错的话,一个简单的解决方法是为每张卡片设置一个高度变量,也就是为适配器中的每个取景器设置一个高度变量。
你可以这样做:
// inner class for viewholder to use,
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
int height;
}
然后您可以像这样更新您的 onPreDraw 函数:
@Override
public boolean onPreDraw() {
txtSubtitle.getViewTreeObserver().removeOnPreDrawListener(this);
txtSubtitle.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
txtSubtitle.measure(widthSpec, heightSpec);
viewHolder.height = txtSubtitle.getMeasuredHeight();
return true;
}
最后在您的 expandTV 中
public void expandTV(final TextView txtSubtitle, final TextView txtArrowTV, final ViewHolder holder) {
txtSubtitle.setVisibility(View.VISIBLE);
txtArrowTV.setText(R.string.fa_icon_chevron_up);
mAnimatorTV = slideAnimator(0, holder.height, txtSubtitle);
mAnimatorTV.start();
}
而且我相信它应该可以解决您的问题:)