ListView、GridView、RecyclerView分析
ListView
列表数据显示需要4个元素,分别是:
- [1] 用来展示列表的控件ListView
- [2] 给列表装配数据的适配器adapter
- [3] 需要展示的数据集
- [4] 数据集展示需要的item布局View
ListView 复用模板
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//有视图缓存,复用视图
if (convertView == null) {
convertView = View.inflate(context, R.layout.item, null);//视图获取方法,可以用别的方法
} else {
//复用convertView
}
//进行数据绑定,返回item的View
return convertView;
}
ListView的优化模式
ListView通过Adapter模式、观察者模式、Item View复用机制实现了高效的列表显示。
观察者模式源码分析:
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
ListView设置adapter调用方法setAdapter(adapter),在源码中 mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver); 看出观察者应该是AdapterDataSetObserver,但是通过进入更底层的源码分析发现,被观察的对象发生变化应该是AdapterView中onChanged()方法中的requestLayout()。
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
//这里是数据源发生改变时,进行更新,通知所有观察者进行相应操作
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
所以真正的观察者是AdapterView或者其中ViewGroup的方法requestLayout(),ListView是AdapterView的子类,也可以说ListView是观察者,应用了观察者模式。
GridView
类似ListView。只有布局方式不同,其他机制基本一致。
RecyclerView
导入v7包:implementation ‘com.android.support:appcompat-v7:28.0.0’
代码模板:
public class ARecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
protected List<T> tList;//集合数据源
protected Context context;
public ARecyclerAdapter(List<T> tList, Context context) {
this.tList = tList;
this.context = context;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
//创建ViewHolder
MyViewHolder holder;
//添加item布局
View view = LayoutInflater.from(context).inflate(R.layout.item, viewGroup, false);
holder = new MyViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
//绑定数据
MyViewHolder holder = (MyViewHolder) viewHolder;
holder.setData();
}
@Override
public int getItemCount() {
return tList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(@NonNull View itemView) {
super(itemView);
}
public void setData() {
}
}
}
RecyclerView的优点:
- ListView 以及GridView 的复用优化方法在RecyclerView中有封装好的方法,代码量大大减少。
- 将布局方式抽象为LayoutManager,默认提供了LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager 3种布局,对应线性布局、网络布局、交错布局,使用桥接方式把布局职责抽离出去,使得RecyclerView变得更为灵活。
- RecyclerView对于Item View的控制更为精细,可以通过ItemDecotation为Item View 添加装饰,也可以通过ItemAnimator为Item View 添加动画。具有很强大的扩展性。
ItemDecoration
/**
* RecyclerView 的分割线设置
*/
class SimpleItemDecoration : RecyclerView.ItemDecoration {
var width: Int //屏幕宽
var height: Int//屏幕高
var item_height: Int //分割线高度
var paint: Paint//画分割线画笔
var itemPadding: Int = 0// 分割线距离左右的间距,默认是0
constructor(context: Context) : super() {
width = context.resources.displayMetrics.widthPixels
height = context.resources.displayMetrics.heightPixels
//初始化画笔
paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG) //抗锯齿 或者 使位图进行有利的抖动的位掩码标志
paint.color = Color.BLACK
item_height = 1
}
/**
* item 上画分割线的方法
*
* Item图层和画的分割线的图层在同一图层,所以会出现重合情况,
* 这时候就需要使用getItemOffsets方法,设置分割线偏移量,避免重合
*
*/
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
var itemCount = parent.childCount
val child: Int
for (i in 0 until itemCount) {
var view = parent.getChildAt(i)
var top = view.top
var bottom = top + item_height
c.drawRect(itemPadding.toFloat(), top.toFloat(), (width - itemPadding).toFloat(), bottom.toFloat(), paint)
}
}
/**
* 位于onDraw之上
*/
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(c, parent, state)
}
/**
* item的偏移量
*/
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
outRect.bottom = item_height // 分割线相对于item的偏移量
}
}
本文详细分析了Android中的ListView、GridView和RecyclerView。ListView通过Adapter模式、观察者模式和Item View复用实现高效显示。GridView在机制上与ListView相似,只是布局方式不同。RecyclerView引入了LayoutManager和ItemDecoration,提供了更丰富的布局选择和扩展性,优化了代码量,增加了灵活性和动画支持。
1718

被折叠的 条评论
为什么被折叠?



