Recycleview實(shí)現(xiàn)無限自動輪播
概述
RecycleView實(shí)現(xiàn)特定數(shù)據(jù)無限重復(fù)滑動在我看來不外乎有兩種方法
1.修改adpter的復(fù)用機(jī)制,無限復(fù)用數(shù)據(jù)
2.在adpter中返回?cái)?shù)據(jù)長度返回Integer的最大值
由于第一種雖然能實(shí)現(xiàn)數(shù)據(jù)的無限重復(fù)但是數(shù)據(jù)位還是沒有任何變化,所以在自動跳轉(zhuǎn)至最后的時(shí)候無法在向下一位輪播,所以在這里我使用第二種方式實(shí)現(xiàn)自動輪播
簡單講述修改adpter的復(fù)用機(jī)制
我們拿LinearLayoutManager線性的為例子,我們只需要重新LinearLayoutManager在繪制的時(shí)候做一些手手腳就可以實(shí)現(xiàn)
package com.li.liproject.recycle; import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.ViewGroup; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearSmoothScroller; import androidx.recyclerview.widget.RecyclerView; /** * @author 版本:1.0 * 創(chuàng)建日期:2020/4/14 14 * 描述: */ public class ScrollSpeedLinearLayoutManger extends LinearLayoutManager { public ScrollSpeedLinearLayoutManger(Context context) { super(context); } public ScrollSpeedLinearLayoutManger(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public ScrollSpeedLinearLayoutManger(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public RecyclerView.LayoutParams generateDefaultLayoutParams() { return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT); } // 1 在RecyclerView初始化時(shí),會被調(diào)用兩次。 // 2 在調(diào)用adapter.notifyDataSetChanged()時(shí),會被調(diào)用。 // 3 在調(diào)用setAdapter替換Adapter時(shí),會被調(diào)用。 // 4 在RecyclerView執(zhí)行動畫時(shí),它也會被調(diào)用。 @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { Log.d("TAG","onLayoutChildren "); if (getItemCount() == 0){ detachAndScrapAttachedViews(recycler); return; } //state.isPreLayout()是支持動畫的 if (getItemCount() == 0 && state.isPreLayout()){ return; } //將當(dāng)前Recycler中的view全部移除并放到報(bào)廢緩存里,之后優(yōu)先重用緩存里的view detachAndScrapAttachedViews(recycler); int actualHeight = 0; for (int i = 0 ;i < getItemCount() ; i++){ View scrap = recycler.getViewForPosition(i); addView(scrap); measureChildWithMargins(scrap,0,0); int width = getDecoratedMeasuredWidth(scrap); int height = getDecoratedMeasuredHeight(scrap); layoutDecorated(scrap,0,actualHeight,width,actualHeight+height); actualHeight+=height; //超出界面的就不畫了,也不add了 if (actualHeight > getHeight()){ break; } } } @Override public boolean canScrollVertically() { return true; } @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { Log.d("feifeifei","getChildCount() " + getChildCount() + " recycler.getScrapList().size() " + recycler.getScrapList().size()); //界面向下滾動的時(shí)候,dy為正,向上滾動的時(shí)候dy為負(fù) //填充 fill(dy,recycler,state); //滾動 offsetChildrenVertical(dy*-1); //回收已經(jīng)離開界面的 recycleOut(dy,recycler,state); return dy; } private void fill(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){ //向下滾動 if (dy > 0){ //先在底部填充 View lastView = getChildAt(getChildCount() -1); int lastPos = getPosition(lastView); if (lastView.getBottom() - dy < getHeight()){ View scrap; if (lastPos == getItemCount() -1){ scrap = recycler.getViewForPosition(0); }else { scrap = recycler.getViewForPosition(lastPos+1); } addView(scrap); measureChildWithMargins(scrap,0,0); int width = getDecoratedMeasuredWidth(scrap); int height = getDecoratedMeasuredHeight(scrap); layoutDecorated(scrap,0,lastView.getBottom(),width,lastView.getBottom()+height); } }else { //向上滾動 //現(xiàn)在頂部填充 View firstView = getChildAt(0); int layoutPostion = getPosition(firstView); if (firstView.getTop() >= 0 ){ View scrap ; if (layoutPostion == 0){ scrap = recycler.getViewForPosition(getItemCount()-1); }else { scrap = recycler.getViewForPosition(layoutPostion -1); } addView(scrap,0); measureChildWithMargins(scrap,0,0); int width = getDecoratedMeasuredWidth(scrap); int height = getDecoratedMeasuredHeight(scrap); layoutDecorated(scrap,0,firstView.getTop() - height,width,firstView.getTop()); } } } private void recycleOut(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){ for (int i = 0 ; i <getChildCount() ;i++){ View view = getChildAt(i); if (dy >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 public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext()); smoothScroller.setTargetPosition(position); startSmoothScroll(smoothScroller); } private class CenterSmoothScroller extends LinearSmoothScroller { public CenterSmoothScroller(Context context) { super(context); } protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return 0.2f; } } }
大概就是這么寫的網(wǎng)格的需要自己重新寫因?yàn)橛?jì)算會有區(qū)別,這里簡單的講述一下
正題
Adpter適配器的實(shí)現(xiàn)
寫法沒什么區(qū)別唯一的getItemCount 和onBindViewHolder要做一下處理大概如下
public class AdAuditorAdapter extends RecyclerView.Adapter<AdAuditorAdapter.MyViewHolder> { private Context mContext; private List<String> mData; public AdAuditorAdapter(Context mContext, List<String> mData) { this.mContext = mContext; this.mData = mData; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { MyViewHolder holder = new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_adauditor, viewGroup, false)); return holder; } @Override public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, @SuppressLint("RecyclerView") int i) { Log.e("HHHHHHHHH", "onBindViewHolder: -->"+i ); //取余否則會出現(xiàn)索引越界 myViewHolder.tv_1.setText(mData.get(i%mData.size())); myViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { listener.onClick(v, i%mData.size(), 3); } }); } @Override public int getItemCount() { //返回adpter最大值 return Integer.MAX_VALUE; } public void update(List<String> list) { this.mData = list; notifyDataSetChanged(); } class MyViewHolder extends RecyclerView.ViewHolder { TextView tv_1; public MyViewHolder(@NonNull View itemView) { super(itemView); tv_1 = itemView.findViewById(R.id.tv_1); //ID } } public MyClickListener getListener() { return listener; } public void setMyClickListener(MyClickListener listener) { this.listener = listener; } MyClickListener listener; public interface MyClickListener { void onClick(View view, int position, int type); } public List<String> getData() { return mData; } }
activity的實(shí)現(xiàn)
1基本實(shí)現(xiàn)
1.1 添加假數(shù)據(jù)寫好點(diǎn)擊事件
1.2 用handler延遲發(fā)消息 mRecyclerView.smoothScrollToPosition(position);移動到指定位置
1.3 點(diǎn)擊停止移動
2效果優(yōu)化
2.1 添加勻速阻尼效果
2.2 實(shí)現(xiàn)無限輪播考慮數(shù)值超過Integer最大值情況
2.3 點(diǎn)擊正在輪播時(shí)的recycleview會停止輪播,再次點(diǎn)擊才會執(zhí)行點(diǎn)擊事件(優(yōu)化為點(diǎn)擊停止并執(zhí)行點(diǎn)擊事件)
阻尼效果就是減少滑動速率
我們這么做
package com.li.liproject.recycle; import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.ViewGroup; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearSmoothScroller; import androidx.recyclerview.widget.RecyclerView; /** * @author 版本:1.0 * 創(chuàng)建日期:2020/4/14 14 * 描述: */ public class ScrollSpeedGridLayoutManager1 extends GridLayoutManager { public ScrollSpeedGridLayoutManager1(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public ScrollSpeedGridLayoutManager1(Context context, int spanCount) { super(context, spanCount); } public ScrollSpeedGridLayoutManager1(Context context, int spanCount, int orientation, boolean reverseLayout) { super(context, spanCount, orientation, reverseLayout); } @Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext()); smoothScroller.setTargetPosition(position); startSmoothScroll(smoothScroller); } private class CenterSmoothScroller extends LinearSmoothScroller { public CenterSmoothScroller(Context context) { super(context); } protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return 10f;//滑動速率問題 } } }
activity全部代碼
package com.li.liproject.recycle; import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.li.liproject.MainActivity; import com.li.liproject.R; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * @author 版本:1.0 * 創(chuàng)建日期:2020/4/14 14 * 描述: */ public class RecycleViewActivity extends AppCompatActivity { static RecyclerView rv_1; private static int HANDLER_MSG = 0x0011; private static int HANDLER_LONG_MSG = 0x0021; static int position = 0; static int addNum = 3; @SuppressLint("HandlerLeak") private static Handler handler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { if (msg.what == HANDLER_MSG) { // 9宮格效果實(shí)現(xiàn)速率相同 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){ //點(diǎn)擊或超過2分之Integer.MAX_VALU重置adpter LongAutoMove(); }else { AutoMove(); } }else if (msg.what==HANDLER_LONG_MSG){ position = 0; addNum = 3; Log.e("TAG", "handleMessage: -->" + position); smoothMoveToPosition(rv_1, 0); AutoMove(); } } }; private static AdAuditorAdapter adAuditorAdapter; static List<String> strings; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycle); rv_1 = findViewById(R.id.rv_1); strings = new ArrayList<>(); for (int i = 0; i < 50; i++) { strings.add(i + ""); } adAuditorAdapter = new AdAuditorAdapter(this, strings); adAuditorAdapter.setMyClickListener(new AdAuditorAdapter.MyClickListener() { @Override public void onClick(View view, int position, int type) { Toast.makeText(RecycleViewActivity.this, adAuditorAdapter.getData().get(position), Toast.LENGTH_SHORT).show(); StopMove(); } }); GridLayoutManager layoutManager = new ScrollSpeedGridLayoutManager1(this,3,GridLayoutManager.HORIZONTAL, false); rv_1.setLayoutManager(layoutManager); rv_1.setAdapter(adAuditorAdapter); rv_1.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 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.ViewHolder holder = recyclerView.getChildViewHolder(recyclerView.getRootView()); recyclerView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(RecycleViewActivity.this, adAuditorAdapter.getData().get(position)+"........", Toast.LENGTH_SHORT).show(); } }); StopMove(); LongAutoMove(); } } // @Override // public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { // super.onScrolled(recyclerView, dx, dy); // Log.e("TAG", "onScrolled: dx=" +dx +" dy="+dy ); // } }); rv_1.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction()==MotionEvent.ACTION_DOWN){ //監(jiān)測點(diǎn)擊位置找到view實(shí)現(xiàn)點(diǎn)擊事件 View childView = rv_1.findChildViewUnder(event.getX(), event.getY()); Log.e("GGGGGGGGGGGGGGGGG", "onTouch: -->"+rv_1.getChildLayoutPosition(childView)); adAuditorAdapter.getListener().onClick(v, rv_1.getChildLayoutPosition(childView),1); } return false; } }); AutoMove(); } private static void AutoMove() { handler.removeMessages(HANDLER_MSG); handler.sendEmptyMessageDelayed(HANDLER_MSG, 2000); } private static void LongAutoMove() { if (handler.hasMessages(HANDLER_MSG)) { handler.removeMessages(HANDLER_LONG_MSG); } handler.sendEmptyMessageDelayed(HANDLER_LONG_MSG, 5000); } public static void StopMove() { if (handler.hasMessages(HANDLER_MSG)) { handler.removeMessages(HANDLER_MSG); } } //目標(biāo)項(xiàng)是否在最后一個(gè)可見項(xiàng)之后 private static boolean mShouldScroll; //記錄目標(biāo)項(xiàng)位置 private static int mToPosition; /** * 滑動到指定位置 */ private static void smoothMoveToPosition(RecyclerView mRecyclerView, final int position) { if (position==0){ mRecyclerView.setAdapter(adAuditorAdapter); } mRecyclerView.smoothScrollToPosition(position); mToPosition = position; mShouldScroll = true; } @Override protected void onDestroy() { super.onDestroy(); if (handler!=null){ handler.removeCallbacksAndMessages(null); handler= null; } if (adAuditorAdapter!=null) { adAuditorAdapter= null; } } }
自動輪播效果基本實(shí)現(xiàn)
這里的Demo只寫了大概的效果還有很多的東西需要優(yōu)化一下,才能拿到項(xiàng)目中使用
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Android中的沉浸式狀態(tài)欄效果實(shí)例
本篇文章主要介紹了Android中的沉浸式狀態(tài)欄效果,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12Android Compose衰減動畫Animatable使用詳解
這篇文章主要為大家介紹了Android Compose衰減動畫Animatable使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11詳解如何在Android studio中更新sdk版本和build-tools版本
這篇文章主要介紹了如何在Android studio中更新sdk版本和build-tools版本,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Android定時(shí)器Timer的停止和重啟實(shí)現(xiàn)代碼
本篇文章主要介紹了Android實(shí)現(xiàn)定時(shí)器Timer的停止和重啟實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Android開發(fā)中Google為什么不讓用Handler的runWithScissors()
這篇文章主要介紹了Android開發(fā)中Google為什么不讓用Handler的runWithScissors(),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Android自定義水波紋動畫Layout實(shí)例代碼
這篇文章主要介紹了Android自定義水波紋動畫Layout的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11Android 自定義縮短Toast顯示時(shí)間的實(shí)例代碼
這篇文章主要介紹了Android 自定義縮短Toast顯示時(shí)間,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01Android編程設(shè)計(jì)模式之抽象工廠模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之抽象工廠模式,結(jié)合實(shí)例形式詳細(xì)分析了Android抽象工廠模式的概念、原理、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-12-12