介绍
在项目中有时会需要recyclerview滑动式时某个view滑出后会固定在头部显示,比较常用的比如手机联系人界面、地区选择界面等。 StickHeaderRecyclerView就是实现这个功能的。效果图:

这样的控件网上一抓一大把了,本控件的优点就是使用简单- lib简单 - 使用的语法也简单(之前下了2个类似开源项目,都是上万行代码。读起来麻烦、改起来麻烦就自己写了这个控件)

使用
只需要让你的adapter实现StickHeaderDecoration.StickHeaderInterface接口,方法boolean isStick(int position)中返回的值就标识当前位置的view是否需要固定。
同时需要让Adapter中的item不复用(如果怕影响性能也可以单独让需要固定的view不复用) 在adapter构造方法中setHasStableIds(true); 同时复写adapter的public long getItemId(int position) {return position;}
上代码

public class NormalAdapter  extends RecyclerView.Adapter<NormalAdapter.InnerHolder> implements StickHeaderDecoration.StickHeaderInterface{

    NormalAdapter(Activity activity, List<String> dates){
this.activity = activity;
this.dates = dates;
} @Override
public boolean isStick(int position) {
return position % == ;
} Activity activity;
private List<String> dates; @Override
public InnerHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflate = LayoutInflater.from(activity).inflate(R.layout.item, parent,
false);
return new InnerHolder(inflate);
} @Override
public void onBindViewHolder(InnerHolder holder, int position) {
if(isStick(position)){
holder.itemView.setBackgroundResource(R.color.colorAccent);
holder.tvText.setText(position / +"");
}else{
holder.itemView.setBackgroundResource(R.color.white);
holder.tvText.setText(dates.get(position));
}
} @Override
public int getItemCount() {
return dates.size();
} class InnerHolder extends RecyclerView.ViewHolder{
TextView tvText;
public InnerHolder(View itemView) {
super(itemView);
tvText = (TextView) itemView.findViewById(R.id.tvText);
}
}
}

activity代码

public class MainActivity extends Activity {

    private RecyclerView recycle;
private List<String> dates = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recycle = (RecyclerView) findViewById(R.id.recycle); for(int i=;i<;i++){
dates.add("date : "+i);
} recycle.setLayoutManager(new LinearLayoutManager(this));
recycle.setAdapter(new NormalAdapter(this, dates));
recycle.addItemDecoration(new StickHeaderDecoration(recycle));
} }

完成了

原理

先上核心类代码

public class StickHeaderDecoration extends RecyclerView.ItemDecoration {

    public interface StickHeaderInterface {
/**
* is this item need stick
* @param position now item position in the recyclerView
* @return true : need stick else not
*/
boolean isStick(int position);
} private RecyclerView recyclerView;
private RecyclerView.LayoutManager manager;
private RecyclerView.Adapter adapter;
private StickHeaderInterface stickHeaderInterface; /**
* 进行一些容错检查
*/
public StickHeaderDecoration(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
this.manager = recyclerView.getLayoutManager();
this.adapter = recyclerView.getAdapter();
if (adapter == null) {
throw new RuntimeException("please set Decoration after set adapter");
} if (adapter instanceof StickHeaderInterface) {
stickHeaderInterface = (StickHeaderInterface) adapter;
return;
}
throw new RuntimeException("please make your adapter implements StickHeaderInterface");
} /**
* 绘制头部的stick view
*/
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
View childAt = parent.getChildAt();
if (childAt == null)
return;
RecyclerView.ViewHolder childViewHolder = parent.getChildViewHolder(childAt);
int position = childViewHolder.getPosition();
for (int i = position; i >= ; i--) {
if (stickHeaderInterface.isStick(i)) {
int top = ;
if (position + < adapter.getItemCount()) {
if (stickHeaderInterface.isStick(position + )) {
View childNext = parent.getChildAt();
top = manager.getDecoratedTop(childNext) < ? : manager
.getDecoratedTop(childNext);
}
}
RecyclerView.ViewHolder inflate = recyclerView.getAdapter().createViewHolder(parent,
recyclerView.getAdapter().getItemViewType(i));
recyclerView.getAdapter().bindViewHolder(inflate, i);
int measureHeight = getMeasureHeight(inflate.itemView);
c.save();
if (top < inflate.itemView.getMeasuredHeight() && top > ) {
c.translate(, top - measureHeight);
}
inflate.itemView.draw(c);
c.restore();
return;
}
}
} /**
* 测量控件的高度
*
* @param header
*/
private int getMeasureHeight(View header) {
int widthSpec = View.MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), View
.MeasureSpec.EXACTLY);
int heightSpec = View.MeasureSpec.makeMeasureSpec(, View.MeasureSpec.UNSPECIFIED);
header.measure(widthSpec, heightSpec);
header.layout(, , header.getMeasuredWidth(), header.getMeasuredHeight());
return header.getMeasuredHeight();
}
}

首先ItemDecoration是一个接口,通过RecyclerView的 recycle.addItemDecoration方法设置进去
其中只有6个方法其中3个过时了。我们这儿只需要对onDrawOver进行操作。
onDrawOver是当前RecyclerView绘制完毕后调用,可以其中进行绘制。我们的头部固定其实就是在这个方法中绘制进去的。

算法
1.这儿我们需要判断当前显示item的前面是否有需要固定的item(这儿取名为beforitem)如果有则绘制在顶部
2.我们还需要当第二个固定的item把前面的item慢慢顶上去的效果,这儿通过判断当前显示的第一个item的下一个item是否需要固定,如果需要则通过manager.getDecoratedTop(childNext)获取这个item距离顶部的距离然后通过计算把beforitem先上移动一定的距离。
基本原理就这样,相信代码更加有说服力,github 地址
https://github.com/LiuLinXin/StickHeaderRecyclerView-philer

待优化
头部view现在是通过ondraw绘制进去的,不能相应点击事件等。暂时没相处好的解决办法,希望有想法的朋友提示下。

最新文章

  1. 有了大量微信用户,就不需要App了吗?
  2. 【经验之谈】Git使用之Windows环境下配置
  3. 读 [The Root of Lisp]
  4. Asp.net MVC+Bootstrap3的悬浮式登录框效果
  5. SGU326 Perspective(指派问题)
  6. 用C#调用蓝牙编程
  7. MySQL【第二篇】基本命令
  8. (转)select 1 from ... sql语句中的1代表什么意思? .
  9. Java NIO框架Netty课程(一) – Hello Netty
  10. grunt--自动化任务快速上手
  11. 深入理解Java内存模型 - volatile
  12. asp.net core mvc剖析:动作执行
  13. CVE-2017-8635复现
  14. Azure Powershell获取指定订阅下的虚拟机信息(ASM)
  15. BZOJ1095(动态点分治+堆)
  16. 【Unity编辑器】UnityEditor多重弹出窗体与编辑器窗口层级管理
  17. WMS专业术语&amp;系统功能操作培训
  18. python模块分析之hashlib加密(二)
  19. GCT之数学公式(微积分)
  20. 5 -- Hibernate的基本用法 --1 2 基本映射方式

热门文章

  1. $(xx).load()同步
  2. 012 Android Palette颜色选择器的使用
  3. php:// 访问各个输入/输出流
  4. 【floyd】【bitset】洛谷 P1841 [JSOI2007]重要的城市 题解
  5. Tensorlflow-神经网络解决非线性回归问题
  6. bzoj1041 圆上的整点 数学
  7. HDU1501 简单DP
  8. setlocal enabledelayedexpansion 解释
  9. [转]Oracle ROWNUM用法和分页查询总结
  10. 关于GBK、GB2312、UTF8之间的区别