1. 程式人生 > >吸頂效果的另一種實現

吸頂效果的另一種實現

 

前面介紹過一篇文章,是使用ItemDecoration來實現吸頂效果,使用起來很解耦,簡單,方便,但是優缺點是拓展性比較差,今天就通過另一種方式來實現吸頂效果,並且吸頂欄可以高度制定佈局和互動,步入正題,下面來實現它,先看看效果圖:

一、實現原理

頭部的內容位於Recycleview上面,監聽Recycleview的滑動,當Recycleview的分組item頂部接觸到頭部的內容佈局底部時候,浮層隨處Recycleview的上滑而上推,當分組完全到達頂部時候,浮層復位,這樣吸頂效果就產生了,依據這個思路來實現吸頂效果。

二、佈局檔案實現

1、抽取公共的吸頂佈局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/sticky_text"
            android:layout_width="match_parent"
            android:layout_height="@dimen/head_top"
            android:background="@color/bg_header"
            android:gravity="center_vertical"
            android:paddingLeft="@dimen/sticky_text_margin"/>

    </LinearLayout>

2、MainActivity

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="cn.com.stickyrecycleview.MainActivity">

        <!--內容欄-->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycleview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <!--吸頂欄-->
        <include layout="@layout/sticky_layout"/>

    </FrameLayout>

三、Adapter實現

1、佈局檔案

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="vertical">

        <!--分組欄-->
        <include layout="@layout/sticky_layout"/>

        <!--內容欄-->
        <TextView
            android:id="@+id/tv_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="@string/app_name"/>
    </LinearLayout>

佈局檔案裡面包含兩個View,一個是分組的View,一個是內容item的View,需要注意的是分組的View一定要和吸頂的View一致,這裡為了方便期間,都設定為一個TextView。當需要展示分組的是讓分組的View為VISIBLE,否則GONE掉。

 2、onBindViewHolder

    //無吸頂
    public static final int NONE_STICKY_VIEW = 0;
    //有吸頂
    public static final int HAS_STICKY_VIEW = 1;
    //第一個
    public static final int FIRST_STICKY_VIEW = 2;

onBindViewHolder是核心處理方法,如果是第0個,肯定是吸頂,設定tag:FIRST_STICKY_VIEW。如果是其他的,當前的分組依據和上一個一致,則認為是同一組,不展示分組item,設定tag:NONE_STICKY_VIEW,否則是不同組,展示分組item,設定tag:HAS_STICKY_VIEW。最後通過ContentDescription方法來記錄並獲取要吸頂展示的資訊。詳細如下備註:

     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
         DataBean dataBean = mDataList.get(position);
         if (position == 0) {
             //第0個展示吸頂欄,設定tag:FIRST_STICKY_VIEW
             holder.stickyText.setVisibility(View.VISIBLE);
             holder.stickyText.setText(dataBean.data);
             holder.itemView.setTag(FIRST_STICKY_VIEW);
         } else {
             if (!TextUtils.equals(dataBean.data, mDataList.get(position - 1).data)) {
                 //和上一個分組的依據不同,展示吸頂欄,設定tag:HAS_STICKY_VIEW
                 holder.stickyText.setVisibility(View.VISIBLE);
                 holder.stickyText.setText(dataBean.data);
                 holder.itemView.setTag(HAS_STICKY_VIEW);
             } else {
                 //其他的,隱藏分組欄
                 holder.stickyText.setVisibility(View.GONE);
                 holder.itemView.setTag(NONE_STICKY_VIEW);
             }
         }
         holder.textView.setText(dataBean.des);
         // ContentDescription 用來記錄並獲取要吸頂展示的資訊
         holder.itemView.setContentDescription(dataBean.data);
     }

四、滑動監聽

最後需要通過addOnScrollListener方法監聽Recycleview的滑動,來控制吸頂變化,原理看備註:

    recycleview.addOnScrollListener(new RecyclerView.OnScrollListener() {

                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    //找到RecyclerView的item中,和RecyclerView的getTop 向下相距5個畫素的那個item,
                    //該方法是根據座標獲取item的View,座標點是以RecyclerView控制元件作為座標軸,並不是以螢幕左上角作為座標原點。
                    View stickyInfoView = recyclerView.findChildViewUnder(stickyText.getMeasuredWidth() / 2, 5);
                    if (stickyInfoView != null && stickyInfoView.getContentDescription() != null) {
                        //設定吸頂欄內容
                        stickyText.setText(String.valueOf(stickyInfoView.getContentDescription()));
                    }
                    //找到固定在頂部的View的下面一個item的View
                    View transInfoView = recyclerView.findChildViewUnder(stickyText.getMeasuredWidth() / 2, stickyText.getMeasuredHeight() + 1);
                    if (transInfoView != null && transInfoView.getTag() != null) {
                        //獲取該View的tag
                        int transViewStatus = (int) transInfoView.getTag();
                        int dealtY = transInfoView.getTop() - stickyText.getMeasuredHeight();
                        if (transViewStatus == MyAdapter.HAS_STICKY_VIEW) {
                            if (transInfoView.getTop() > 0) {
                                //最上面的itemView沒滑出螢幕,給頂部的View設定,
                                //注意setTranslationY移動的ViewGroup,而scrollTo()/scrollBy()移動的是View的內容,如文字、圖片等
                                stickyText.setTranslationY(dealtY);
                            } else {
                                //最上面的itemView滑出螢幕,頂部的復位
                                stickyText.setTranslationY(0);
                            }
                        } else if (transViewStatus == MyAdapter.NONE_STICKY_VIEW) {
                            //如果是沒有分組,即無吸頂的item,則設定頂部的View移動為0,保持原位置
                            stickyText.setTranslationY(0);
                        }
                    }
                }
            });

原始碼地址:https://github.com/yoonerloop/StickyViewLayout