RecyclerView的基本介绍
RecyclerView是谷歌V7包下新增的控件,用来替代ListView的使用,在RecyclerView标准化了ViewHolder类似于ListView中convertView用来做视图缓.
RecyclerView好处
①RecylerView封装了viewholder的回收复用,也就是说RecylerView标准化了ViewHolder,编写Adapter面向的是 ViewHolder而不再是View了,复用的 逻辑被封装了,写起来更加简单。
②提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecylerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。例如recyclerview不局限与下拉列表,它还支持GridView效果和瀑布流效果
③可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecylerView有其自己默认的实现。
涉及到的类
Adapter:使用RecyclerView之前,你需要一个继承自RecyclerView.Adapter的适配器,作用是将数据与每一个item的界面进行绑定。
LayoutManager:用来确定每一个item如何进行排列摆放,何时展示和隐藏。回收或重用一个View的时候,LayoutManager会向适配器请求新的数据来替换旧的数据,这种机制避免了创建过多的View和频繁的调用findViewById方法(与ListView原理类似)。
RecyclerView的三种显示方式
目前SDK中提供了三种自带的LayoutManager:
LinearLayoutManager
GridLayoutManager
StaggeredGridLayoutManager
RecyclerView依赖,布局,属性使用
1、添加依赖
implementation ‘com.android.support:recyclerview-v7:27.1.1’
2.然后是在XML文件用使用它
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
3.创建完布局之后在MainActivity中获取这个RecyclerView,并声明LayoutManager与Adapter,代码如下:
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this );
//设置布局管理器
recyclerView.setLayoutManager(layoutManager);
//设置为垂直布局,这也是默认的
layoutManager.setOrientation(OrientationHelper. VERTICAL);
//设置Adapter
recyclerView.setAdapter( recycleAdapter);
//设置分隔线
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));
//设置增加或删除条目的动画
recyclerView.setItemAnimator( new DefaultItemAnimator());
RecyclerView的适配器RecyclerView.Adapter
RecyclerView.Adapter
①onCreateViewHolder()
这个方法主要生成为每个Item inflater出一个View,但是该方法返回的是一个ViewHolder。该方法把View直接封装在ViewHolder中,然后我们面向的是ViewHolder这个实例,当然这个ViewHolder需要我们自己去编写。直接省去了当初的convertView.setTag(holder)和convertView.getTag()这些繁琐的步骤。
②onBindViewHolder()
这个方法主要用于适配渲染数据到View中。方法提供给你了一个viewHolder,而不是原来的convertView。
③getItemCount()
这个方法就类似于BaseAdapter的getCount方法了,即总共有多少个条目。
创建适配器和ViewHolder
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private final ArrayList<String> mData;
private final Context mContext;
private final LayoutInflater mInflater;
private OnItemClickListener mListener;
public MyAdapter(Context context,ArrayList<String> data) {
this.mContext = context;
this.mData = data;
mInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public MyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//创建一个自定义ViewHolder
View inflate = mInflater.inflate(R.layout.item, parent,false);
ViewHolder viewHolder = new ViewHolder(inflate);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull MyAdapter.ViewHolder holder, final int position) {
//将数据与界面绑定
holder.mTv.setText(mData.get(position));
if (position%2 == 0){
holder.mIv.setImageResource(R.drawable.friend_circle_default);
}else{
holder.mIv.setImageResource(R.drawable.cate);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这个方法被调用的时候,想要让MainActivity知道
if (mListener != null){
mListener.onItemClick(v,position);
}
}
});
}
//1.定义一个接口
public interface OnItemClickListener{
void onItemClick(View view,int position);
}
//2.传递接口类实现对象
public void setOnItemClickListener(OnItemClickListener listener){
this.mListener = listener;
}
@Override
public int getItemCount() {
//获取item数量
return mData.size();
}
/**
* RecyclerView 内部封装了VIewHolder的回收复用,标准化了ViewHolder,编写Adapter就是面向ViewHolder,而不是View
*/
public class ViewHolder extends RecyclerView.ViewHolder{
private TextView mTv;
private ImageView mIv;
public ViewHolder(View itemView) {
super(itemView);
mTv = itemView.findViewById(R.id.tv);
mIv = itemView.findViewById(R.id.iv);
}
}
}
RecyclerView接口回调
1.定义接口
在哪里定义?
定义外部接口和内部接口都可以,google一般定义内部,所以我们也定义内部接口
接口里有什么方法
取决于我们要做什么,需要什么方法就定义什么
//Adapter内部定义
public interface OnItemClickListener{
void onItemClick(View view,int position);
}
2.设置传递接口类实现对象的方法
public void setOnItemClickListener(OnItemClickListener listener){
this.mListener = listener;
}
3.传递接口实现类对象
mAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "position:" + position, Toast.LENGTH_SHORT).show();
startActivity(new Intent(MainActivity.this, MultiTypeActivity.class));
}
});
Recycleview优化:
1,如果确定recycleview中数据变化的位置,可以对其进行局部刷新
2,在编写布局的时候,如果能通过代码实现排布,可以将xml方式进行替换,提升页面加载的效率;另外尤其在多布局展示中,对应不同item中公用的一些控件或组件,可以通过自定义view的方式,减少view的构造和嵌套。
3,如果一个item中,多个子view都需要设置点击事件的监听,我们可以设置一个全局的监听,然后通过view.getId或view.getTag来找到具体item,将具体view的点击事件设置到viewHolder中,避免列表在不停滑动过程中,犹豫onbindviewholder要反复绘制数据带来的点击事件的重复创建。
4,如果一个recyclerview中嵌套了多个recyclerview,而嵌套recycerview的适配器是一样的,可以共享一个对象池。
5,对recyclerview数据的预加载,当我们加载完一页数据后,不管用户有没有及时上拉查看,我们可以再后台动态的加载一定数量的数据,当列表上划时,计算划出的条目数量,然后从预加载的数据中取出对应条目的数量追加到recycleview末尾,这样给用户更为流畅的滑动体验。
6,对于scrollview中嵌套recyclerview,我们可以调用setNestedScrollingEnabled(false)来禁止recycleview的滑动,以确保外部view可流畅的滑动。
7,如果item高度固定,可以设置RecyclerView.setHasFixedSize(true); 来避免 requestLayout 浪费资源;
8,调用RecyclerView.onViewRecycled(holder) 来回收资源。
9,通过 RecycleView.setItemViewCacheSize(size); 来加大 RecyclerView 的缓存,用空间换时间来提高滚动的流畅性
10,瀑布流上拉加载时发生item抖动,建议采用notifyItemRangeChanged进行局部刷新
关于recycleview刷新是条目闪烁问题
1,如果仅仅是刷新某一个item,一定要调用指定位置的局部刷新,尽量避免更为消耗资源的全部刷新
2,如果没有给item加出场动画,尽量把默认的动画禁止掉,防止onbindviewholder重绘item的时候,由于出场动画给item的刷新带来闪烁的bug
3,对于item中图片的闪烁,一方面要注意图片的加载是否加了出场动画(比如glide默认有出场动画,应该把它禁止掉),第二就是看有没有在刷新的时候动态绘制imageview的形状,如果绘制效率不高的情况下,也会引发图片闪烁,第三在item中尽量不要使用办透明效果,尤其是数据量较大或者涉及到需要频繁刷新的情况,因为半透明绘制底层涉及到大量的数学运算,而这些严重影响item的绘制,所以当item数量庞大时,会出现item闪烁甚至滑动卡顿的问题。