RecyclerView的使用

RecyclerView
RecyclerView
RecyclerView
RecyclerView
  • RecyclerView是ListView的升级版,它具备了更好的性能,且更容易使用。和ListView一样,RecyclerView是用来显示大量数据的容器,并通过复用有限数量的View,来提高滚动时的性能。当你的视图上的元素经常动态的且有规律的改变时候,可以使用RecyclerView控件。
  • 与ListView不同的是RecyclerView现在不再负责布局,只专注于复用机制,布局交由LayoutManager来管理。 RecyclerView仍然通过Adapter来获取需要显示的对象。
RecyclerView
  • 要使用RecyclerView组件,创建Adapter不再继承自BaseAdapter,而是应该继承自RecyclerView.Adapter类,并且最好指定一个继承自RecyclerView.ViewHolder的范型,Adapter不再要求你返回一个View,而是一个ViewHolder。
  • 继承自Adapter后,需要实现3个抽象方法:
    <pre>
    // 当RecyclerView需要一个ViewHolder时会回调该方法,如果有可复用的View则该方法不会得倒回调
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i);
    // 当一个View需要出现在屏幕上时,该方法会被回调,你需要在该方法中根据数据来更改视图
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i);
    // 用于告诉RecyclerView有多个视图需要显示public int getItemCount();
    </pre>
  • 新的Adapter和原有的Adapter并没有太多的差别,只是不再需要我们写复用判断的逻辑,因为复用逻辑其实都是相似的,它已经有了自身的实现。和原有的Adapter一样,仍然可以通过notifyDataSetChanged来刷新UI,通过getItemViewType来获取对应位置的类型,但是它不再需要你指定有多少类型了,因为该方法已经能够判断出有多少类型。新增的onViewRecycled方法可以让使用者监听View被移除屏幕的时机,并且还提供了一个AdapterDataObserver的观察者,对外提供数据改变时的回调。
  • ViewHolder是对所有的单个item的封装,不仅包含了item需要显示的View,并且还包含和item相关的其它数据,例如:当前的position、之前的position、即将显示的position、被回收的次数、View的类型、是否处于显示中等信息。创建一个ViewHolder需要传递一个View对象,这个View就是该holder的显示视图,该View中通常会包含一些子视图,我们最好把这些子视图都记录在holder中,便于复用时设置不同的数据。
  • RecyclerView不再对布局进行管理,而是通过LayoutManager管理布局,我们可以通过继承自LayoutManager来实现特殊的布局,系统提供了三种常用的布局管理器:
    LinearLayoutManager 线性布局
    GridLayoutManager 九宫格布局
    StaggeredGridLayoutManager 瀑布流布局
  • 并且每一种都可以设置横行和纵向的布局,可惜的均不能添加header,如果要添加header,我们可以在Adapter中使用不同的类型来达到该效果。
  • RecyclerView默认提供了item的增加和删除的动画效果,如果我们使用自定义的动画,需要继承继承RecyclerView.ItemAnimator类,时候时,通过RecyclerView.setItemAnimator()方法来设置我们自定义的动画。
Demo代码:
public class MainActivity extends Activity{

private FrameLayout mDeleteBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // mDeleteBar
    mDeleteBar = (FrameLayout) findViewById(R.id.deleteBar);
    // 创建右下角圆形按钮
    // 创建RecyclerView控件
    // 创建上方的Delete面板

    ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() {

        @Override
        public void getOutline(View view, Outline outline)  {
            // 获取按钮的尺寸
            int fabSize = getResources().getDimensionPixelSize(
                    R.dimen.fab_size);
            // 设置轮廓的尺寸
            outline.setOval(-4, -4, fabSize + 2, fabSize + 2);

        }
    };
    // 获得右下角圆形按钮对象
    View fabView = findViewById(R.id.fab_add);

    fabView.setOutlineProvider(viewOutlineProvider);

    // 获取RecyclerView对象
    final RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);

    //  创建线性布局管理器(默认是垂直方向)
    final LinearLayoutManager layoutManager = new LinearLayoutManager(this);

    //  为RecyclerView指定布局管理对象
    recyclerView.setLayoutManager(layoutManager);

    //  创建列表项分隔线对象
    final RecyclerView.ItemDecoration itemDecoration = new SampleDivider(this);

    //  为RecyclerView控件指定分隔线对象
    recyclerView.addItemDecoration(itemDecoration);
    String s = "a";

    final SampleRecyclerAdapter sampleRecyclerAdapter = new SampleRecyclerAdapter();
    recyclerView.setAdapter(sampleRecyclerAdapter);

    //  处理添加按钮的单击事件
    fabView.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v){
            //  获取第一个可视的Item的位置
            int positionToAdd = layoutManager.findFirstCompletelyVisibleItemPosition();
            //  在该位置插入新的Item
            sampleRecyclerAdapter.addItem(positionToAdd);
        }
    });
   //  处理删除事件
    mDeleteBar.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v){
            int positionToRemove = layoutManager.findFirstCompletelyVisibleItemPosition();

            // 删除第一个可视的ite
            sampleRecyclerAdapter.removeData(positionToRemove);
        }
    });

    //  为RecyclerView控件设置滚动事件
    recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener(){

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

            super.onScrolled(recyclerView, dx, dy);
            //  dx:大于0,向右滚动    小于0,向左滚动
            //  dy:大于0,向上滚动    小于0,向下滚动

            if(dy > 0) {
                //  列表向上滚动,隐藏删除面板
                if(mDeleteBar.getVisibility() == View.VISIBLE) {
                    hideDeleteBar();
                }
            }
            else {
                // 列表向下滚动,显示删除面板
                if(mDeleteBar.getVisibility() == View.GONE) {
                    showDeleteBar();
                }
            }

        }
    });
}

private void showDeleteBar() {

    mDeleteBar.startAnimation(AnimationUtils.loadAnimation(this,
            R.anim.translate_up_on));

    mDeleteBar.setVisibility(View.VISIBLE);
}

private void hideDeleteBar(){

    mDeleteBar.startAnimation(AnimationUtils.loadAnimation(this,
            R.anim.translate_up_off));

    mDeleteBar.setVisibility(View.GONE);
  }
}

public class DemoApp {
//  获取要显示的数据(初始化数据)
public static ArrayList<SampleModel> getSampleData(int size) {
        ArrayList<SampleModel> sampleData = new ArrayList<SampleModel>(size);
        for(int i = 0; i < size; i++)  {
                //  每一项数据后面都有相应的序号
            sampleData.add(new SampleModel("新的列表项<" + i + ">"));
        }
    return sampleData;
}
}

public class SampleDivider extends RecyclerView.ItemDecoratio{
// 默认分隔条Drawable资源的ID
private static final int[] ATTRS ={ android.R.attr.listDivider };
// 分隔条Drawable对象
private Drawable mDivider;

public SampleDivider(Context context) {
    TypedArray a = context.obtainStyledAttributes(ATTRS);
    // 获取系统提供的分隔条Drawable对象
    mDivider = a.getDrawable(0);
    // 回收TypedArray所占用的空间
    a.recycle();
}

// 在该方法中绘制了所有列表项之间的分隔条
@Override
public void onDrawOver(Canvas c, RecyclerView parent) {
    // 获取列表项距离左边缘的距离
    int left = parent.getPaddingLeft();

    // 获取列表项距离右边缘的距离
    int right = parent.getWidth() - parent.getPaddingRight();

    // 获取列表项的总数
    int childCount = parent.getChildCount();

    // 开始绘制所有列表项之间的分隔线
    for (int i = 0; i < childCount; i++){
        // 获得当前的列表项
        View child = parent.getChildAt(i);

        // 获取当前列表项的布局参数信息
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                .getLayoutParams();
        //  计算分隔条左上角的纵坐标
        int top = child.getBottom() + params.bottomMargin;
        //  计算分隔条右下角的纵坐标
        int bottom = top + mDivider.getIntrinsicHeight();
        //  设置分隔条绘制的位置
        mDivider.setBounds(left, top, right, bottom);
        //  开始绘制当前列表项下方的分隔条
        mDivider.draw(c);
    }
}
}

public class SampleModel {

private String sampleText;

public SampleModel(String sampleText) {
    this.sampleText = sampleText;
}

public void setSampleText(String sampleText) {
    this.sampleText = sampleText;
}

public String getSampleText() {
    return sampleText;
}
}

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

private final ArrayList<SampleModel> sampleData = DemoApp.getSampleData(20);

// 用于创建控件
@Override
public ViewHolder onCreateViewHolder(ViewGroup parentViewGroup, int i){

    // 获得列表项控件(LinearLayer对象)

    // list_basic_item.xml布局文件中只包含一个<LinearLayer>标签,在该标签中包含
    // 了一个<TextView>标签
    //  item是LinearLayout对象
    View item = LayoutInflater.from(parentViewGroup.getContext()).inflate(
            R.layout.list_basic_item, parentViewGroup, false);

    return new ViewHolder(item);

}
// 为控件设置数据
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
    //  获取当前item中显示的数据
    final SampleModel rowData = sampleData.get(position);

    //  设置要显示的数据
    viewHolder.textViewSample.setText(rowData.getSampleText());
    viewHolder.itemView.setTag(rowData);
}

@Override
public int getItemCount()  {

    return sampleData.size();
}
//  删除指定的Item
public void removeData(int position) {
    sampleData.remove(position);
    //  通知RecyclerView控件某个Item已经被删除
    notifyItemRemoved(position);

}
//  在指定位置添加一个新的Item
public void addItem(int positionToAdd){
    sampleData.add(positionToAdd,new SampleModel("新的列表项" + new Random().nextInt(10000)));
    //  通知RecyclerView控件插入了某个Item
    notifyItemInserted(positionToAdd);
}

public static class ViewHolder extends RecyclerView.ViewHolder{
    private final TextView textViewSample;

    public ViewHolder(View itemView) {
        super(itemView);

        textViewSample = (TextView) itemView
                .findViewById(R.id.textViewSample);
    }
   }
}
实现ListView和GridView
public class MainActivity extends AppCompatActivity {

private RecyclerView mRecyclerView;
private List<String> datas;  //数据集合
private SimpleAdapter mAdapter; //RecyclerView的适配器

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.home_activity);
    mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    //初始化显示的数据
    datas = new ArrayList<String>();
    for(int i = (int)'A';i < (int)'Z';i++){
        char c = (char) i;
        datas.add(c + "");
    }
    //设置适配器
    mAdapter = new SimpleAdapter(this,datas);
    mRecyclerView.setAdapter(mAdapter);
   /* //设置item之间的分割线
    mRecyclerView.addItemDecoration(new DividerItemDecoration(this,
            DividerItemDecoration.VERTICAL_LIST));*/

    /*//设置为ListView形式显示(方向为垂直)
    RecyclerView.LayoutManager manager = new LinearLayoutManager(this,
            LinearLayoutManager.VERTICAL,false);*/

    /*//设置为GridView的样式并制定列数为4列
    RecyclerView.LayoutManager manager = new GridLayoutManager(this,4);
    */

    //设置为横向滑动的Gridview的样式
    RecyclerView.LayoutManager manager = new StaggeredGridLayoutManager(
            5,StaggeredGridLayoutManager.HORIZONTAL);
    mRecyclerView.setLayoutManager(manager);


}

class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder>{

    Context context;
    List<String> datas;//数据集合
    LayoutInflater inflater;//布局解析器

    public SimpleAdapter(Context context,List<String> datas){
        this.context = context;
        this.datas = datas;
        inflater = LayoutInflater.from(context);
    }

    //创建ViewHolder
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.list_item,parent,false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }

    //绑定ViewHolder
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.tv.setText(datas.get(position));

    }

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

class MyViewHolder extends RecyclerView.ViewHolder{

    public TextView tv;
    public View itemView;
    public MyViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        tv = (TextView) itemView.findViewById(R.id.tv_item);
    }
  }
}

只需要设置不同的LayoutManager就可以实现ListView和GridVIew以及方向的设置

实现瀑布流
public class StaggerActivity extends AppCompatActivity {

private RecyclerView mRecyclerView;
private List<String> datas;  //数据集合
private SimpleAdapter mAdapter; //RecyclerView的适配器
private static List<Integer> mHeight;//存放Item的随机高度

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.home_activity);
    mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    //初始化显示的数据
    datas = new ArrayList<String>();
    mHeight = new ArrayList<Integer>();
    for (int i = (int) 'A'; i < (int) 'Z'; i++) {
        char c = (char) i;
        datas.add(c + "");
        //随机设置每个item的高度
        mHeight.add((int) (100 + Math.random() * 80));
    }
    //设置适配器
    mAdapter = new SimpleAdapter(this, datas);
    mRecyclerView.setAdapter(mAdapter);

    //设置竖直的GridView的样式,然后在Adapter中随机的修改Item的高度就可以实现瀑布流的方式了
    RecyclerView.LayoutManager manager = new StaggeredGridLayoutManager(
            4, StaggeredGridLayoutManager.VERTICAL);
    mRecyclerView.setLayoutManager(manager);
    //设置默认的动画效果(删除和添加Item时呈现)
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    mAdapter.setOnItemClickListener(new ItemClickListener() {
        @Override
        public void onItemClick(View view, int position) {
            Toast.makeText(StaggerActivity.this,"" + position,Toast.LENGTH_SHORT).show();
        }
    });
}

public void onClick(View view){
    int id = view.getId();
    if(id == R.id.add){ //增加Item
        mAdapter.addItem(1);
    }else if(id == R.id.del){ //删除Item
        mAdapter.deleteItem(1);
    }
}
static class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder> {

    Context context;
    List<String> datas;
    LayoutInflater inflater;

    public SimpleAdapter(Context context, List<String> datas) {
        this.context = context;
        this.datas = datas;
        inflater = LayoutInflater.from(context);
    }

    //创建ViewHolder
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.list_item, parent, false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }

    //绑定ViewHolder
    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
        holder.tv.setText(datas.get(position));
       ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
        params.height = mHeight.get(position);
        holder.itemView.setLayoutParams(params);
        //RecycleVie没有提供Item的点击事件,需要自己设置,有多中方法
        //此处采用的是在Adapter中设置
        if(listener != null){
            /**
             * 刷新数据用的是notifyItemChanged:不会刷新有的,只会刷新指定的项
             * 所以直接用函数中的position在有新添加或者删除的item时,位置就不准确了
             * 所以调用holder自带的getLayoutPosition来获取在布局中的位置
             */
            final int layoutPosition = holder.getLayoutPosition();
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.onItemClick(holder.itemView,layoutPosition);
                }
            });
        }
    }

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

    public void addItem(int pos){
        datas.add(pos,"新添加的项");
        //调用这个通知才有动画效果
        notifyItemChanged(pos);
    }

    public void deleteItem(int pos){
        datas.remove(pos);
        notifyItemChanged(pos);
    }

    private ItemClickListener listener;
    public void setOnItemClickListener(ItemClickListener listener){
        this.listener = listener;
    }
}
自定义Item点击监听接口
interface ItemClickListener{
    public void onItemClick(View view,int position);
}

static class MyViewHolder extends RecyclerView.ViewHolder {

    public TextView tv;
    public View itemView;

    public MyViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        tv = (TextView) itemView.findViewById(R.id.tv_item);
    }
  }
}

所谓瀑布流的实现:就是在GridView的基础上动态随机的设置每个Item的高度

注意点

RecyclerView没有实现Item点击监听,需要自己去实现
当有增加和删除Item时,Adapter函数中的position与真实的位置就会冲突,所以通过 holder自带的API:getLayoutPosition()来获取相应的位置
设置Item之间的分割线:也可以这样实现
设置item布局的背景颜色,然后再设置个margin也能区分
资源链接(GitHub)

Item分割线 :DividerItemDecoration

https://gist.github.com/alexfu/0f464fc3742f134ccd1e

DividerItemDecoration:里面用到了系统的属性:通过在Style中增加Item可以实现自己设置的效果

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>

<!--定义RecylerView的Item之间的分割线-->
<item name="android:listDivider">@drawable/divider</item>
</style>

//divider:自定义的shape
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size android:height="5dp" />
    <gradient
        android:centerColor="@android:color/holo_red_light"
        android:endColor="@android:color/holo_green_light"
        android:startColor="@android:color/holo_blue_light" />
</shape>
Item删除和添加的动画

https://github.com/gabrielemariotti/RecyclerViewItemAnimators

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容