简体   繁体   English

展开后,RecyclerView内部的CardView不能调整为正确的高度

[英]CardView inside RecyclerView not resizing to correct height when expanded

After creating a RecyclerView, I've noticed that my CardView does not resize to the correct height when I expand it ( Item A , Item B and Item C ). 创建RecyclerView之后,我注意到我的CardView展开时( 项目A项目B项目C )没有调整为正确的高度。 It should be the same height as the txtSubtitle TextView. 它的高度应与txtSubtitle TextView的高度相同。 I think that txtSubtitleHeight could be the culprit but does anyone know what is causing this problem and how to resolve it? 我认为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

<?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>

Macmist's suggestion 麦克米斯特的建议

在此处输入图片说明

Ok so it seems that it's your function to get the height that is not correct. 好的,看来这是您获得不正确高度的功能。 Actually By rereading your code, I think I might have found an idea of what is going on. 实际上,通过重新阅读您的代码,我想我可能已经对发生的事情有所了解。 With the tests you have done we've determined that the issue was on the side of the function that retrieve the size. 通过测试,我们确定问题出在检索大小的函数方面。 Now, on your demonstrating gif, all the expanded views have the same size, and for the last card, it seems to have the size you want, which is not having any space after the text. 现在,在演示gif上,所有展开的视图都具有相同的大小,对于最后一张卡片,它似乎具有所需的大小,该文本后没有空格。

So based on those information, my guess is the following: 因此,基于这些信息,我的猜测如下:

  • your function to determine the height actually works 您确定高度的功能实际上起作用
  • BUT you have only one variable defining that size (txtSubtitleHeight) 但是您只有一个变量定义该大小(txtSubtitleHeight)
  • So for each card, the height is calculated and txtSubtitleHeight is replaced by the height of that card 因此,对于每张卡,都会计算高度,然后用该卡的高度替换txtSubtitleHeight
  • And in the end, txtSubtitleHeight has the height of the last card seen. 最后,txtSubtitleHeight具有最后看到的卡片的高度。 And as you are using this variable for expanding each card, each card will have the height from that last card seen. 而且,当您使用此变量扩展每张卡时,每张卡的高度都将与最后一张卡的高度成正比。

So, based on that, if I'm not mistaken, a simple fix would be to have one height variable for each card, so one for each viewholder from your adapter. 因此,基于此,如果我没记错的话,一个简单的解决方法是为每个卡设置一个高度变量,从而为适配器中的每个视口持有者设置一个高度变量。

You could do like: 您可以这样做:

   // inner class for viewholder to use,
    class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
        }
        int height;
    }

then you can update your onPreDraw function like this: 那么您可以像这样更新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;
}

And finally in your expandTV 最后在您的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();
}

And I believe it should solve your issue :) 而且我相信它应该可以解决您的问题:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM