一、使用ViewHolder提升性能
在Android 5.0以前,RecyclerView使用ViewHolder来重用布局并减少内存消耗。ViewHolder是一个包含视图组件的容器,可以提高视图重用的效率。我们可以通过继承RecyclerView.ViewHolder来创建ViewHolder。
class MyViewHolder extends RecyclerView.ViewHolder { TextView name; TextView age; public MyViewHolder(View view) { super(view); name = (TextView) view.findViewById(R.id.name); age = (TextView) view.findViewById(R.id.age); } }
ViewHolder的使用可以减少inflate布局的次数,从而提高RecyclerView的性能。在onBindViewHolder()方法中,我们可以将数据绑定到ViewHolder中的视图组件上。
@Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.name.setText(mData.get(position).getName()); holder.age.setText(mData.get(position).getAge()); }
二、使用DiffUtil优化数据更新
RecyclerView经常需要更新数据,而DiffUtil可以提高数据更新的效率。DiffUtil是一个工具类,用于计算两个数据集之间的差异。在数据更新时,我们可以使用DiffUtil来计算数据集差异,从而只更新变化的部分,减少不必要的重绘和动画效果。
class MyDiffCallback extends DiffUtil.Callback { private ListoldList; private List newList; public MyDiffCallback(List oldList, List newList) { this.oldList = oldList; this.newList = newList; } @Override public int getOldListSize() { return oldList.size(); } @Override public int getNewListSize() { return newList.size(); } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId(); } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { MyData oldData = oldList.get(oldItemPosition); MyData newData = newList.get(newItemPosition); return oldData.getName().equals(newData.getName()) && oldData.getAge() == newData.getAge(); } }
DiffUtil需要实现四个回调方法,getOldListSize()和getNewListSize()返回旧数据集和新数据集的大小,areItemsTheSame()判断两个数据是否为同一个item,areContentsTheSame()判断同一位置的两条数据是否内容相同。
在数据更新时,我们需要在新数据集合成前使用DiffUtil计算差异,将差异应用到适配器中并更新RecyclerView。
MyDiffCallback diffCallback = new MyDiffCallback(mData, newData); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback); mData = newData; diffResult.dispatchUpdatesTo(this);
三、使用ItemDecoration美化界面和提升性能
ItemDecoration可以为RecyclerView添加自定义的分割线、颜色、边距等,使界面更加美观,同时也可以提高RecyclerView的性能。我们通常使用RecyclerView.ItemDecoration来创建ItemDecoration。
public class MyItemDecoration extends RecyclerView.ItemDecoration { private int mDividerHeight; private Paint mPaint; public MyItemDecoration(int dividerHeight, int dividerColor) { mDividerHeight = dividerHeight; mPaint = new Paint(); mPaint.setColor(dividerColor); mPaint.setStyle(Paint.Style.FILL); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.bottom = mDividerHeight; } @Override public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) { int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); for (int i = 0; i < parent.getChildCount() - 1; i++) { View child = parent.getChildAt(i); float top = child.getBottom(); canvas.drawRect(left, top, right, top + mDividerHeight, mPaint); } } }
在RecyclerView中添加ItemDecoration。
MyItemDecoration itemDecoration = new MyItemDecoration(dp2px(1), Color.GRAY); recyclerView.addItemDecoration(itemDecoration);
四、使用RecyclerView滑动监听减少内存消耗
RecyclerView在滑动时,会大量的创建和销毁视图,如果在滑动过程中同时处理复杂的数据操作,会导致卡顿或者OOM。因此,我们可以在RecyclerView滑动时停止加载图片或者其他占用内存的操作,从而减少内存消耗,提高滑动性能。我们可以通过RecyclerView的addOnScrollListener()监听RecyclerView的滑动状态。
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); switch (newState) { case RecyclerView.SCROLL_STATE_IDLE: //滑动停止,可以执行数据加载等其他操作 break; case RecyclerView.SCROLL_STATE_DRAGGING: //手指触摸滑动中,应停止数据加载等流操作 break; case RecyclerView.SCROLL_STATE_SETTLING: //惯性滑动中,应停止数据加载等流操作 break; } } });
五、使用LayoutManager优化布局
LayoutManager是RecyclerView中布局管理器的基类,可以用于控制RecyclerView中子View的布局方式。使用LayoutManager可以实现多种复杂的布局,并且可以优化RecyclerView的性能。下面介绍几种常见的LayoutManager。
1. LinearLayoutManager
LinearLayoutManager是RecyclerView默认的布局管理器,控制子View的排列方向,有三种排列方式:VERTICAL、HORIZONTAL、Horizontal.VERTICAL。
LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager);
2. GridLayoutManager
GridLayoutManager可以将子View排列成网格形式。可以通过GridLayoutManager的构造函数来控制列数。
GridLayoutManager layoutManager = new GridLayoutManager(this, 2); recyclerView.setLayoutManager(layoutManager);
3. StaggeredGridLayoutManager
StaggeredGridLayoutManager可以将子View排列成不规则的网格形式。可以通过StaggeredGridLayoutManager的构造函数来控制列数和方向。
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager);
完整代码示例
public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private ListmData; private MyAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = findViewById(R.id.recyclerView); initData(); mAdapter = new MyAdapter(mData); recyclerView.setAdapter(mAdapter); LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); MyItemDecoration itemDecoration = new MyItemDecoration(dp2px(1), Color.GRAY); recyclerView.addItemDecoration(itemDecoration); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); switch (newState) { case RecyclerView.SCROLL_STATE_IDLE: //滑动停止,可以执行数据加载等其他操作 break; case RecyclerView.SCROLL_STATE_DRAGGING: //手指触摸滑动中,应停止数据加载等流操作 break; case RecyclerView.SCROLL_STATE_SETTLING: //惯性滑动中,应停止数据加载等流操作 break; } } }); } private void initData() { mData = new ArrayList<>(); mData.add(new MyData(1, "张三", 18)); mData.add(new MyData(2, "李四", 20)); mData.add(new MyData(3, "王五", 22)); mData.add(new MyData(4, "赵六", 21)); } public int dp2px(float dpValue) { final float scale = getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } class MyAdapter extends RecyclerView.Adapter { private List mData; public MyAdapter(List data) { mData = data; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); MyViewHolder viewHolder = new MyViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.name.setText(mData.get(position).getName()); holder.age.setText(mData.get(position).getAge()); } @Override public int getItemCount() { return mData.size(); } } class MyViewHolder extends RecyclerView.ViewHolder { TextView name; TextView age; public MyViewHolder(View view) { super(view); name = (TextView) view.findViewById(R.id.name); age = (TextView) view.findViewById(R.id.age); } } class MyData { private int id; private String name; private int age; public MyData(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public String getName() { return name; } public int getAge() { return age; } } class MyItemDecoration extends RecyclerView.ItemDecoration { private int mDividerHeight; private Paint mPaint; public MyItemDecoration(int dividerHeight, int dividerColor) { mDividerHeight = dividerHeight; mPaint = new Paint(); mPaint.setColor(dividerColor); mPaint.setStyle(Paint.Style.FILL); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.bottom = mDividerHeight; } @Override public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) { int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); for (int i = 0; i < parent.getChildCount() - 1; i++) { View child = parent.getChildAt(i); float top = child.getBottom(); canvas.drawRect(left, top, right, top + mDividerHeight, mPaint); } } } class MyDiffCallback extends DiffUtil.Callback { private List oldList; private List newList; public MyDiffCallback(List oldList, List newList) { this.oldList = oldList; this.newList = newList; } @Override public int getOldListSize() { return oldList.size(); } @Override public int getNewListSize() { return newList.size(); } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId(); } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { MyData oldData = oldList.get(oldItemPosition); MyData newData = newList.get(newItemPosition); return oldData.getName().equals(newData.getName()) && oldData.getAge() == newData.getAge(); } } }