RecyclerView item 可展開動畫效果的實現

前文提要:

Android list 列表裏面空間的顯示和 隱藏,基本都是用的View.VISIBLE 和 View.GONE 實現的,展示的效果有點突兀,看了ios 同事做的相同的效果,他們的很順暢,所以決定做一個相同的效果.

在這裏插入圖片描述

已經上傳到github 上面地址是 demo的項目地址 :https://github.com/luhui2014/ExpandableViewHolder/tree/master

1.相關說明:

參考資料:Android—RecyclerView之動畫(工具類)實現可展開列表

1-1.佈局文件:

將需要展開收縮的那部分佈局的透明度在xml文件裏默認設置爲0,在代碼中設置一樣
在這裏插入圖片描述

1-2.動畫工具類說明(代碼我基本上都添加了註釋):

這裏我就不贅述了,請參考原文 Android—RecyclerView之動畫(工具類)實現可展開列表

相關原理就是:利用屬性動畫,動態計算view展開後的高度,實現動畫效果。中間插了一段alpha 的動畫,爲了過渡顯示,關鍵代碼:

//OpenHolder中動畫的具體操作方法
    public static Animator ofItemViewHeight(RecyclerView.ViewHolder holder) {
        View parent = (View) holder.itemView.getParent();
        if (parent == null)
            throw new IllegalStateException("Cannot animate the layout of a view that has no parent");

        //測量擴展動畫的起始高度和結束高度
        int start = holder.itemView.getMeasuredHeight();
        holder.itemView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(),
                View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        int end = holder.itemView.getMeasuredHeight();
        final Animator animator = LayoutAnimator.ofHeight(holder.itemView, start, end); //具體的展開動畫

        //設定該Item在動畫開始結束和取消時能否被recycle
        animator.addListener(new ViewHolderAnimatorListener(holder));

        //設定結束時這個Item的寬高
        animator.addListener(new LayoutParamsAnimatorListener(holder.itemView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        return animator;


    }

還有一段比較有意思的地方是 處理了recyclerView 的的回收,增加了一個動畫監聽,在動畫結束的時候,讓recyclerView 自身去處理是否回收的問題

public static class ViewHolderAnimatorListener extends AnimatorListenerAdapter {
        private final RecyclerView.ViewHolder mHolder; //holder對象

        //設定在動畫開始結束和取消狀態下是否可以被回收
        public ViewHolderAnimatorListener(RecyclerView.ViewHolder holder) {
            mHolder = holder;
        }

        @Override
        public void onAnimationStart(Animator animation) { //開始時
            mHolder.setIsRecyclable(false);
        }

        @Override
        public void onAnimationEnd(Animator animation) { //結束時
            mHolder.setIsRecyclable(true);
        }

        @Override
        public void onAnimationCancel(Animator animation) { //取消時
            mHolder.setIsRecyclable(true);
        }
    }

1-3.問題:

但是原作裏沒有處理好展開和收縮緩存的問題,已經解耦的問題。對應展開item之後是否需要其他的動畫,這裏應該開放出來,自行去實現,所以我這裏就改了一下:

  /**
         * 響應ViewHolder的點擊事件
         *
         * @param holder holder對象
         */
        @SuppressWarnings("unchecked")
        public void toggle(VH holder) {
            int position = holder.getPosition();
            if (explanedList.contains(position + "")) {
                opened = -1;
                deletePositionInExpaned(position);

                holder.doCustomAnim(true);
                ExpandableViewHoldersUtil.getInstance().closeHolder(holder, holder.getExpandView(), true);
            } else {
                preOpen = opened;
                opened = position;

                addPositionInExpaned(position);
                holder.doCustomAnim(false);
                ExpandableViewHoldersUtil.getInstance().openHolder(holder, holder.getExpandView(), true);

                //是否要關閉上一個
                if (needExplanedOnlyOne && preOpen != position) {
                    final VH oldHolder = (VH) ((RecyclerView) holder.itemView.getParent()).findViewHolderForPosition(preOpen);
                    if (oldHolder != null) {
                        Log.e("KeepOneHolder", "oldHolder != null");
                        ExpandableViewHoldersUtil.getInstance().closeHolder(oldHolder, oldHolder.getExpandView(), true);
                        deletePositionInExpaned(preOpen);
                    }
                }
            }
        }
    }

開放出來一個回調接口處理:holder.doCustomAnim(true);,根據需要自行增加相關的動畫;

對於記錄展開和收縮狀態的問題,定義了一個全局的變量用於存儲,初始化的時候,進行判斷。在用戶點擊動畫的時候,進行相應的增加和刪除處理:

在這裏插入圖片描述
這裏用String 記錄是爲了處理刪除的時候數據越界的問題:

private void deletePositionInExpaned(int pos) {
        //remove Object 直接寫int,會變成index,造成數組越界
        explanedList.remove(pos + "");
    }

在這裏插入圖片描述
源碼中有這麼一段,直接刪除int 會存在數值越界的問題;

2.如何使用:

2-1.viewHoler 需要實現 ExpandableViewHoldersUtil.Expandable 接口

回調的view,就是處理展開動畫的view,

不要忘記初始化 keepOne = ExpandableViewHoldersUtil.getInstance().getKeepOneHolder();

class ViewHolder extends RecyclerView.ViewHolder implements ExpandableViewHoldersUtil.Expandable {
        TextView tvTitle;
        ImageView arrowImage;
        LinearLayout lvArrorwBtn;
        LinearLayout lvLinearlayout;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            tvTitle = itemView.findViewById(R.id.item_user_concern_title);
            lvLinearlayout = itemView.findViewById(R.id.item_user_concern_link_layout);
            lvArrorwBtn = itemView.findViewById(R.id.item_user_concern_arrow);
            arrowImage = itemView.findViewById(R.id.item_user_concern_arrow_image);

            keepOne = ExpandableViewHoldersUtil.getInstance().getKeepOneHolder();

            lvLinearlayout.setVisibility(View.GONE);
            lvLinearlayout.setAlpha(0);
        }

        @Override
        public View getExpandView() {
            return lvLinearlayout;
        }

        @Override
        public void doCustomAnim(boolean isOpen) {
            if (isOpen) {
                ExpandableViewHoldersUtil.getInstance().rotateExpandIcon(arrowImage, 180, 0);
            } else {
                ExpandableViewHoldersUtil.getInstance().rotateExpandIcon(arrowImage, 0, 180);
            }
        }
    }

2-2.adapter

在 onBindViewHolder 的時候需要綁定對應的view,初始展開和收縮的狀態,自然點擊效果就是 keepOne.toggle(viewHolder);

@Override
        public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int position) {

            viewHolder.tvTitle.setText("中美經貿磋商 po=" + position);

            keepOne.bind(viewHolder, position);

            viewHolder.tvTitle.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    keepOne.toggle(viewHolder);
                }
            });

            viewHolder.lvArrorwBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    keepOne.toggle(viewHolder);
                }
            });
        }

2-3.ExpandableViewHoldersUtil

1.setNeedExplanedOnlyOne
//true 點擊第二個會收縮前一個
//false 不會
ExpandableViewHoldersUtil.getInstance().init().setNeedExplanedOnlyOne(false);

2.//清空記錄展開還是關閉的緩存數據,這個每次在下拉刷新的時候,是否清空根據需求自行處理
ExpandableViewHoldersUtil.getInstance().resetExpanedList();

3.結束:

參照的相關的代碼並根據自己在使用過程中遇到的問題,做了相關處理,原文鏈接已貼在開始。特意寫了一個簡單demo 上傳到git上,

還有問題請參考具體的實現demo :https://github.com/luhui2014/ExpandableViewHolder/tree/master

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章