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

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/android_freshman/article/details/94354088
RecyclerView item 可展開動畫效果的實現
前文提要:
1.相關說明:
1-1.佈局文件:
1-2.動畫工具類說明(代碼我基本上都添加了註釋):
1-3.問題:
2.如何使用:
2-1.viewHoler 須要實現 ExpandableViewHoldersUtil.Expandable 接口
2-2.adapter
2-3.ExpandableViewHoldersUtil
3.結束:
前文提要:
Android list 列表裏面空間的顯示和 隱藏,基本都是用的View.VISIBLE 和 View.GONE 實現的,展現的效果有點突兀,看了ios 同事作的相同的效果,他們的很順暢,因此決定作一個相同的效果.android

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

1.相關說明:
參考資料:Android—RecyclerView之動畫(工具類)實現可展開列表git

1-1.佈局文件:
將須要展開收縮的那部分佈局的透明度在xml文件裏默認設置爲0,在代碼中設置同樣github


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

        //測量擴展動畫的起始高度和結束高度
        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;動畫


    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
還有一段比較有意思的地方是 處理了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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
                    }
                }
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
開放出來一個回調接口處理:holder.doCustomAnim(true);,根據須要自行增長相關的動畫;

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


這裏用String 記錄是爲了處理刪除的時候數據越界的問題:

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

源碼中有這麼一段,直接刪除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);
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
                }
            });
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 ———————————————— 版權聲明:本文爲CSDN博主「android_小路」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/android_freshman/article/details/94354088

相關文章
相關標籤/搜索