RecyclerView实现View吸附顶部效果

本文实现的效果
在这里插入图片描述
实现步骤:
1、布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ContentFragment"
    android:background="#fff">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/detail_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</FrameLayout>

2、列表实现的代码


public class ContentFragment extends Fragment {

    private RecyclerView detail_recycler;

    public ContentFragment() {

    }


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_content,container,false);
        initView(view);
        return view;

    }

    private void initView(View view) {
        getData();
        detail_recycler = view.findViewById(R.id.detail_recycler);
        GridLayoutManager gridLayoutManager=new GridLayoutManager(getContext(),3);
        gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        detail_recycler.setLayoutManager(gridLayoutManager);
        detail_recycler.addItemDecoration(new MyItem2());
        detail_recycler.addItemDecoration(new ItemHeaderDecoration(getActivity(),list));
        detail_recycler.setAdapter(new MyAdapter());

        gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int i) {
                return list.get(i).isTitle() ? 3:1;
            }
        });
    }


    List<DetailBean> list = new ArrayList<>();
    private void getData(){
        for (int i=0;i<10;i++){
            list.add(new DetailBean(true,"标题"+i,"tag"+i));
            for(int j = 0 ; j < 20 ; j++){
                list.add(new DetailBean(false,"内容"+j,"tag"+i));
            }
        }
        Log.v("list=======","list="+new Gson().toJson(list));
    }


    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
        private static final int TITLE  = 1;
        private static final int NORMAL  = 2;

        @NonNull
        @Override
        public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
            return i==TITLE ? new MyAdapter.MyViewHolder(LayoutInflater.from(getActivity()).inflate(R.layout.detail_title, viewGroup, false))
                    :new MyAdapter.MyViewHolder(LayoutInflater.from(getActivity()).inflate(R.layout.item_layout, viewGroup, false));
        }

        @Override
        public void onBindViewHolder(@NonNull MyAdapter.MyViewHolder holder, int i) {
            holder.title_textview.setText(list.get(i).getName());
        }

        @Override
        public int getItemCount() {
            return list.size();
        }

        @Override
        public int getItemViewType(int position) {
            return list.get(position).isTitle() ? TITLE:NORMAL;
        }

        public class MyViewHolder extends RecyclerView.ViewHolder {
            public TextView title_textview;
            public MyViewHolder(@NonNull View itemView) {
                super(itemView);
                title_textview = itemView.findViewById(R.id.title_textview);
            }
        }
    }
}

3、最重要的一步重写 RecyclerView.ItemDecoration 中的 onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)方法



import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;

/**
 * Created by User on 2017/7/24.
 */

public class ItemHeaderDecoration extends RecyclerView.ItemDecoration {


    private Context mContext;
    private List<DetailBean> mList;
    public static String currentTag = "0";
    private LayoutInflater mLayoutInflater;
    private int mTitleHight = 50;


    public ItemHeaderDecoration(Context context , List<DetailBean> dataList) {
        super();
        this.mContext = context;
        this.mList = dataList;
        mTitleHight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50,context.getResources().getDisplayMetrics());
        // mTitleTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics());
        this.mLayoutInflater = LayoutInflater.from(mContext);
    }



    public static void setCurrentTag(String tag){
        ItemHeaderDecoration.currentTag = tag;
    }



    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
    }



    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
    }



    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        //获取到视图中第一个可见的item的position
        GridLayoutManager manager = (GridLayoutManager) parent.getLayoutManager();
        int position = manager.findFirstVisibleItemPosition();
        String tag = mList.get(position).getTag();
        View child = parent.findViewHolderForLayoutPosition(position).itemView;
        int nextPosition = position+2;
        boolean flag = false ;
        if(nextPosition < mList.size()){
            String suspensionTag = mList.get(nextPosition).getTag();
            Log.v("====child=======","=="+child.getTop());
            if(!TextUtils.isEmpty(tag) && !tag.equals(suspensionTag)){
                if(child.getHeight()+child.getTop() < mTitleHight){
                    c.save();
                    flag = true ;
                    c.translate(0,child.getHeight()+child.getTop()-mTitleHight);
                }
            }
        }


        View topTitleView = mLayoutInflater.inflate(R.layout.detail_title,parent,false);
        TextView textView =  topTitleView.findViewById(R.id.title_textview);
        textView.setText("分类"+tag);
        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) topTitleView.getLayoutParams();
        if (lp != null) {
            //依次调用 measure,layout,draw方法,将复杂头部显示在屏幕上。
            topTitleView.measure(mWidth(lp,parent), mHeight(lp,parent));
            //topTitleView在屏幕中显示的位置
            topTitleViewLayout(parent,topTitleView);
            //Canvas默认在视图顶部,无需平移,直接绘制
            topTitleView.draw(c);
            if (flag){
                c.restore();//还原画布
            }
        }
    }


    /**
     * 显示的位置
     *
     * @param parent
     * @param topTitleView
     */
    private void topTitleViewLayout(RecyclerView parent, View topTitleView) {
        int l = parent.getPaddingLeft();//如果父布局有内句左边
        int t = parent.getTop();//当前距离父布局顶部的距离
        int r = parent.getPaddingLeft() + topTitleView.getMeasuredWidth();;
        int b = t +topTitleView.getMeasuredHeight();
        topTitleView.layout(l,t,r,b);
    }




    /**
     * 高度
     *
     * @param lp
     * @param parent
     * @return
     */
    private int mHeight(RecyclerView.LayoutParams lp , RecyclerView parent) {
        int h;
        //高度同理
        if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
            h = View.MeasureSpec.makeMeasureSpec(
                    parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(), View.MeasureSpec.EXACTLY);
        } else if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            h = View.MeasureSpec.makeMeasureSpec(
                    parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(), View.MeasureSpec.AT_MOST);
        } else {
            h = View.MeasureSpec.makeMeasureSpec(mTitleHight, View.MeasureSpec.EXACTLY);
        }
        return h;
    }



    /**
     * 宽
     *
     * @param lp
     * @param parent
     * @return
     */
    private int mWidth(RecyclerView.LayoutParams lp, RecyclerView parent) {
        int w;
        if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
            //如果是MATCH_PARENT,则用父控件能分配的最大宽度和EXACTLY构建MeasureSpec。
            w = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.EXACTLY);
        } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            //如果是WRAP_CONTENT,则用父控件能分配的最大宽度和AT_MOST构建MeasureSpec。
            w = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST);
        } else {
            //否则则是具体的宽度数值,则用这个宽度和EXACTLY构建MeasureSpec。
            w = View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY);
        }
        return w;
    }


    /**
     * @param val
     * @return
     */
    public int dpToPx(int val){
        float density = mContext.getResources().getDisplayMetrics().density;
        float px =  val * density + 0.5f;
        return (int) px;
    }


}

两个吸附的view交替过程的实现

       if(nextPosition < mList.size()){
            String suspensionTag = mList.get(nextPosition).getTag();
            Log.v("====child=======","=="+child.getTop());
            if(!TextUtils.isEmpty(tag) && !tag.equals(suspensionTag)){
                if(child.getHeight()+child.getTop() < mTitleHight){
                    c.save();
                    flag = true ;
                    c.translate(0,child.getHeight()+child.getTop()-mTitleHight);
                }
            }
        }

真正实现吸附部分

if (lp != null) {
            //依次调用 measure,layout,draw方法,将复杂头部显示在屏幕上。
            topTitleView.measure(mWidth(lp,parent), mHeight(lp,parent));
            //topTitleView在屏幕中显示的位置
            topTitleViewLayout(parent,topTitleView);
            //Canvas默认在视图顶部,无需平移,直接绘制
            topTitleView.draw(c);
            if (flag){
                c.restore();//还原画布
            }
        }

以上的代码就可以实现吸附效果了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
RecyclerView 点击折叠效果实现可以通过自定义 RecyclerView.Adapter 实现。 1. 在 RecyclerView.Adapter 中定义一个 boolean 类型的变量用于记录当前 item 的展开状态。 2. 在 onBindViewHolder 方法中判断当前 item 是否展开,根据展开状态设置 item 的高度。如果当前 item 是展开状态,则高度为全部内容的高度;否则,高度为折叠后的高度。 3. 在 item 的点击事件中,改变展开状态并调用 notifyDataSetChanged() 方法刷新数据。 以下是示例代码: ```java public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { private boolean isExpanded = false; // 记录当前 item 是否展开 @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // 创建 ViewHolder View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { // 绑定数据 if (isExpanded) { // 如果当前 item 是展开状态,则设置 item 的高度为全部内容的高度 holder.itemView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; } else { // 否则,高度为折叠后的高度 holder.itemView.getLayoutParams().height = 200; } } @Override public int getItemCount() { // 返回数据集大小 return 10; } class MyViewHolder extends RecyclerView.ViewHolder { public MyViewHolder(View itemView) { super(itemView); // 设置 item 的点击事件 itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 改变展开状态 isExpanded = !isExpanded; // 刷新数据 notifyDataSetChanged(); } }); } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

将哥哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值