转载
https://blog.csdn.net/u012124438/article/details/53495951
首先添加依赖
implementation 'com.android.support:recyclerview-v7:28.0.0-alpha3'
版本号要与supportv7库一致,不然报错
如果使用了androidx版本,更改依赖
implementation 'androidx.recyclerview:recyclerview:1.0.0'
更多androidx版本以及配套库的变化,参考开发文档
RecycleView的调用
LinearLayoutManager linear = new LinearLayoutManager(this.getContext());
linear.setOrientation(LinearLayoutManager.VERTICAL);
rvPictures.setLayoutManager(linear);
RecycleViewAdapter adapter=new RecycleViewAdapter(this.getContext(),datas);
rvPictures.setAdapter(adapter);
需要实现自己的adapter类和Item布局
1 上Item布局(每个子选项的布局)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_pictures"
android:layout_width="200dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
/>
</LinearLayout>
2然后是自定义适配器
2.1适配器需要有接收的数据和获取的上下文
private Context mContext;
private List<Integer> datas;
然后是构造器
public RecycleViewAdapter(Context mContext, List<Integer> datas) {
this.mContext = mContext;
this.datas = datas;
}
2.2实现ViewHolder
Viewholder中包含子布局的视图实例和子布局中的控件,并在构造器中初始化
class MyHolder extends RecyclerView.ViewHolder{
private ImageView mImageview;
public MyHolder(View itemView) {
super(itemView);
this.mImageview = (ImageView) itemView.findViewById(R.id.iv_pictures);
}
}
2.3必须重载的3个方法
getItemCount,返回Item数目
@Override
public int getItemCount() {
return datas.size();
}
onCreateViewHolder 动态加载子布局,并把获得的view封装在ViewHolder中返回
@NonNull
@Override
public RecycleViewAdapter.MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View mView= LayoutInflater.from(mContext).inflate(R.layout.recycly_item_layout,null);
MyHolder myHolder=new MyHolder(mView);
return myHolder;
}
onBindViewHolder根据adapter的来初始化holder中控件的属性
@Override
public void onBindViewHolder(@NonNull RecycleViewAdapter.MyHolder holder, final int position) {
holder.mImageview.setImageResource(datas.get(position).intValue());
}
3 给Item增加点击功能
3.1在adapter中定义回调接口,在活动中具体实现
首先,adapter中保存有监听器变量
private OnItemClickListener mOnItemClickListener;
其次,定义回调接口和监听器变量的设置入口
public interface OnItemClickListener{
void ItemClick(View view,int position);
}
public void setOnItemClickListener(OnItemClickListener itemClickListener){
this.mOnItemClickListener=itemClickListener;
}
在绑定ViewHolder的实现中,给每个itemView设置点击事件
@Override
public void onBindViewHolder(@NonNull RecycleViewAdapter.MyHolder holder, final int position) {
holder.mImageview.setImageResource(datas.get(position).intValue());
if (mOnItemClickListener!=null){
holder.mImageview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.ItemClick(v,position);
}
});
}
}
最后在活动中实现ItemClick()方法
adapter.setOnItemClickListener(new RecycleViewAdapter.OnItemClickListener() {
@Override
public void ItemClick(View view, int position) {
Toast.makeText(getActivity(), "You click picture "+position, Toast.LENGTH_SHORT).show();
}
});
示例代码
adapter文件
public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.MyHolder>{
private Context mContext;
private List<Integer> datas;
private OnItemClickListener mOnItemClickListener;
public RecycleViewAdapter(Context mContext, List<Integer> datas) {
this.mContext = mContext;
this.datas = datas;
}
public interface OnItemClickListener{
void ItemClick(View view,int position);
}
public void setOnItemClickListener(OnItemClickListener itemClickListener){
this.mOnItemClickListener=itemClickListener;
}
@NonNull
@Override
public RecycleViewAdapter.MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View mView= LayoutInflater.from(mContext).inflate(R.layout.recycly_item_layout,parent,false);
MyHolder myHolder=new MyHolder(mView);
return myHolder;
}
@Override
public void onBindViewHolder(@NonNull RecycleViewAdapter.MyHolder holder, final int position) {
holder.mImageview.setImageResource(datas.get(position).intValue());
if (mOnItemClickListener!=null){
holder.mImageview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.ItemClick(v,position);
}
});
}
}
@Override
public int getItemCount() {
return datas.size();
}
class MyHolder extends RecyclerView.ViewHolder{
private ImageView mImageview;
public MyHolder(View itemView) {
super(itemView);
this.mImageview = (ImageView) itemView.findViewById(R.id.iv_pictures);
}
}
}
活动文件
public class BlankFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_blank, container, false);
rvPictures=(RecyclerView)view.findViewById(R.id.rv_pictures);
List<Integer> datas=new ArrayList<>();
datas.add(R.drawable.ic_back_1);
datas.add(R.drawable.ic_back_2);
datas.add(R.drawable.ic_back_3);
datas.add(R.drawable.ic_back_4);
datas.add(R.drawable.ic_back_5);
datas.add(R.drawable.ic_back_6);
LinearLayoutManager linear = new LinearLayoutManager(this.getContext());
linear.setOrientation(LinearLayoutManager.VERTICAL);
rvPictures.setLayoutManager(linear);
RecycleViewAdapter adapter=new RecycleViewAdapter(this.getContext(),datas);
adapter.setOnItemClickListener(new RecycleViewAdapter.OnItemClickListener() {
@Override
public void ItemClick(View view, int position) {
Toast.makeText(getActivity(), "You click picture "+position, Toast.LENGTH_SHORT).show();
}
});
rvPictures.setAdapter(adapter);
return view;
}
}
扩展:给recyclerView添加分隔线
在ListView中,xml配置中是有divider属性的
RecyclerView中同样的有分割线属性,但是仅支持线性布局
来源 https://blog.csdn.net/lindroid20/article/details/76407954
用法
一,默认的分割线
直接给控件设置代码,注意方向要与LinearLayouManage布局方向一致
DividerItemDecoration divider=new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
rvStructureObject.addItemDecoration(divider);
二,设置一个自定义分割线
实现一个透明的分割线,露出底部view颜色
首先是样式布局
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent"/>
<size android:height="8dp"/>
</shape>
然后是实际应用
DividerItemDecoration divider=new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
divider.setDrawable(getResources().getDrawable(R.drawable.shape_transparent_divider));
rvStructureObject.addItemDecoration(divider);
三 如何让RecyclerView实现加载不同的ItemView效果
RecyclerView提供了两个接口
获取到对应位置的viewType
getItemViewType(int position)
根据ViewType来加载不同的ViewHolder
onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
如何设置ViewType
这个依据实现,比如根据position参数,偶数位置实现一个效果,奇数位置实现一个效果
然后根据不同的Type,返回不同的ViewHolder
在绑定ViewHolder数据的时候,判断Holder的类型来初始化.
List<T> list;
getItemViewType(int position) {
return position%2 ? 0:1;
}
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE.ITEM_TYPE_LEFT.ordinal()) {
return new LeftViewHolder(mLayoutInflater.inflate(R.layout.item_left, parent, false));
} else {
return new RightViewHolder(mLayoutInflater.inflate(R.layout.item_right, parent, false));
}
}
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof LeftViewHolder) {
((LeftViewHolder) holder).tvContent.setText(list.get(position).getContent());
} else if (holder instanceof RightViewHolder) {
((RightViewHolder) holder).tvContent.setText(list.get(position).getContent());
}
}
ViewType的初始化,也可以作为data对象的属性,这样更自由一些
public int getItemViewType(int position) {
return list.get(position).getType();
}
四 滚动监听
需要实现用户下拉到底部加载新的数据的效果
滑动监听事件,建议参考资料 https://blog.csdn.net/kgpszjia/article/details/51679063
首先监听到滑动到底部事件
条件,完全看到最后1条item,并且isloading为假(说明之前没有到达底部)
监听到底部以后,设置isLoading为真.开始模拟下载,为了通知用户,可以添加一个特殊的Item给RecyclerView,或者设置一个progressBar.这个依靠效果而定.这里采用的是添加progressBar.
List<KeyString> list;
//设置标志位,滑动到底部时,截断监听
private boolean isLoading;
然后设置监听
rvList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//int position = linearLayoutManager.findLastCompletelyVisibleItemPosition();
//int position2 = linearLayoutManager.findLastVisibleItemPosition();
//Log.i(TAG, "onScrolled: position1=" + position);
//Log.i(TAG, "onScrolled: position2=" + position2);
if (isLoading == false && linearLayoutManager.findLastCompletelyVisibleItemPosition() == (list.size() - 1)) {
isLoading = true;
progressBar.setVisibility(View.VISIBLE);
}
}
});
我们再加个按钮点击事件,模拟下载完毕
删除新加的item/隐藏进度条,重置isLoading为false;
@OnClick({R.id.b_left, R.id.b_right,R.id.cancel})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.b_left:
break;
case R.id.b_right:
break;
case R.id.cancel:
isLoading=false;
progressBar.setVisibility(View.GONE);
break;
}
}
五,滑动控件的嵌套
有这么一种需求,一个RecyclerView的Item中嵌套一个ListView
遇到两个问题
问题1.ListView显示不全,ListView设置为wrap方式时,只能显示一个item,多余的item被折叠
解决办法,1,固定死ListView的高度,缺点是不太适合高度需要动态变化的情况
解决办法2,计算高度,代码中设置ListView的高度
//我把item的高度存储在adapter中,在给listView设置adapter后,计算高度并存储
int itemHeight=adapter.getItemHeight();
if (itemHeight==0){
if (list.size()!=0){
View itemView=adapter.getView(0,null,lvCustomList);
itemView.measure(0,0);
itemHeight=itemView.getMeasuredHeight();
adapter.setItemHeight(itemHeight);
Log.i(TAG, "onBindChild: itemHeight="+itemHeight);
}
}
//然后就可以根据需求设置控件高度
//我希望的是item最多显示5条
//ListView除了item还有分割线需要计算高度.我设置的分割线高度为8dp,
int listHeight=adapter.getListHeight();
if (listHeight==0){
if (list.size()!=0){
//1dp在屏幕显示的真实高度
int dpValue=(int)
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,
MyApplication.getmContext().getResources().getDisplayMetrics());
if (list.size()>5){
listHeight=5*itemHeight+40*dpValue;
}else {
listHeight=list.size()*itemHeight+list.size()*8*dpValue;
}
adapter.setListHeight(listHeight);
ViewGroup.LayoutParams lp=lvCustomList.getLayoutParams();
LogUtil.i(TAG, "onBindChild: listHeight="+listHeight);
lp.height=listHeight;
//设置高度
lvCustomList.setLayoutParams(lp);
}
}
问题2.嵌套的ListView与RecyclerView滑动冲突.
我希望的是item超过5个时,可以滚动查看.实际情况是只能RecyclerView滚动,
解决办法,设置滚动监听
lvCustomList.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "onTouch: parentView="+itemView.getParent());
Log.i(TAG, "onTouch: event="+event.getAction());
if (event.getAction()==MotionEvent.ACTION_UP){
// 把触摸事件还给父控件
itemView.getParent().requestDisallowInterceptTouchEvent(false);
}else {
// 取消父控件对触摸事件的处理
itemView.getParent().requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
这里有个坑,如果对ListView的Item的整个View设置了点击事件,会导致触摸监听起不到作用,我的办法是,把Item的点击事件,只设置给Item中具体的某一个控件.
填坑1
如何监听recyclerView滚动到底部或者顶部
参考资料 https://www.jianshu.com/p/c138055af5d2
方法1:当可视item就是最后一个item的时候,滚动到底部
public static boolean isVisBottom(RecyclerView recyclerView){
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
//屏幕中最后一个可见子项的position
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
//当前屏幕所看到的子项个数
int visibleItemCount = layoutManager.getChildCount();
//当前RecyclerView的所有子项个数
int totalItemCount = layoutManager.getItemCount();
//RecyclerView的滑动状态
int state = recyclerView.getScrollState();
if(visibleItemCount > 0 && lastVisibleItemPosition == totalItemCount - 1 && state == recyclerView.SCROLL_STATE_IDLE){
return true;
}else {
return false;
}
}
方法2:利用view自带的方法,当前显示长度+滚动过得长度,和view的最大范围比较
public static boolean isSlideToBottom(RecyclerView recyclerView) {
if (recyclerView == null) return false;
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange())
return true;
return false;
}
方法3:利用recyclerView封装好的方法2,提供的接口函数
RecyclerView.canScrollVertically(1)的值表示是否能向上滚动,false表示已经滚动到底部
RecyclerView.canScrollVertically(-1)的值表示是否能向下滚动,false表示已经滚动到顶部
方法4:最后一种方法其实是比较呆板的,就是利用LinearLayoutManager的几个方法,1.算出已经滑过的子项的距离,2.算出屏幕的高度,3.算出RecyclerView的总高度。然后用他们做比较,原理类似于方法二。
填坑2
接着上面的坑
如何获得最后一个View
有两种方式
LinearLayoutManager manager;
//方法1
View lastView1=manager.getChildAt(count-1);
//方法2
View lastView2=manager.findViewByPosition(count-1);
但是实际上经常lastView1返回null(当列表的数目超过当前显示的数量时)
这是因为复用机制,getChildAt()方法获得的Child,只存在屏幕中,屏幕中的View一旦划出屏幕,就被复用成新的View
所以方法2获得的View也只有在屏幕中时才不为null
方法1需要修改为
View lastView1=manager.getChildAt(count-manager.findFirstVisibleItemPosition()-1);
类似的,ListView也存在复用机制
ListView lvList;
//方法1,获取当前屏幕中view的数量
lvList.getChildCount();
//获取List数据列表中的数量
lvList.getCount();
要获取屏幕中最后一个View
View lastView=lvList.getChildAt(count-lvList.getFirstVisiblePosition()-1);