Recycleview实现无限自动轮播
概述
RecycleView实现特定数据无限重复滑动在我看来不外乎有两种方法
1.修改adpter的复用机制,无限复用数据
2.在adpter中返回数据长度返回Integer的最大值
由于第一种虽然能实现数据的无限重复但是数据位还是没有任何变化,所以在自动跳转至最后的时候无法在向下一位轮播,所以在这里我使用第二种方式实现自动轮播
简单讲述修改adpter的复用机制
我们拿LinearLayoutManager线性的为例子,我们只需要重新LinearLayoutManager在绘制的时候做一些手手脚就可以实现
packagecom.li.liproject.recycle; importandroid.content.Context; importandroid.util.AttributeSet; importandroid.util.DisplayMetrics; importandroid.util.Log; importandroid.view.View; importandroid.view.ViewGroup; importandroidx.recyclerview.widget.LinearLayoutManager; importandroidx.recyclerview.widget.LinearSmoothScroller; importandroidx.recyclerview.widget.RecyclerView; /** *@author版本:1.0 *创建日期:2020/4/1414 *描述: */ publicclassScrollSpeedLinearLayoutMangerextendsLinearLayoutManager{ publicScrollSpeedLinearLayoutManger(Contextcontext){ super(context); } publicScrollSpeedLinearLayoutManger(Contextcontext,intorientation,booleanreverseLayout){ super(context,orientation,reverseLayout); } publicScrollSpeedLinearLayoutManger(Contextcontext,AttributeSetattrs,intdefStyleAttr,intdefStyleRes){ super(context,attrs,defStyleAttr,defStyleRes); } @Override publicRecyclerView.LayoutParamsgenerateDefaultLayoutParams(){ returnnewRecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT); } //1在RecyclerView初始化时,会被调用两次。 //2在调用adapter.notifyDataSetChanged()时,会被调用。 //3在调用setAdapter替换Adapter时,会被调用。 //4在RecyclerView执行动画时,它也会被调用。 @Override publicvoidonLayoutChildren(RecyclerView.Recyclerrecycler,RecyclerView.Statestate){ Log.d("TAG","onLayoutChildren"); if(getItemCount()==0){ detachAndScrapAttachedViews(recycler); return; } //state.isPreLayout()是支持动画的 if(getItemCount()==0&&state.isPreLayout()){ return; } //将当前Recycler中的view全部移除并放到报废缓存里,之后优先重用缓存里的view detachAndScrapAttachedViews(recycler); intactualHeight=0; for(inti=0;igetHeight()){ break; } } } @Override publicbooleancanScrollVertically(){ returntrue; } @Override publicintscrollVerticallyBy(intdy,RecyclerView.Recyclerrecycler,RecyclerView.Statestate){ Log.d("feifeifei","getChildCount()"+getChildCount()+"recycler.getScrapList().size()"+recycler.getScrapList().size()); //界面向下滚动的时候,dy为正,向上滚动的时候dy为负 //填充 fill(dy,recycler,state); //滚动 offsetChildrenVertical(dy*-1); //回收已经离开界面的 recycleOut(dy,recycler,state); returndy; } privatevoidfill(intdy,RecyclerView.Recyclerrecycler,RecyclerView.Statestate){ //向下滚动 if(dy>0){ //先在底部填充 ViewlastView=getChildAt(getChildCount()-1); intlastPos=getPosition(lastView); if(lastView.getBottom()-dy =0){ Viewscrap; if(layoutPostion==0){ scrap=recycler.getViewForPosition(getItemCount()-1); }else{ scrap=recycler.getViewForPosition(layoutPostion-1); } addView(scrap,0); measureChildWithMargins(scrap,0,0); intwidth=getDecoratedMeasuredWidth(scrap); intheight=getDecoratedMeasuredHeight(scrap); layoutDecorated(scrap,0,firstView.getTop()-height,width,firstView.getTop()); } } } privatevoidrecycleOut(intdy,RecyclerView.Recyclerrecycler,RecyclerView.Statestate){ for(inti=0;i 0){ if(view.getBottom()-dy<0){ Log.d("feifeifei","recycleOut"+i); removeAndRecycleView(view,recycler); } }else{ if(view.getTop()-dy>getHeight()){ Log.d("feifeifei","recycleOut"+i); removeAndRecycleView(view,recycler); } } } } @Override publicvoidsmoothScrollToPosition(RecyclerViewrecyclerView,RecyclerView.Statestate,intposition){ RecyclerView.SmoothScrollersmoothScroller=newCenterSmoothScroller(recyclerView.getContext()); smoothScroller.setTargetPosition(position); startSmoothScroll(smoothScroller); } privateclassCenterSmoothScrollerextendsLinearSmoothScroller{ publicCenterSmoothScroller(Contextcontext){ super(context); } protectedfloatcalculateSpeedPerPixel(DisplayMetricsdisplayMetrics){ return0.2f; } } }
大概就是这么写的网格的需要自己重新写因为计算会有区别,这里简单的讲述一下
正题
Adpter适配器的实现
写法没什么区别唯一的getItemCount和onBindViewHolder要做一下处理大概如下
publicclassAdAuditorAdapterextendsRecyclerView.Adapter{ privateContextmContext; privateList mData; publicAdAuditorAdapter(ContextmContext,List mData){ this.mContext=mContext; this.mData=mData; } @NonNull @Override publicMyViewHolderonCreateViewHolder(@NonNullViewGroupviewGroup,inti){ MyViewHolderholder=newMyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_adauditor,viewGroup,false)); returnholder; } @Override publicvoidonBindViewHolder(@NonNullMyViewHoldermyViewHolder,@SuppressLint("RecyclerView")inti){ Log.e("HHHHHHHHH","onBindViewHolder:-->"+i); //取余否则会出现索引越界 myViewHolder.tv_1.setText(mData.get(i%mData.size())); myViewHolder.itemView.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ listener.onClick(v,i%mData.size(),3); } }); } @Override publicintgetItemCount(){ //返回adpter最大值 returnInteger.MAX_VALUE; } publicvoidupdate(List list){ this.mData=list; notifyDataSetChanged(); } classMyViewHolderextendsRecyclerView.ViewHolder{ TextViewtv_1; publicMyViewHolder(@NonNullViewitemView){ super(itemView); tv_1=itemView.findViewById(R.id.tv_1);//ID } } publicMyClickListenergetListener(){ returnlistener; } publicvoidsetMyClickListener(MyClickListenerlistener){ this.listener=listener; } MyClickListenerlistener; publicinterfaceMyClickListener{ voidonClick(Viewview,intposition,inttype); } publicList getData(){ returnmData; } }
activity的实现
1基本实现
1.1添加假数据写好点击事件
1.2用handler延迟发消息mRecyclerView.smoothScrollToPosition(position);移动到指定位置
1.3点击停止移动
2效果优化
2.1添加匀速阻尼效果
2.2实现无限轮播考虑数值超过Integer最大值情况
2.3点击正在轮播时的recycleview会停止轮播,再次点击才会执行点击事件(优化为点击停止并执行点击事件)
阻尼效果就是减少滑动速率
我们这么做
packagecom.li.liproject.recycle; importandroid.content.Context; importandroid.util.AttributeSet; importandroid.util.DisplayMetrics; importandroid.util.Log; importandroid.view.View; importandroid.view.ViewGroup; importandroidx.recyclerview.widget.GridLayoutManager; importandroidx.recyclerview.widget.LinearSmoothScroller; importandroidx.recyclerview.widget.RecyclerView; /** *@author版本:1.0 *创建日期:2020/4/1414 *描述: */ publicclassScrollSpeedGridLayoutManager1extendsGridLayoutManager{ publicScrollSpeedGridLayoutManager1(Contextcontext,AttributeSetattrs,intdefStyleAttr,intdefStyleRes){ super(context,attrs,defStyleAttr,defStyleRes); } publicScrollSpeedGridLayoutManager1(Contextcontext,intspanCount){ super(context,spanCount); } publicScrollSpeedGridLayoutManager1(Contextcontext,intspanCount,intorientation,booleanreverseLayout){ super(context,spanCount,orientation,reverseLayout); } @Override publicvoidsmoothScrollToPosition(RecyclerViewrecyclerView,RecyclerView.Statestate,intposition){ RecyclerView.SmoothScrollersmoothScroller=newCenterSmoothScroller(recyclerView.getContext()); smoothScroller.setTargetPosition(position); startSmoothScroll(smoothScroller); } privateclassCenterSmoothScrollerextendsLinearSmoothScroller{ publicCenterSmoothScroller(Contextcontext){ super(context); } protectedfloatcalculateSpeedPerPixel(DisplayMetricsdisplayMetrics){ return10f;//滑动速率问题 } } }
activity全部代码
packagecom.li.liproject.recycle; importandroid.annotation.SuppressLint; importandroid.os.Bundle; importandroid.os.Handler; importandroid.os.Message; importandroid.util.Log; importandroid.view.MotionEvent; importandroid.view.View; importandroid.widget.Toast; importandroidx.annotation.NonNull; importandroidx.annotation.Nullable; importandroidx.appcompat.app.AppCompatActivity; importandroidx.recyclerview.widget.GridLayoutManager; importandroidx.recyclerview.widget.LinearLayoutManager; importandroidx.recyclerview.widget.RecyclerView; importcom.li.liproject.MainActivity; importcom.li.liproject.R; importjava.lang.ref.WeakReference; importjava.util.ArrayList; importjava.util.List; /** *@author版本:1.0 *创建日期:2020/4/1414 *描述: */ publicclassRecycleViewActivityextendsAppCompatActivity{ staticRecyclerViewrv_1; privatestaticintHANDLER_MSG=0x0011; privatestaticintHANDLER_LONG_MSG=0x0021; staticintposition=0; staticintaddNum=3; @SuppressLint("HandlerLeak") privatestaticHandlerhandler=newHandler(){ @Override publicvoidhandleMessage(@NonNullMessagemsg){ if(msg.what==HANDLER_MSG){ //9宫格效果实现速率相同 if(addNum==3){ position=position+addNum; addNum=6; }else{ position=position+addNum; addNum=3; } Log.e("TAG","handleMessage:-->"+position); smoothMoveToPosition(rv_1,position>=0?position:0); if(position==Integer.MAX_VALUE/2){ //点击或超过2分之Integer.MAX_VALU重置adpter LongAutoMove(); }else{ AutoMove(); } }elseif(msg.what==HANDLER_LONG_MSG){ position=0; addNum=3; Log.e("TAG","handleMessage:-->"+position); smoothMoveToPosition(rv_1,0); AutoMove(); } } }; privatestaticAdAuditorAdapteradAuditorAdapter; staticListstrings; @Override protectedvoidonCreate(@NullableBundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycle); rv_1=findViewById(R.id.rv_1); strings=newArrayList<>(); for(inti=0;i<50;i++){ strings.add(i+""); } adAuditorAdapter=newAdAuditorAdapter(this,strings); adAuditorAdapter.setMyClickListener(newAdAuditorAdapter.MyClickListener(){ @Override publicvoidonClick(Viewview,intposition,inttype){ Toast.makeText(RecycleViewActivity.this,adAuditorAdapter.getData().get(position),Toast.LENGTH_SHORT).show(); StopMove(); } }); GridLayoutManagerlayoutManager=newScrollSpeedGridLayoutManager1(this,3,GridLayoutManager.HORIZONTAL,false); rv_1.setLayoutManager(layoutManager); rv_1.setAdapter(adAuditorAdapter); rv_1.addOnScrollListener(newRecyclerView.OnScrollListener(){ @Override publicvoidonScrollStateChanged(RecyclerViewrecyclerView,intnewState){ super.onScrollStateChanged(recyclerView,newState); //if(mShouldScroll&&RecyclerView.SCROLL_STATE_IDLE==newState){ //mShouldScroll=false; //smoothMoveToPosition(recyclerView,mToPosition); //} Log.e("TAG","onScrollStateChanged11111111:-->"+newState); if(newState==1){ //RecyclerView.ViewHolderholder=recyclerView.getChildViewHolder(recyclerView.getRootView()); recyclerView.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ Toast.makeText(RecycleViewActivity.this,adAuditorAdapter.getData().get(position)+"........",Toast.LENGTH_SHORT).show(); } }); StopMove(); LongAutoMove(); } } //@Override //publicvoidonScrolled(@NonNullRecyclerViewrecyclerView,intdx,intdy){ //super.onScrolled(recyclerView,dx,dy); //Log.e("TAG","onScrolled:dx="+dx+"dy="+dy); //} }); rv_1.setOnTouchListener(newView.OnTouchListener(){ @Override publicbooleanonTouch(Viewv,MotionEventevent){ if(event.getAction()==MotionEvent.ACTION_DOWN){ //监测点击位置找到view实现点击事件 ViewchildView=rv_1.findChildViewUnder(event.getX(),event.getY()); Log.e("GGGGGGGGGGGGGGGGG","onTouch:-->"+rv_1.getChildLayoutPosition(childView)); adAuditorAdapter.getListener().onClick(v,rv_1.getChildLayoutPosition(childView),1); } returnfalse; } }); AutoMove(); } privatestaticvoidAutoMove(){ handler.removeMessages(HANDLER_MSG); handler.sendEmptyMessageDelayed(HANDLER_MSG,2000); } privatestaticvoidLongAutoMove(){ if(handler.hasMessages(HANDLER_MSG)){ handler.removeMessages(HANDLER_LONG_MSG); } handler.sendEmptyMessageDelayed(HANDLER_LONG_MSG,5000); } publicstaticvoidStopMove(){ if(handler.hasMessages(HANDLER_MSG)){ handler.removeMessages(HANDLER_MSG); } } //目标项是否在最后一个可见项之后 privatestaticbooleanmShouldScroll; //记录目标项位置 privatestaticintmToPosition; /** *滑动到指定位置 */ privatestaticvoidsmoothMoveToPosition(RecyclerViewmRecyclerView,finalintposition){ if(position==0){ mRecyclerView.setAdapter(adAuditorAdapter); } mRecyclerView.smoothScrollToPosition(position); mToPosition=position; mShouldScroll=true; } @Override protectedvoidonDestroy(){ super.onDestroy(); if(handler!=null){ handler.removeCallbacksAndMessages(null); handler=null; } if(adAuditorAdapter!=null){ adAuditorAdapter=null; } } }
自动轮播效果基本实现
这里的Demo只写了大概的效果还有很多的东西需要优化一下,才能拿到项目中使用
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。