美文网首页各种view优秀案例今日看点
简单实现StickHeader粘性列表特效

简单实现StickHeader粘性列表特效

作者: 皮球二二 | 来源:发表于2016-12-26 14:03 被阅读1883次

粘性列表特效,一般在类似通讯录这种强调分类的需求中广泛使用。它的好处在于可以直观的知道某一个ITEM属于哪种类型。在github上也有Star量很大的StickyListHeaders可以进行参考学习,但是我觉得,如果可以自己动手写,那么在特效的理解以及后期的维护上将会更加得心应手

本次代码已经上传到github上,欢迎star,follow

实现效果

实现思路

其实一开始还是比较困惑这个被顶上去的流程的,但是你听我分析之后,肯定就会豁然开朗

  1. 首先看看Item,不用说它肯定是由蓝色的粘性部分和真实数据两部分组成。


    Item布局效果
  2. 以RecyclerView为例,看看列表部分的布局。这里也有一个蓝色部分,这个是真正的索引视图,每一个索引的值由当前列表中每一个firstVisibleItem的值决定


    RecyclerView组合

    基于以上2点,你肯定可以实现一个普通的按类型分类的列表了,下面说的就是滑动部分了

  3. 如果要监听RecyclerView或者ListView的滑动,肯定要通过ScrollListener去完成。可以滚之后,下面就要先想办法去解决粘性部分的值如何去修改。这也很简单,只需要将firstVisibleItem所对应的Item的值直接赋值给粘性部分即可。这里直接通过getChildAt(0)即可获取完成
  4. 最后剩下的就是动画部分了。这里有个简单的逻辑判断:当粘性部分与某一个Item进行无缝相接之后,我们就得判断粘性部分与这个Item是不是属于同一个分类类型。如果是一个分类,则粘性部分保持当前位置;如果是一个新类别的Item,那么粘性部分就需要根据这个item的getTop值去修改自己Y轴方向的位置,直到与顶部交接而复原。RecylerView通过findChildViewUnder即可通过坐标点获取相应的视图,ListView则是通过pointToPosition去获取。这里需要判断的Y坐标点就是粘性部分高度值+1

动手写代码

  1. 布局
    先看看头部文件,这个没什么好说的,就是粘性部分
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"    
    android:id="@+id/header_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/holo_blue_bright"
    android:padding="10dp"
    android:textColor="@android:color/white"
    android:textSize="14sp" />

再看看Item的布局

<?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:layout_marginTop="1dp"
    android:orientation="vertical">
    <include layout="@layout/view_header" />
    <RelativeLayout
        android:id="@+id/rl_content_wrapper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp"
        android:padding="10dp">
        <TextView
            android:id="@+id/adapter_item_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:padding="5dp"
            android:textColor="@android:color/black"
            android:textSize="14sp"
            android:text="text1"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_below="@+id/adapter_item_tv"
            android:padding="5dp"
            android:textColor="@android:color/black"
            android:textSize="14sp"
            android:text="text2"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:textColor="@android:color/black"
            android:textSize="14sp"
            android:text="text3" />
    </RelativeLayout>
</LinearLayout>

看看RecyclerView的布局

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_sticky"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <include layout="@layout/view_header" />
</FrameLayout>
  1. 代码
    先看看Adapter部分。首先我们先要确定一下布局。position为0的布局肯定需要将粘性部分一直显示出来,其余条件下则不一样了,只有新分类的布局才会显示粘性部分,相同分类不需要显示。同时我们将状态保存以便获取
public static final int FIRST_STICKY_VIEW = 1;
public static final int HAS_STICKY_VIEW= 2;
public static final int NONE_STICKY_VIEW= 3;
@Override
public void onBindViewHolder(MainViewHolder holder, int position) {
    holder.header_view.setText(strings.get(position));
    holder.itemView.setContentDescription(strings.get(position));
    if (position==0) {
        holder.header_view.setVisibility(View.VISIBLE);
        holder.itemView.setTag(FIRST_STICKY_VIEW);
    }
    else {
        if (!strings.get(position).equals(strings.get(position-1))) {
            holder.header_view.setVisibility(View.VISIBLE);
            holder.itemView.setTag(HAS_STICKY_VIEW);
        }
        else {
            holder.header_view.setVisibility(View.GONE);
            holder.itemView.setTag(NONE_STICKY_VIEW);
        }
    }
}

随后就是关键的地方,随着RecyclerView滚动而改变粘性部分的文字,粘性部分动画范围为(0--粘性部分高度)。最后等滚动完成之后,就重新恢复到之前的位置。

rv_sticky.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        View stickyInfoView=recyclerView.getChildAt(0);
        if (stickyInfoView!=null && stickyInfoView.getContentDescription()!=null) {
            header_view.setText(String.valueOf(stickyInfoView.getContentDescription()));
        }
        View transInfoView=recyclerView.findChildViewUnder(header_view.getMeasuredWidth()/2, header_view.getMeasuredHeight()+1);
        if (transInfoView!=null && transInfoView.getTag()!=null) {
            int tag= (int) transInfoView.getTag();
            int deltaY=transInfoView.getTop()-header_view.getMeasuredHeight();
            if (tag==MainRVAdapter.HAS_STICKY_VIEW) {
                if (transInfoView.getTop()>0) {
                    header_view.setTranslationY(deltaY);
                }
                else {
                    header_view.setTranslationY(0);
                }
            }
            else {
                header_view.setTranslationY(0);
            }
        }
    }});

整套代码还是很简单的,最后附上ListView的处理方法

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    View stickyInfoView=view.getChildAt(0);
    if (stickyInfoView!=null && stickyInfoView.getContentDescription()!=null) {
        header_view.setText(String.valueOf(stickyInfoView.getContentDescription()));
    }
    int index_=view.pointToPosition(header_view.getMeasuredWidth()/2, header_view.getMeasuredHeight()+1);
    View transInfoView=view.getChildAt(index_-firstVisibleItem);
    if (transInfoView!=null && transInfoView.findViewById(R.id.*header_view*).getTag()!=null) {
        int tag= (int) transInfoView.findViewById(R.id.header_view).getTag();
        int deltaY=transInfoView.getTop()-header_view.getMeasuredHeight();
        if (tag==MainListAdapter.HAS_STICKY_VIEW) {
            if (transInfoView.getTop()>0) {
                header_view.setTranslationY(deltaY);
            }
            else {
                header_view.setTranslationY(0);
            }
        }
        else {
            header_view.setTranslationY(0);
        }
    }
}

相关文章

  • 简单实现StickHeader粘性列表特效

    粘性列表特效,一般在类似通讯录这种强调分类的需求中广泛使用。它的好处在于可以直观的知道某一个ITEM属于哪种类型。...

  • Android RecyclerView之粘性头部+点击事件

    实现上图列表的粘性头部功能一般通过在布局页面额外写粘性头部View,然后通过监听列表的滑动来控制显示隐藏粘性头部V...

  • 项目-namelist

    该姓名列表想要实现的功能是:列表展开时有个slideDown特效,且单击名字会淡出。在给列表添加特效时出现了一个小...

  • ExpandableRecyclerView

    使用RecyclerView 实现的折叠列表 效果图 粘性头部: 最后一个条目展开动画: GridLayoutMa...

  • 超简单实现iOS列表的索引功能

    超简单实现iOS列表的索引功能 超简单实现iOS列表的索引功能

  • 用RecyclerView的ItemDecoration实现粘性

    通过ItemDecoration来实现类似联系人列表的粘性头部效果,如下图: 定义实体类 Deconation的代...

  • Android 粘性标题简单实现

    先上图: 代码见git地址:https://github.com/xiaokele/StickyTitleDemo...

  • Android类似美妆相机高级美妆列表

    先上效果图 一、需求分析 实现类似美妆相机中高级美妆素材列表。 功能要求如下: 横向列表,可以左右滑动。提供粘性头...

  • 顶部粘性的列表

    先看效果 源码 为什么写这个控件? 之前有朋友问我,会不会手写这种效果,不是借助于第三方,卧槽,懵逼了。。。网上搜...

  • 水面效果(一)

    想要实现水面波纹Shader,这个特效很简单,最简单的一种方式是改变uv,让uv扭曲就可以实现

网友评论

  • Dora_Liang:请问你写的有上传到
    Github上吗?
    蜗牛改变自己大雄:可以细致讲解一下实现吗?看了几遍还是不太懂哎。:joy:
    Dora_Liang:@r17171709 谢谢
    皮球二二:在最最开头的地方
  • 4640f78109a4:int index_ = view.pointToPosition(header_view.getMeasuredWisth()/2,header_view.getMeasuredHeight()+1);这段代码什么意思啊?能解释一下吗?

本文标题:简单实现StickHeader粘性列表特效

本文链接:https://www.haomeiwen.com/subject/kolrvttx.html