列表

qiang.zhang大约 6 分钟

列表

这里我个人只推荐使用RecyclerView,RecyclerView是官方推荐代替Listview和GridView用的, 简单说,Listview和GridView能做的,RecyclerView都能做。

布局

RecyclerView 中的列表项由 LayoutManager 类负责排列。RecyclerView 库提供了三种布局管理器,用于处理最常见的布局情况:

  • LinearLayoutManager 将各个项排列在一维列表中。

  • GridLayoutManager 将所有项排列在二维网格中: 如果网格垂直排列,GridLayoutManager 会尽量使每行中所有元素的宽度和高度相同,但不同的行可以有不同的高度。 如果网格水平排列,GridLayoutManager 会尽量使每列中所有元素的宽度和高度相同,但不同的列可以有不同的宽度。

  • StaggeredGridLayoutManager 与 GridLayoutManager 类似,但不要求同一行中的列表项具有相同的高度(垂直网格有此要求)或同一列中的列表项具有相同的宽度(水平网格有此要求)。其结果是,同一行或同一列中的列表项可能会错落不齐。

  • 如果上面都不满足我们日常的开发需求,可以通过扩展 RecyclerView.LayoutManager 抽象类来创建自己的布局管理器。

   LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this,RecyclerView.HORIZONTAL,false);
   GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
   StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(4,RecyclerView.VERTICAL);
   recyclerView.setLayoutManager(linearLayoutManager);
   recyclerView.setLayoutManager(gridLayoutManager);
   recyclerView.setLayoutManager(staggeredGridLayoutManager);

RecyclerView 的基本使用

实现 Adapter 和 ViewHolder

定义 Adapter 时,需要替换三个关键方法:

  • onCreateViewHolder():每当 RecyclerView 需要创建新的 ViewHolder 时,它都会调用此方法。此方法会创建并初始化 ViewHolder 及其关联的 View,但不会填充视图的内容,因为 ViewHolder 此时尚未绑定到具体数据。

  • onBindViewHolder():RecyclerView 调用此方法将 ViewHolder 与数据相关联。此方法会提取适当的数据,并使用该数据填充 ViewHolder 的布局。例如,如果 RecyclerView 显示的是一个名称列表,该方法可能会在列表中查找适当的名称,并填充 ViewHolder 的 TextView widget。

  • getItemCount():RecyclerView 调用此方法来获取数据集的大小。例如,在通讯簿应用中,这可能是地址总数。RecyclerView 使用此方法来确定什么时候没有更多的列表项可以显示。

代码示例:

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

    private String[] localDataSet;

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder).
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView textView;

        public ViewHolder(View view) {
            super(view);
            // Define click listener for the ViewHolder's View

            textView = (TextView) view.findViewById(R.id.textView);
        }

        public TextView getTextView() {
            return textView;
        }
    }

     public enum Type{
        A,
        B
    }

    @Override
    public int getItemViewType(int position) {
        // return item.type 返回数据的类型,可以根据不同的type生成不同的item,比如微信好友列表的实现
        // return Type.A.ordinal();
        return super.getItemViewType(position);
    }

    /**
     * Initialize the dataset of the Adapter.
     *
     * @param dataSet String[] containing the data to populate views to be used
     * by RecyclerView.
     */
    public CustomAdapter(String[] dataSet) {
        localDataSet = dataSet;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // Create a new view, which defines the UI of the list item
        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.text_row_item, viewGroup, false);
        //  if(viewType ==  Type.A.ordinal()){
        //      View view = LayoutInflater.from(viewGroup.getContext())
        //              .inflate(R.layout.other_a_row_item, viewGroup, false);
        //  }
        //  else{
        //      View view = LayoutInflater.from(viewGroup.getContext())
        //              .inflate(R.layout.other_b_row_item, viewGroup, false);
        //  }

        return new ViewHolder(view);
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.getTextView().setText(localDataSet[position]);
        // 可自定义接口实现item的点击长按等监听事件

        final int currentIndex = position;
        if(onItemOnclickListener!=null){
            viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onItemOnclickListener.onClick(localDataSet[currentIndex]);
                }
            });

            // viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            //          @Override
            //          public boolean onLongClick(View v) {
            //              return false;
            //          }
            //      });
            //
            // viewHolder.itemView.setOnTouchListener(new View.OnTouchListener() {
            //          @Override
            //          public boolean onTouch(View v, MotionEvent event) {
            //              return false;
            //          }
            //      });
        }
    }
    // 为了更方便的展示效果,将相关的代码放在挨得近的地方
    public interface OnItemOnclickListener{
        void onClick(String data);
    }

    private OnItemOnclickListener onItemOnclickListener;

    public void setOnItemOnclickListener(OnItemOnclickListener onItemOnclickListener) {
        this.onItemOnclickListener = onItemOnclickListener;
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return localDataSet.length;
    }
}

子item的布局xml 文件:

<!--- text_row_item.xml -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/list_item_height"
    android:layout_marginLeft="@dimen/margin_medium"
    android:layout_marginRight="@dimen/margin_medium"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/element_text"/>
</FrameLayout>

为列表项添加动画

每当某个项发生变化时,RecyclerView 都会使用 animator 来更改其外观。该 animator 是一个扩展抽象 RecyclerView.ItemAnimator 类的对象。默认情况下,RecyclerView 使用 DefaultItemAnimator 来提供动画。如果想提供自定义动画,可以通过扩展 RecyclerView.ItemAnimator 来定义自己的 animator 对象。

启用列表项选择

借助 recyclerview-selection 库,用户可以通过触摸或鼠标输入来选择 RecyclerView 列表中的项。仍然可以控制所选项的视觉呈现效果。也仍然可以控制用于约束选择行为的政策,例如符合入选条件的项以及可以选择的项数。

如需为 RecyclerView 实例添加对选择操作的支持,可以按以下步骤操作:

  1. 确定要使用的选择键类型,然后构建 ItemKeyProvider。 有三种键类型可供标识所选项:Parcelable(以及所有子类,如 Uri)、String 和 Long。选择键类型,参阅 SelectionTracker.Builder。

  2. 实现 ItemDetailsLookup。 ItemDetailsLookup 使选择功能库能够访问给定 MotionEvent 对应的 RecyclerView 项的相关信息。它实际上是由 RecyclerView.ViewHolder 实例支持(或从中提取)的 ItemDetails 实例的工厂。

  3. 更新 RecyclerView 中的 Views 项,以反映用户已将其选中或取消选中。 选择功能库不会为所选项提供默认视觉装饰。必须在实现 onBindViewHolder() 时提供此设置。建议采用如下方法:

    在onBindViewHolder() 中,对 View 对象调用 setActivated()(而不是 setSelected())并传入 true 或 false(具体取决于对应的项否处于选中状态)。 更新视图样式以表示已激活状态。建议使用颜色状态列表资源来配置样式。

  4. 使用 ActionMode 为用户提供对所选项执行操作所需的工具。 注册 SelectionTracker.SelectionObserver 以在选择状态有变时接收通知。首次选择时,请启动 ActionMode 以向用户表示这一点,并提供特定于该选择的操作。例如,可以向 ActionMode 栏添加删除按钮,然后将栏上的返回箭头连接到取消选择的操作。当选择列表变空时(如果用户取消选择最后一项),请不要忘记终止操作模式。

  5. 执行任何经过解释的次级操作 在事件处理流水线的最后,库可能会判断用户试图通过点按某个项来激活它或试图拖放某个项或一组选定项。请通过注册适当的监听器来回应这些解释。参阅 SelectionTracker.Builder。

  6. 使用 SelectionTracker.Builder 汇编所有内容,将这些部分组合在一起

SelectionTracker tracker = new SelectionTracker.Builder<>(
        "my-selection-id",
        recyclerView,
        new StableIdKeyProvider(recyclerView),
        new MyDetailsLookup(recyclerView),
        StorageStrategy.createLongStorage())
        .withOnItemActivatedListener(myItemActivatedListener)
        .build();

构建 SelectionTracker 实例,必须向 SelectionTracker.Builder 提供之前初始化 RecyclerView 时所用的同一个 RecyclerView.Adapter。因此,创建 RecyclerView.Adapter 后,需要在 SelectionTracker 实例一经创建后就将其注入到 RecyclerView.Adapter 中。否则,将无法通过 onBindViewHolder() 方法检查某个项的已选中状态。

  1. 构建 SelectionTracker 实例,必须向 SelectionTracker.Builder 提供之前初始化 RecyclerView 时所用的同一个 RecyclerView.Adapter。因此,创建 RecyclerView.Adapter 后,需要在 SelectionTracker 实例一经创建后就将其注入到 RecyclerView.Adapter 中。否则,将无法通过 onBindViewHolder() 方法检查某个项的已选中状态。
Loading...