天天看点

Android RecyclerView粘性头部的两种实现方式

先上图:

<方式一>添加外部Header实现方式

Android RecyclerView粘性头部的两种实现方式

<方式二>绘制ItemDecoration实现方式

Android RecyclerView粘性头部的两种实现方式

二者实现效果一样,下面分析下各自的实现逻辑。

添加外部Header实现方式

首先为每个RecyclerView的ItemView添加Header布局,这里简称为itemHeader。然后在RecyclerView的同级布局下添加Header布局,这里简称为mainHeader。列表数据展示时,只有位于同组的首个ItemView显示itemHeader,其余ItemView将itemHeader隐藏。当下一组的首个ItemView移动到位于mainHeader正下方时,计算该ItemView的位置来移动mainHeader;当该ItemView移动到顶部,即itemHeader位于mainHeader的位置时,mainHeader恢复原来位置。

Activity xml代码:

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

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        toolbar:popupTheme="@style/ToolbarPopupTheme">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="StickyHeader"
            android:textColor="@color/colorWhite"
            android:textSize="18sp"/>
    </android.support.v7.widget.Toolbar>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/stickyheader_recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <!-- Header布局 -->
        <LinearLayout
            android:id="@+id/sticky_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/color_material_light"
            android:orientation="vertical">

            <TextView
                android:id="@+id/header_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="10dp"
                android:paddingLeft="15dp"
                android:paddingTop="10dp"
                android:textColor="@color/colorTextContent"
                android:textSize="14sp"
                tools:text="2017年09月27日"/>
        </LinearLayout>
    </RelativeLayout>
</LinearLayout>
           

ItemView xml代码:

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <!-- Header布局 -->
        <LinearLayout
            android:id="@+id/sticky_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/color_material_light"
            android:orientation="vertical">

            <TextView
                android:id="@+id/header_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="10dp"
                android:paddingLeft="15dp"
                android:paddingTop="10dp"
                android:textColor="@color/colorTextContent"
                android:textSize="14sp"
                tools:text="2017年09月27日"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingLeft="15dp">

            <TextView
                android:id="@+id/sticky_time_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@color/colorTextContent"
                android:textSize="13sp"
                android:textStyle="bold"
                tools:text="18:00"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/sticky_content_textview"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="15dp"
                    android:layout_marginLeft="15dp"
                    android:layout_marginRight="15dp"
                    android:layout_marginTop="15dp"
                    android:textColor="@color/colorTextContent"
                    android:textSize="16sp"
                    tools:text="这是内容"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="0.5dp"
                    android:layout_marginLeft="15dp"
                    android:background="#E9E9E9"/>
            </LinearLayout>
        </LinearLayout>

    </LinearLayout>
</LinearLayout>
           

适配器Adapter代码:

...
    public static final int FIRST_STICKY_VIEW = ;
    public static final int HAS_STICKY_VIEW = ;
    public static final int NONE_STICKY_VIEW = ;
    ...
    @Override
    public void onBindViewHolder(ViewHolder holder, int position)
    {
        StickyHeaderActivity.Model model = dataList.get(position);
        holder.headerTv.setText(model.getHeader());
        holder.timeTv.setText(model.getTime());
        holder.contentTv.setText(model.getContent());
        holder.itemView.setContentDescription(model.getHeader());
        if (position == )
        {
            holder.headerLayout.setVisibility(View.VISIBLE);
            holder.itemView.setTag(FIRST_STICKY_VIEW);
        } else
        {
            if (model.getHeader().equals(dataList.get(position - ).getHeader())) //当前Item头部与上一个Item头部相同,则隐藏头部
            {
                holder.headerLayout.setVisibility(View.GONE);
                holder.itemView.setTag(NONE_STICKY_VIEW);
            } else
            {
                holder.headerLayout.setVisibility(View.VISIBLE);
                holder.itemView.setTag(HAS_STICKY_VIEW);
            }
        }
    }
...
           

RecyclerView滚动监听类代码:

/**
     * 监听RecyclerView滚动,实现粘性头部
     */
    private class RvScrollListener extends RecyclerView.OnScrollListener
    {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            super.onScrolled(recyclerView, dx, dy);

            View stickyInfoView = recyclerView.getChildAt();//获取头部View
            if (stickyInfoView != null && stickyInfoView.getContentDescription() != null)
            {
                headerView.setVisibility(View.VISIBLE);
                headerViewText.setText(String.valueOf(stickyInfoView.getContentDescription()));
            }
            View transInfoView = recyclerView.findChildViewUnder(headerView.getMeasuredWidth() / 
                    , headerView.getMeasuredHeight() + );//位于headerView下方的itemView(该坐标是否在itemView内)
            if (transInfoView != null && transInfoView.getTag() != null)
            {
                int tag = (int) transInfoView.getTag();
                int deltaY = transInfoView.getTop() - headerView.getMeasuredHeight();
                if (tag == StickyHeaderAdapter.HAS_STICKY_VIEW)//当Item包含粘性头部一类时
                {
                    if (transInfoView.getTop() > )//当Item还未移动出顶部时
                    {
                        headerView.setTranslationY(deltaY);
                    } else//当Item移出顶部,粘性头部复原
                    {
                        headerView.setTranslationY();
                    }
                } else//当Item不包含粘性头部时
                {
                    headerView.setTranslationY();
                }
            }
        }
    }
           

最后给RecyclerView添加滚动监听:

mRecyclerView.addOnScrollListener(new RvScrollListener());
           

绘制ItemDecoration实现方式

请参考 RecyclerView探索之通过ItemDecoration实现StickyHeader效果

源码地址:github

继续阅读