单击时 CardView expanded/collapsed 不正确

Incorrect CardView expanded/collapsed on click

出于某种原因,项目 C CardView 是唯一一个动画 - 即使 项目 B 或单击 项目 A CardView法语元音 CardView 不适用,因为它工作正常。有谁知道如何解决这个问题以便正确的 CardView 动画?

GridViewCustom class 用于 GridView

public class GridViewCustom extends GridView {
    public GridViewCustom(Context context) {
        super(context);
    }

    public GridViewCustom(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public GridViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int heightSpec;

        if (getLayoutParams().height == AbsListView.LayoutParams.WRAP_CONTENT) {
            heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        }
        else {
            //Any other height should be respected as is.
            heightSpec = heightMeasureSpec;
        }

        super.onMeasure(widthMeasureSpec, heightSpec);
    }
}

片段class

public class MyFragmentRV extends android.support.v4.app.Fragment {
    private Boolean mCurrentValue;

    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;
    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
        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);

                switch (getItemViewType(position)) {
                    case HEADER_TYPE:
                        Button expandButton = viewHolder.itemView.findViewById(R.id.button);
                        expandButton.setText("Expand all");
                    break;
                    case HEADER_TYPE_2:
                        Button collapseButton = viewHolder.itemView.findViewById(R.id.button);
                        collapseButton.setText("Collapse all");
                    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) {
                                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");

                        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 fVowel : fVowels) {
                            adapterGV.addAdapterItem(new MyFragmentRV.AdapterItem(fVowel));
                        }


                        //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);
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

    public void expandGV() {
        mGridViewA.setVisibility(View.VISIBLE);
        txtArrowGV.setText(R.string.fa_icon_chevron_up);
        ValueAnimator mAnimatorGV = slideAnimator(0, mGridViewHeight);
        mAnimatorGV.start();
    }
    public void collapseGV() {
        txtArrowGV.setText(R.string.fa_icon_chevron_down);

        int finalGVHeight = mGridViewA.getHeight();

        mAnimatorGV = slideAnimator(finalGVHeight, 0);
        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() {
        txtSubtitle.setVisibility(View.VISIBLE);
        txtArrowTV.setText(R.string.fa_icon_chevron_up);
        mAnimatorTV = slideAnimator(0, txtSubtitleHeight);
        mAnimatorTV.start();
    }
    public void collapseTV() {
        txtArrowTV.setText(R.string.fa_icon_chevron_down);
        int finalTVHeight = txtSubtitle.getHeight();
        mAnimatorTV = slideAnimator(finalTVHeight, 0);
        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);
            }
        });
        return animator;
    }

    /// Adapter for GridView
    private class MyAdapter extends ArrayAdapter<MyFragmentRV.AdapterItem> {
        private List<MyFragmentRV.AdapterItem> items = new ArrayList<>();

        MyAdapter(Context context, int textviewid) {
            super(context, textviewid);
        }

        void addAdapterItem(MyFragmentRV.AdapterItem item) {
            items.add(item);
        }

        @Override
        public int getCount() {
            return items.size();
        }

        @Override
        public MyFragmentRV.AdapterItem getItem(int position) {
            return ((null != items) ? items.get(position) : null);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @NonNull
        @Override
        public View getView(final int position, View convertView, @NonNull final ViewGroup parent) {
            View rowView;
            if (convertView == null) {
                rowView = getActivity().getLayoutInflater().inflate(R.layout.gridview_item, parent, false);
            } else {
                rowView = convertView;
            }

            TextView tv = rowView.findViewById(R.id.item_gridview);
            tv.setText(items.get(position).first);

            return rowView;
        }
    }

    public class AdapterItem {
        String first;

        //add more items
        AdapterItem(String first) {
            this.first = first;
        }
    }
}

根据 Nikhil 的建议

这里的问题是您将 txtSubtitle 存储为片段的成员变量。因此,当 GridView 完成初始化您的网格项目时,txtSubtitle 将拥有最后分配的 TextView,在您的情况下是 Item C。因此,当您执行动画时,它总是会应用于 Item C。这里的解决方案是让 txtSubtitle 成为 local final 变量。从 MyFragmentRV 中删除 TextView txtArrowGV, txtArrowTV, txtTitle, txtSubtitle;。如果 ITEM_TYPE:

final TextView txtSubtitle = viewHolder.itemView.findViewById(R.id.tv_tv_B);
....


 cardView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if(txtSubtitle.getVisibility() == View.GONE){
                                expandTV(txtSubtitle);
                            } else {
                                collapseTV(txtSubtitle);
                            }
                        }
                    });

txtSubtitle 传递给 expandTVcollapseTV 并在 expandTV:

public void expandTV(TextView txtSubtitle) {
        txtSubtitle.setVisibility(View.VISIBLE);
        txtArrowTV.setText(R.string.fa_icon_chevron_up);
        mAnimatorTV = slideAnimator(0, txtSubtitleHeight);
        mAnimatorTV.start();
    }

编辑: 将幻灯片动画函数更改为:

public ValueAnimator slideAnimator(int start, int end,TextView 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);
        }
    });
    return animator;
}

并这样称呼它:

mAnimatorTV = slideAnimator(0, txtSubtitleHeight,txtSubtitle);
mAnimatorTV.start();