RecyclerView使用

RecyclerView使用

1. 基础使用

RecyclerView需要自己继承RecyclerView.Adapter以及RecyclerView.ViewHolder。

  1. 实现ViewHolder,作用就是初始Item中要用的子控件,其作为Adapter的内部类即可。

        static class MyViewHolder extends RecyclerView.ViewHolder {
            TextView titleTv;
            TextView contentTv;
            TextView desTv;
            public MyViewHolder(View itemView) {
                super(itemView);
                titleTv=itemView.findViewById(R.id.tv_title);
                contentTv=itemView.findViewById(R.id.tv_content);
                desTv=itemView.findViewById(R.id.tv_des);
            }
        }
    复制代码
  2. 实现Adapter,这里有三个重要的方法,onCreateViewHolder创建ViewHolder实例,onBindViewHolder将数据绑定到ViewHolder实例中,getItemCount获取列表的数量。

    public class VerticalRecyclerViewAdapter extends RecyclerView.Adapter<VerticalRecyclerViewAdapter.MyViewHolder> {
        private ArrayList<Book> data;
        private Context mContext;
    
        public void setData(ArrayList<Book> data) {
            this.data = data;
            notifyDataSetChanged();
        }
    
        public VerticalRecyclerViewAdapter(Context context) {
            mContext = context;
        }
    
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view= LayoutInflater.from(mContext).inflate(R.layout.view_item1,parent,false);
            return new MyViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
            holder.title.setText(data.get(position).getName());
        }
    
        @Override
        public int getItemCount() {
            return data == null ? 0 : data.size();
        }
    
        static class MyViewHolder extends RecyclerView.ViewHolder {
            public TextView title;
            public TextView content;
            public TextView des;
    
            public MyViewHolder(View itemView) {
                super(itemView);
                title = itemView.findViewById(R.id.tv_title);
                content = itemView.findViewById(R.id.tv_content);
                des = itemView.findViewById(R.id.tv_des);
            }
        }
    }
    复制代码
  3. 在Activity或者Fragment中配置控件,可以设置item横向布局、竖向布局、网格布局

         // 配置布局方式
         LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
         linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
         mRecyclerView.setLayoutManager(linearLayoutManager);
         // 初始化adapter
         mAdapter = new VerticalRecyclerViewAdapter(this);
         mAdapter.setData(list);
         // 为RecyclerView设置Adapter
         mRecyclerView.setAdapter(mAdapter);
    
         // 设置网格布局
         GridLayoutManager gridLayoutManager=new GridLayoutManager(this,3);
         // 重新设置列数
         gridLayoutManager.setSpanCount(2);
    复制代码

2. 进阶使用

2.1 点击事件

要监听RecyclerView中Item的点击事件一般有两种实现方式,第一种是在Adapter中进行点击事件的处理,第二种是在外部实现点击事件处理。

    // 定义接口
    public interface OnItemClickListener {
        void onItemClick(View view, Book book);
    }

    // 监听器的注入,可使用构造时注入,也可以使用setter注入。
    private Context mContext;
    private OnItemClickListener mOnItemClickListener;
    public ClickRecyclerViewAdapter(Context context, OnItemClickListener onItemClickListener) {
        mContext = context;
        mOnItemClickListener = onItemClickListener;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        mOnItemClickListener = onItemClickListener;
    }
    // 数据绑定及具体控件的事件监听
    public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
        holder.titleBtn.setText("按钮:" + (position + 1));
        holder.titleBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, "在adapter中实现点击处理逻辑", Toast.LENGTH_SHORT).show();
            }
        });

        holder.desTv.setText(data.get(position).toString());
        holder.desTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mOnItemClickListener.onItemClick(v, data.get(position));
            }
        });
    }

    // 在初始化Adapter时可以注入事件处理逻辑
    ClickRecyclerViewAdapter adapter=new ClickRecyclerViewAdapter(this, new ClickRecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, Book book) {
                Snackbar.make(view,"外部注入的点击逻辑:"+view.getId(),Snackbar.LENGTH_SHORT).setAction("Action",null).show();
            }
    });

复制代码

2.2 分组

分组的实现有多种方式,最简单的是header和body写在一个布局中,绑定数据时进行比较,若和上一个位置的数据是同一组的,即隐藏本Item的header。 注意:这要求传入的数据就是分组好的。主要实现如下: Item布局如下

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".group.Item5Activity">

    <TextView
        android:id="@+id/tv_group_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:background="@color/blueviolet"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_group_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:background="@color/beige"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_group_title" />

</android.support.constraint.ConstraintLayout>

复制代码

按照数据分组的逻辑如下:

    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.titleTv.setText(data.get(position).getName());
        holder.contentTv.setText(data.get(position).getContent());
        // title 就是header
        if (position == 0) {
            holder.titleTv.setVisibility(View.VISIBLE);
        } else {
            if (data.get(position).getName().equals(data.get(position - 1).getName())) {
                holder.titleTv.setVisibility(View.GONE);
            } else {
                holder.titleTv.setVisibility(View.VISIBLE);
            }
        }
    }
复制代码

2.3 悬浮吸顶

悬浮吸顶的实现同样需要用到分组,具体思路是在RecyclerView最上方固定一个header布局,当列表中的header滚动到此处时,将固定的header向上滑动。当header滑出之后更换为下一个header

  1. RecyclerView布局,将RecyclerView和header放在一起。
<android.support.constraint.ConstraintLayout 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=".sticky.Item7Activity">

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

    <include layout="@layout/view_item7_sticky_header_item" />

</android.support.constraint.ConstraintLayout>
复制代码
  1. Item布局文件,和分组的实现一样。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <include layout="@layout/view_item7_sticky_header_item" />

    <TextView
        android:id="@+id/tv_sticky_body"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
复制代码
  1. 分组显示并为Item设置标记位
    public static final int FIRST_STICKY_TAG = 1;// 是否是第一个元素
    public static final int HAS_STICKY_TAG = 2;// 该Item是带有Header
    public static final int NONE_STICKY_TAG = 3;// 该Item没有Header

    /**
     * 分组显示的核心思路就是,header和body 分开显示,header的显示取决于当前item的头部是否和上一个item的头部一致。
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Book book = mBookList.get(position);
        holder.bodyTv.setText(book.getContent());

        if (position == 0) {
            // 首位元素的显示逻辑
            holder.headerTv.setText(book.getName());
            holder.headerTv.setVisibility(View.VISIBLE);
            holder.itemView.setTag(FIRST_STICKY_TAG);
        } else {
            if (TextUtils.equals(book.getName(), mBookList.get(position - 1).getName())) {
                // 如果相等,说明Name有一致的情况,隐藏头部标题,而这个itemView标记为没有粘结头部
                holder.headerTv.setVisibility(View.GONE);
                holder.itemView.setTag(NONE_STICKY_TAG);
            } else {
                // 如果不一致,说明需要一个新的头部
                holder.headerTv.setText(book.getName());
                holder.headerTv.setVisibility(View.VISIBLE);
                holder.itemView.setTag(HAS_STICKY_TAG);
            }
        }
        // 为itemView添加描述内容,用于为列表顶部展示的那个header赋值
        holder.itemView.setContentDescription(book.getName());
    }
复制代码
  1. 在Activity中滑动逻辑
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
           @Override
           public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
               super.onScrolled(recyclerView, dx, dy);
               // 按像素位置取到列表中 最上面的那个itemView
               View firstView = recyclerView.findChildViewUnder(headerTv.getMeasuredWidth() / 2, 5);
               // 根据该itemView的ContentDes 来为最上面固定的那个header设置内容。
               // 这也就是在绑定数据时,用header数据为itemview设置contentDES的作用
               if (firstView != null && firstView.getContentDescription() != null) {
                   headerTv.setText(String.valueOf(firstView.getContentDescription()));
               }
               View secondView = recyclerView.findChildViewUnder(headerTv.getMeasuredWidth() / 2, headerTv.getMeasuredHeight() + 2);
               if (secondView != null && secondView.getTag() != null) {
                   // 第二个itemview是否有header
                   int secondViewStatus = (int) secondView.getTag();
                   // 这个距离是 第二个带有header的item距离布局中固定的header的底部的距离。
                   // 大于0说明两个header还未接触,小于0说明需要将固定的header向上移动了。
                   // 因为已经是第二个header的列表的天下了。
                   int dealtY = secondView.getTop() - headerTv.getMeasuredHeight(); 
                   if (secondViewStatus==StickyGroupRecyclerViewAdapter.HAS_STICKY_TAG){
                       // 如果有header,就需要处理item自带的header和固定的header的位置关系。
                       if (secondView.getTop()>0){
                           // 在有header的情况下,top>0说明当前item自带的header还没有移动到list顶部。此时需要把固定的那个header 往上推一些top-headerHeight
                           headerTv.setTranslationY(dealtY);
                       }else {
                           // 如果item的header已经有一部分移出去了,固定的header就不要动了,已经完全遮住item的header了
                           headerTv.setTranslationY(0);
                       }
                   }else if (secondViewStatus==StickyGroupRecyclerViewAdapter.NONE_STICKY_TAG){
                       // 如果listitem没有header,那固定的header就不要动了
                       headerTv.setTranslationY(0);
                   }
               }
           }
       });
复制代码

2.4 ItemTouchHelper介绍

RecyclerView中涉及到拖拽就需要使用ItemTouchHelper,这是support v7包提供的处理关于在RecyclerView上添加拖动排序与滑动删除的非常强大的工具类。

ItemTouchHelper的使用主要可分为两步: 第一步是继承ItemTouchHelper.Callback这个抽象类,并实现其中关于拖拽操作的抽象回调方法。 第二步是创建自定义的callback实例对象,并用该实例对象创建itemTouchHelper对象,再通过itemTouchHelper.attachToRecyclerView(mRecyclerView);将 itemTouchHelper和我们的RecyclerView实例对象绑定在一起。

关键点就是 抽象回调方法的实现。

下面我们来看下有哪些常用的回调方法:

    /**
    * 设置滑动类型标记
    * 如果是线性布局,则一般drag可让其支持UP|DOWN
    * 如果是网格布局,则一般drag可让其支持UP|DOWN|LEFT|RIGHT 四个方向
    * swipe状态可以设置LEFT|RIGHT
    * 不允许该类操作则 可设置为0
    * @param recyclerView
    * @param viewHolder
    * 上面两个入参,可用于根据列表类型或ITEM类型来让其支持不同的操作。
    * @return
    */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = 0;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    /***
     * 当drag操作发生时会回调该方法,位置变动可交由adapter去处理,自己实现位置变动的逻辑。
     *
     * @param recyclerView
     * @param viewHolder 正在拖动的item
     * @param target 在移动方向上最近的那个item
     * @return 如果有位置变动返回true 没有变动则返回false
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        mDragViewAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    /**
     * 当item滑动达到指定距离或者达到指定速度,都会回调该方法。
     * 一般可在此处处理滑动删除的逻辑,需要自己在adapter中实现。
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
         mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
    }
复制代码

以上是三个最常用的方法,还有其他一些会用到的方法这里也罗列一下:

    /**
     * 在长按时是否进入drag状态,当为false时,可以在外部调用mItemTouchHelper.startDrag(viewHolder);
     * 来进入drag状态,比如说:通过item的一个按钮的点击事件来触发starDrag方法来进入drag状态。
     */
    public boolean isLongPressDragEnabled() {
            return true;
    }

    /**
     * 当item被drag或swipe选中时回调
     * 此处可以对选中的item做一些状态的更改。
     * @param viewHolder
     * @param actionState
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        // 只要item不是闲置状态,就为其设置背景
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundResource(R.drawable.item_selected);
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    /**
     * 标记当drag中的item移动到target正上方时,target是否变动位置。
     * 默认为true,如果为false则target位置不会变动,拖拽结束原item会回到原来的位置
     * @param recyclerView
     * @param current
     * @param target
     * @return
     */
    @Override
    public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {
        return true;
    }

    /**
     * 当拖拽或滑动完毕后会调用该方法,我们可以在此处还原一些状态。
     * @param recyclerView 
     * @param viewHolder 拖拽或滑动的那个item
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        Log.d(TAG,"clearView item:"+viewHolder.getAdapterPosition());
        viewHolder.itemView.setBackgroundResource(R.color.aqua);
        super.clearView(recyclerView, viewHolder);
    }

    /**
     * 当拖拽的item移动到下方item多少位置的时候触发onMove 方法,自己测试貌似至少要100% 才会触发移动,小于1f的值无效。
     * 当大于1f时,需要达到指定比例的位置才会触发onMove 方法。
     */
    @Override
    public float getMoveThreshold(RecyclerView.ViewHolder viewHolder) {
        return 1.5f;
    }

复制代码

2.5 拖动Item

了解了ItemTouchHelper,实现拖动就简单了。首先我们需要定义一个接口,传入拖动的id和target的id

    public interface ItemTouchHelperListener {
        void onItemMove(int fromPosition, int toPosition);
    }
复制代码

让我们的Adapter实现该接口

    // 交换数据,只更新指定位置item
    @Override
    public void onItemMove(int fromPosition, int toPosition) {
        Collections.swap(mBookList,fromPosition,toPosition);
        notifyItemMoved(fromPosition, toPosition);
    }
复制代码

实现我们自己定义的ItemTouchHelper

public class DragItemTouchHelper extends ItemTouchHelper.Callback {
    private static final String TAG = DragItemTouchHelper.class.getSimpleName();
    private DragViewAdapter mDragViewAdapter;
    public DragItemTouchHelper(DragViewAdapter dragViewAdapter) {
        mDragViewAdapter = dragViewAdapter;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = 0;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        Log.d(TAG, "onMove: " + viewHolder.toString() + "  " + target.toString());
        mDragViewAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return false;
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        // 只要item不是闲置状态,就为其设置背景
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundResource(R.drawable.item_selected);
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        viewHolder.itemView.setBackgroundResource(R.color.aqua);
        super.clearView(recyclerView, viewHolder);
    }
}
复制代码

最后在Activity或Fragment中实现控件的初始化逻辑。

    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    DragViewAdapter adapter = new DragViewAdapter(this);
    ItemTouchHelper.Callback callback = new DragItemTouchHelper(adapter);
    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
    itemTouchHelper.attachToRecyclerView(mRecyclerView);
    mRecyclerView.setAdapter(adapter);
    adapter.setBookList(mData);
复制代码

2.6 滑动删除

滑动删除的实现方式和拖拽基本一样,关键步骤如下:

    // 回调接口定义
    public interface OnItemTouchHelperListener {
        void onItemDelete(int position);
    }

    // 在adapter中实现删除item的回调
    @Override
    public void onItemDelete(int position) {
        if (position < 0 || position > getItemCount()) {
            return;
        }
        data.remove(position);
        notifyItemRemoved(position);
    }

    // ItemTouchHelper.Callback中的关键实现
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = 0;
        int swipeFlags = ItemTouchHelper.LEFT;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return false;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        mSwipeAdapter.onItemDelete(viewHolder.getAdapterPosition());
    }

    // 设置item选中时的背景色
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundResource(R.drawable.item_selected);
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        viewHolder.itemView.setBackgroundResource(R.drawable.item_normal);
        super.clearView(recyclerView, viewHolder);
    }
复制代码

2.7 下拉刷新

关于下拉刷新官方提供了SwipeRefreshLayout可用,在support.v4.widget中,使用也很简单,SwipeRefreshLayout直接包含列表或者ScrollView。

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl_down_refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_down_refresh"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></android.support.v7.widget.RecyclerView>
    </android.support.v4.widget.SwipeRefreshLayout>
复制代码

以下代码模拟了下载事件

    mSwipeRefreshLayout = findViewById(R.id.srl_down_refresh);
    mSwipeRefreshLayout.setColorSchemeResources(
            android.R.color.holo_red_light,
            android.R.color.holo_orange_light,
            android.R.color.holo_green_light,
            android.R.color.holo_blue_light,
            android.R.color.holo_purple
    );
    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mSwipeRefreshLayout.setRefreshing(false);
                }
            },6000);
        }
    });
复制代码

2.8 双向滑动

双向滑动是指一个RecyclerView中包含一个竖向滚动的RecyclerView和一个横向滚动的RecyclerView。实现思路是将Item作为一个List。 主RecyclerView中包含两个元素,一个横向滚动的item,一个竖向滚动的item。一个列表中同时存在两种类型的item,就得有两种ViewHolder。 还需要创建类型标记。

public class SlideAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final String TAG = SlideAdapter.class.getSimpleName();

    private static final int TYPE_HORIZONTAL = 0;
    private static final int TYPE_VERTICAL = 1;

    private Context mContext;
    private List<Integer> mTypeList = new ArrayList<>();

    private List<String> mHorizontalList = new ArrayList<>();
    private List<String> mVerticalList = new ArrayList<>();

    public SlideAdapter(Context context, List<Integer> typeList) {
        mContext = context;
        mTypeList = typeList;
    }

    public void setHorizontalDataList(List<String> horizontalDataList) {
        mHorizontalList = horizontalDataList;

        notifyDataSetChanged();
    }

    public void setVerticalDataList(List<String> verticalDataList) {
        mVerticalList = verticalDataList;

        notifyDataSetChanged();
    }

    /**
     * 多种类型的item就需要重写该方法,根据数据类型决定item类型。
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        if (mTypeList.get(position) == 0) {         // 横向
            return TYPE_HORIZONTAL;
        } else if (mTypeList.get(position) == 1) {  // 纵向
            return TYPE_VERTICAL;
        } else {
            return super.getItemViewType(position);
        }
    }

    /**
     * 根据viewType创建viewholder,
     * @param parent
     * @param viewType 此处的type也是来自于getItemViewType方法
     * @return
     */
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == TYPE_HORIZONTAL) {
            View viewHorizontal = LayoutInflater.from(mContext).inflate(R.layout.slide_horizontal_include, parent, false);
            return new HorizontalViewHolder(viewHorizontal);
        } else if (viewType == TYPE_VERTICAL) {
            View viewVertical = LayoutInflater.from(mContext).inflate(R.layout.slide_vertical_include, parent, false);
            return new VerticalViewHolder(viewVertical);
        }
        return null;
    }

    /**
     * 根据不同类型绑定数据,其实就是创建子列表的过程。
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof HorizontalViewHolder) {
            if (mHorizontalList != null) {
                // 这里就是创建横向滚动的子RecyclerView 的过程。
                SlideHorizontalAdapter horizontalAdapter = new SlideHorizontalAdapter(mContext, mHorizontalList);
                LinearLayoutManager manager = new LinearLayoutManager(mContext);
                manager.setOrientation(LinearLayoutManager.HORIZONTAL);
                ((HorizontalViewHolder) holder).rcvHorizontal.setLayoutManager(manager);
                ((HorizontalViewHolder) holder).rcvHorizontal.setHasFixedSize(true);
                ((HorizontalViewHolder) holder).rcvHorizontal.addItemDecoration(new DividerItemDecoration(mContext, DividerItemDecoration.HORIZONTAL));
                ((HorizontalViewHolder) holder).rcvHorizontal.setAdapter(horizontalAdapter);

                horizontalAdapter.notifyDataSetChanged();
            }
        } else if (holder instanceof VerticalViewHolder) {
            if (mVerticalList != null) {
                SlideVerticalAdapter verticalAdapter = new SlideVerticalAdapter(mContext, mVerticalList);
                ((VerticalViewHolder) holder).rcvVertical.setLayoutManager(new LinearLayoutManager(mContext));
                ((VerticalViewHolder) holder).rcvVertical.setHasFixedSize(true);
                ((VerticalViewHolder) holder).rcvVertical.addItemDecoration(new DividerItemDecoration(mContext, DividerItemDecoration.VERTICAL));
                ((VerticalViewHolder) holder).rcvVertical.setAdapter(verticalAdapter);

                verticalAdapter.notifyDataSetChanged();
            }
        }
    }

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

    public class HorizontalViewHolder extends RecyclerView.ViewHolder {

        RecyclerView rcvHorizontal;

        public HorizontalViewHolder(View itemView) {
            super(itemView);
            rcvHorizontal = itemView.findViewById(R.id.rcv_slide_horizontal);
        }
    }

    public class VerticalViewHolder extends RecyclerView.ViewHolder {

        RecyclerView rcvVertical;

        public VerticalViewHolder(View itemView) {
            super(itemView);
            rcvVertical = itemView.findViewById(R.id.rcv_slide_vertical);
        }
    }
}
复制代码

2.9 收缩展开

item同样分为header和body,点击头部展开body,具体实现如下: expandedPosition 记录处于展开状态的item位置。 mViewHolder 保存处于展开状态的item。 isExpanded 记录当前item是否是展开的。

    public void onBindViewHolder(@NonNull final ExpandCollapseViewHolder holder, int position) {
        holder.tvTeam.setText(mList.get(position));
        holder.tvTeamChild.setText(mList.get(position) + "的子内容");
        // 判断展开的位置和当前位置是否相同,相同则认定为展开的true
        final boolean isExpanded = position == expandedPosition;
        // 如果是展开的则显示body区域,否则gone
        holder.rlChild.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
        holder.rlParent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 若有展开的holder,则让其关闭
                if (mViewHolder != null) {
                    mViewHolder.rlChild.setVisibility(View.GONE);
                    // 更新展开的item
                    notifyItemChanged(expandedPosition);
                }
                // 如果本item是展开的,则重置标记;否则记录位置及holder
                expandedPosition = isExpanded ? -1 : holder.getAdapterPosition();
                mViewHolder = isExpanded ? null : holder;
                // 更新当前item
                notifyItemChanged(holder.getAdapterPosition());
            }
        });
    }
复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值