首页 > 解决方案 > ExpandableRecyclerView - 如何同时展开和折叠多个 CardView

问题描述

是否可以同时展开和折叠多个CardViews RecyclerView?我创建了一个带有两个按钮的标题(用于我的 RecyclerView),但我不确定点击事件中需要做什么。

在此处输入图像描述

在此处输入图像描述

RecyclerView 适配器类

public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;

    public boolean isSupposedToBeCollapsed;

    private Context mContext;

    RecyclerViewHeader header;
    List<RecyclerViewItem> listItems;
    ValueAnimator mAnimator;



    public MyRecyclerAdapter(Context context, RecyclerViewHeader header, List<RecyclerViewItem> listItems)
    {
        this.mContext = context;
        this.header = header;
        this.listItems = listItems;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(viewType == TYPE_HEADER)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_header, parent, false);
            return new MyRecyclerAdapter.VHHeader(v);
        }
        else if(viewType == TYPE_ITEM)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item, parent, false);
            return new MyRecyclerAdapter.VHItem(v);
        }
        throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
    }

    private RecyclerViewItem getItem(int position)
    {
        return listItems.get(position);
    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final Typeface iconFont = FontManager.getTypeface(mContext, FontManager.FONTAWESOME);

        if (holder instanceof VHHeader)
        {
            final VHHeader vhHeader = (VHHeader)holder;

            vhHeader.btnExpandAll.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(isSupposedToBeCollapsed){
                    // change visibility to 'VISIBLE'
                    txtB.setVisibility(View.VISIBLE);

                    // change direction of chevron to 'up'
                    txtExpandCollapse.setText(R.string.fa_icon_chevron_up);

                    // apply animation to the height of 'txtB'
                    mAnimator = slideAnimator(0, textBHeight);

                    // start the animation
                    mAnimator.start();
                    }
                    else{

                    }
                }
            });

            vhHeader.btnCollapseAll.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(isSupposedToBeCollapsed){
                        // change visibility to 'VISIBLE'
                        txtB.setVisibility(View.VISIBLE);

                        // change direction of chevron to 'up'
                        txtExpandCollapse.setText(R.string.fa_icon_chevron_up);

                        // apply animation to the height of 'txtB'
                        mAnimator = slideAnimator(0, textBHeight);

                        // start the animation
                        mAnimator.start();
                    }
                    else{

                    }
                }
            });
        }
        else if (holder instanceof VHItem)
        {

            RecyclerViewItem currentItem = getItem(position-1);
            final VHItem vhItem = (VHItem)holder;

            vhItem.txtA.setText(currentItem.getTitle());
            vhItem.txtB.setText(currentItem.getDescription());

            vhItem.txtB.setVisibility(View.GONE);

            vhItem.txtExpandCollapse.setText(R.string.fa_icon_chevron_down);
            vhItem.txtExpandCollapse.setTypeface(iconFont);

            //Add onPreDrawListener
            vhItem.txtB.getViewTreeObserver().addOnPreDrawListener(
            new ViewTreeObserver.OnPreDrawListener() {

                @Override
                public boolean onPreDraw() {
                    vhItem.txtB.getViewTreeObserver().removeOnPreDrawListener(this);
                    vhItem.txtB.setVisibility(View.GONE);

                    final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                    final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                    vhItem.txtB.measure(widthSpec, heightSpec);

                    vhItem.textBHeight = vhItem.txtB.getMeasuredHeight();

                    return true;
                }
            });

            vhItem.cardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(vhItem.txtB.getVisibility() == View.GONE){
                        vhItem.expand();
                    } else {
                        vhItem.collapse();
                    }
                }
            });

            vhItem.mLinearLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(vhItem.txtB.getVisibility() == View.GONE){
                        vhItem.expand();
                    } else {
                        vhItem.collapse();
                    }
                }
            });

            vhItem.txtExpandCollapse.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(vhItem.txtB.getVisibility() == View.GONE){
                        vhItem.expand();
                    } else {
                        vhItem.collapse();
                    }
                }
            });
        }
    }

    // need to override this method
    @Override
    public int getItemViewType(int position) {
        if(isPositionHeader(position))
            return TYPE_HEADER;
            return TYPE_ITEM;
    }

    private boolean isPositionHeader(int position)
    {
        return position == 0;
    }

    // increasing getItemcount to 1. This will be the row of header.
    @Override
    public int getItemCount() {
        return listItems.size()+1;
    }

    class VHHeader extends RecyclerView.ViewHolder{
        Button btnCollapseAll, btnExpandAll;

        public VHHeader(View headerView) {
            super(headerView);

            this.btnCollapseAll = headerView.findViewById(R.id.btn_collapseall);
            this.btnExpandAll = headerView.findViewById(R.id.btn_expandall);
        }
    }

    public class VHItem extends RecyclerView.ViewHolder{
        CardView cardView;
        LinearLayout mLinearLayout;
        RecyclerView mRecyclerView;
        RelativeLayout mRelativeLayout;
        TextView txtExpandCollapse, txtA, txtB;
        public int textBHeight;

        public VHItem(View itemView) {
            super(itemView);

            this.cardView = itemView.findViewById(R.id.cv);
            this.mLinearLayout = itemView.findViewById(R.id.cardview_tconnections_titlerow);
            this.mRelativeLayout = itemView.findViewById(R.id.my_relativelayout);
            this.mRecyclerView = itemView.findViewById(R.id.my_recyclerview);
            this.txtExpandCollapse = itemView.findViewById(R.id.tv_expandcollapse);
            this.txtA = itemView.findViewById(R.id.tv_A);
            this.txtB = itemView.findViewById(R.id.tv_B);
        }

        private void expand() {
            // change visibility to 'VISIBLE'
            txtB.setVisibility(View.VISIBLE);

            // change direction of chevron to 'up'
            txtExpandCollapse.setText(R.string.fa_icon_chevron_up);

            // apply animation to the height of 'txtB'
            mAnimator = slideAnimator(0, textBHeight);

            // start the animation
            mAnimator.start();
        }

        private void collapse() {
            // change direction of chevron to 'down'
            txtExpandCollapse.setText(R.string.fa_icon_chevron_down);

            int finalHeight = txtB.getHeight();

            ValueAnimator mAnimator = slideAnimator(finalHeight, 0);

            mAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationEnd(Animator animator) {
                    txtB.setVisibility(View.GONE);
                }

                @Override
                public void onAnimationStart(Animator animator) {
                }

                @Override
                public void onAnimationCancel(Animator animator) {
                }

                @Override
                public void onAnimationRepeat(Animator animator) {
                }
            });
            mAnimator.start();
        }


        public ValueAnimator slideAnimator(int start, int end) {

            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 layoutParams = txtB.getLayoutParams();
                    layoutParams.height = value;
                    txtB.setLayoutParams(layoutParams);
                }
            });
            return animator;
        }
    }
}

片段类

public class MyFragment extends android.support.v4.app.Fragment {

    private MyRecyclerAdapter adapter;

    public MyFragment() {}

    @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);
        linearLayoutManager = new LinearLayoutManager(getActivity());

        MyRecyclerAdapter adapter = new MyRecyclerAdapter(getContext(), getHeader(), getListItems());
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);

        super.onActivityCreated(savedInstanceState);
    }

    RecyclerView recyclerView;
    LinearLayoutManager linearLayoutManager;

    public RecyclerViewHeader getHeader()
    {
        return new RecyclerViewHeader();
    }

    public List<RecyclerViewItem> getListItems()
    {
        List<RecyclerViewItem> rvItems = new ArrayList<>();

        RecyclerViewItem itemA = new RecyclerViewItem();
        itemA.setConnectionMode("Item A");
        itemA.setConnectionName("Feature A1");
        rvItems.add(itemA);

        RecyclerViewItem itemB = new RecyclerViewItem();
        itemB.setConnectionMode("Item B");
        itemB.setConnectionName("Feature B1\nFeature B2");
        rvItems.add(itemB);

        RecyclerViewItem itemC = new RecyclerViewItem();
        itemC.setConnectionMode("Item C");
        itemC.setConnectionName("Feature C1\nFeature C2\nFeature C3");
        rvItems.add(itemC);


        return rvItems;
    }
}

标签: javaandroidandroid-recyclerviewandroid-buttonandroid-cardview

解决方案


  1. 您可以做的是,在 RecyclerAdapter 代码中设置一个存储卡片状态(展开/折叠)的变量(布尔值)。

  2. 然后,在您的 onBindViewHolder 代码块中,编写一段代码,使用上述变量和所需的显示代码检查卡片是否应该展开或折叠。

例子:

if(isSuppossedToBeCollapsed){
    textView.setVisibility = false;
}
else{
    textView.setVisibility = true;
}
  1. 然后,您可以在您的 RecyclerAdapter 代码中调用 notifyDatasetChanged 来更新回收站视图,当您单击将重绘视图并相应地反映卡片状态(展开/折叠)的按钮时。

NotifyDatasetChanged 文档


推荐阅读