zoukankan      html  css  js  c++  java
  • Android StickHeaderRecyclerView

    介绍
    在项目中有时会需要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 % 6 == 0;
        }
    
        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 / 6 +"");
            }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=0;i<66;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(0);
            if (childAt == null)
                return;
            RecyclerView.ViewHolder childViewHolder = parent.getChildViewHolder(childAt);
            int position = childViewHolder.getPosition();
            for (int i = position; i >= 0; i--) {
                if (stickHeaderInterface.isStick(i)) {
                    int top = 0;
                    if (position + 1 < adapter.getItemCount()) {
                        if (stickHeaderInterface.isStick(position + 1)) {
                            View childNext = parent.getChildAt(1);
                            top = manager.getDecoratedTop(childNext) < 0 ? 0 : 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 > 0) {
                        c.translate(0, 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(0, View.MeasureSpec.UNSPECIFIED);
            header.measure(widthSpec, heightSpec);
            header.layout(0, 0, 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绘制进去的,不能相应点击事件等。暂时没相处好的解决办法,希望有想法的朋友提示下。

  • 相关阅读:
    Qt 字符串QString arg()用法总结
    Qt子窗口QMidSubwindow全屏出现的问题总结
    QString介绍
    Qt控制台输出QString
    汽车辐射监测系统-Qt开发[转]发
    Eclipse Qt开发环境的建立
    串口调节工具
    QT 多线程程序设计 -互斥
    ArcGIS Spatial Query
    IQueryFielter接口
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/9804079.html
Copyright © 2011-2022 走看看