Recycleview實現(xiàn)無限自動輪播
概述
RecycleView實現(xiàn)特定數(shù)據(jù)無限重復(fù)滑動在我看來不外乎有兩種方法
1.修改adpter的復(fù)用機制,無限復(fù)用數(shù)據(jù)
2.在adpter中返回數(shù)據(jù)長度返回Integer的最大值
由于第一種雖然能實現(xiàn)數(shù)據(jù)的無限重復(fù)但是數(shù)據(jù)位還是沒有任何變化,所以在自動跳轉(zhuǎn)至最后的時候無法在向下一位輪播,所以在這里我使用第二種方式實現(xiàn)自動輪播
簡單講述修改adpter的復(fù)用機制
我們拿LinearLayoutManager線性的為例子,我們只需要重新LinearLayoutManager在繪制的時候做一些手手腳就可以實現(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初始化時,會被調(diào)用兩次。 // 2 在調(diào)用adapter.notifyDataSetChanged()時,會被調(diào)用。 // 3 在調(diào)用setAdapter替換Adapter時,會被調(diào)用。 // 4 在RecyclerView執(zhí)行動畫時,它也會被調(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全部移除并放到報廢緩存里,之后優(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()); //界面向下滾動的時候,dy為正,向上滾動的時候dy為負 //填充 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)格的需要自己重新寫因為計算會有區(qū)別,這里簡單的講述一下
正題
Adpter適配器的實現(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的實現(xiàn)
1基本實現(xiàn)
1.1 添加假數(shù)據(jù)寫好點擊事件
1.2 用handler延遲發(fā)消息 mRecyclerView.smoothScrollToPosition(position);移動到指定位置
1.3 點擊停止移動
2效果優(yōu)化
2.1 添加勻速阻尼效果
2.2 實現(xiàn)無限輪播考慮數(shù)值超過Integer最大值情況
2.3 點擊正在輪播時的recycleview會停止輪播,再次點擊才會執(zhí)行點擊事件(優(yōu)化為點擊停止并執(zhí)行點擊事件)
阻尼效果就是減少滑動速率
我們這么做
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宮格效果實現(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){ //點擊或超過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)測點擊位置找到view實現(xià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)項是否在最后一個可見項之后 private static boolean mShouldScroll; //記錄目標(biāo)項位置 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; } } }
自動輪播效果基本實現(xiàn)
這里的Demo只寫了大概的效果還有很多的東西需要優(yōu)化一下,才能拿到項目中使用
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android Compose衰減動畫Animatable使用詳解
這篇文章主要為大家介紹了Android Compose衰減動畫Animatable使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11詳解如何在Android studio中更新sdk版本和build-tools版本
這篇文章主要介紹了如何在Android studio中更新sdk版本和build-tools版本,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Android定時器Timer的停止和重啟實現(xiàn)代碼
本篇文章主要介紹了Android實現(xiàn)定時器Timer的停止和重啟實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08Android開發(fā)中Google為什么不讓用Handler的runWithScissors()
這篇文章主要介紹了Android開發(fā)中Google為什么不讓用Handler的runWithScissors(),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09