Recycleview实现无限自动轮播

概述

RecycleView实现特定数据无限重复滑动在我看来不外乎有两种方法

1.修改adpter的复用机制,无限复用数据
2.在adpter中返回数据长度返回Integer的最大值

由于第一种虽然能实现数据的无限重复但是数据位还是没有任何变化,所以在自动跳转至最后的时候无法在向下一位轮播,所以在这里我使用第二种方式实现自动轮播

简单讲述修改adpter的复用机制

我们拿LinearLayoutManager线性的为例子,我们只需要重新LinearLayoutManager在绘制的时候做一些手手脚就可以实现

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
 * 创建日期: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初始化时,会被调用两次。
// 2 在调用adapter.notifyDataSetChanged()时,会被调用。
// 3 在调用setAdapter替换Adapter时,会被调用。
// 4 在RecyclerView执行动画时,它也会被调用。
 @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;
  }
  //将当前Recycler中的view全部移除并放到报废缓存里,之后优先重用缓存里的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);

  //回收已经离开界面的
  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 {
   //向上滚动
   //现在顶部填充
   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;
  }
 }
}

大概就是这么写的网格的需要自己重新写因为计算会有区别,这里简单的讲述一下

正题

Adpter适配器的实现

写法没什么区别唯一的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 );
  //取余否则会出现索引越界
  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的实现

1基本实现

1.1 添加假数据写好点击事件
1.2 用handler延迟发消息 mRecyclerView.smoothScrollToPosition(position);移动到指定位置
1.3 点击停止移动

2效果优化

2.1 添加匀速阻尼效果
2.2 实现无限轮播考虑数值超过Integer最大值情况
2.3 点击正在轮播时的recycleview会停止轮播,再次点击才会执行点击事件(优化为点击停止并执行点击事件)
阻尼效果就是减少滑动速率

我们这么做

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
 * 创建日期: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
 * 创建日期: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宫格效果实现速率相同
    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){
     //监测点击位置找到view实现点击事件
     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);
  }
 }

 //目标项是否在最后一个可见项之后
 private static boolean mShouldScroll;
 //记录目标项位置
 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;
  }
 }
}

自动轮播效果基本实现

这里的Demo只写了大概的效果还有很多的东西需要优化一下,才能拿到项目中使用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2020-07-21

Android实现图片文字轮播特效

本文实例讲解了Android实现图片文字轮播特效的详细代码,分享给大家供大家参考,具体内容如下 图片轮播是类似知乎日报上的一个轮播效果,如下图. 好了直接进入正题,首先是出示一下效果: MainActivity: import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService;

Android实现炫酷轮播图效果

轮播图的实现有很多种方式,早先我在网上看了下别人写的轮播图,感觉都比较的墨守成规,有的还有可能加载不了网络图片.所以我在这里自己重新写了下轮播图 ,方便日后的项目使用. 在下面的代码中,我也用volley封装了网络请求框架,异步加载网络图片,也可以给大家参考,非常实用. 效果图:这只是其中的一种效果 底层封装的我在下面会一一展示,先看下在MainActivity中怎样调取这个轮播控件 package com.wujie.advertisment.activity; import android.

Android实现图片轮播效果的两种方法

大家在使用APP的过程中,经常会看到上部banner图片轮播的效果,那么今天我们就一起来学习一下,android中图片轮询的几种实现方法: 第一种:使用动画的方法实现:(代码繁琐) 这种发放需要:两个动画效果,一个布局,一个主类来实现,不多说了,来看代码吧: public class IamgeTrActivity extends Activity { /** Called when the activity is first created. */ public ImageView image

Android使用ViewPager加载图片和轮播视频

作为Android基础组件之一,大家对viewpager已经很熟悉了,网上也有很多使用viewpager来加载图片的案例.但是像微信那样点击图片,可以轮播显示图片和视频的例子却没找到.正巧项目中有需求,可以就花时间写了下,现在给一下核心代码,希望对有此需求的同学们起一个抛砖引玉的作用.话不多说了,上代码: 以下是initData的代码 public void initData() { //把聊天界面的图片和视频找出来,并加到数组中,并在 //并根据传进来的position来找到视频或图片在数组中

详解android 视频图片混合轮播实现

循环添加视频view  图片view for (int i = 0 ;i<beansArrayList.size();i++){ if (beansArrayList.get(i).getType()==1){ videoPlayer = new NiceVideoPlayer(this); controller = new TxVideoPlayerController(this); videoPlayer.setController(controller); videoPlayer.setU

Android 使用ViewPager自动滚动循环轮播效果

对Android 利用ViewPager实现图片可以左右循环滑动效果,感兴趣的朋友可以直接点击查看内容详情. 主要介绍如何实现ViewPager自动播放,循环滚动的效果及使用.顺便解决ViewPager嵌套(ViewPager inside ViewPager)影响触摸滑动及ViewPager滑动速度设置问题. 先给大家展示下效果图,喜欢的朋友可以下载源码: 1.实现 没有通过ScheduledExecutorService或Timer定期执行某个任务实现,而是简单的通过handler发送消息去

Android实现图片轮播效果

本文实例讲述了JaAndroid实现图片轮播效果代码,分享给大家供大家参考.具体如下: 运行效果截图如下: 具体代码如下: 首先看下一下布局文件: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_par

Android实现图片自动轮播并且支持手势左右无限滑动

废话不多说了,先给大家上左右无限滑动的代码了. 1.左右无限滑动 public class MainActivity extends AppCompatActivity { private static ViewPager viewPager; private RadioGroup group; //图片资源,实际项目需要从网络获取 private int[] imageIds = {R.drawable.ym1, R.drawable.ym2, R.drawable.ym3, R.drawab

Android自动播放Banner图片轮播效果

先看一下效果图 支持本地图片以及网络图片or本地网络混合. 使用方式: <com.jalen.autobanner.BannerView android:id="@+id/banner" android:layout_width="match_parent" android:layout_height="230dip"> </com.jalen.autobanner.BannerView> 核心代码: int length

Android实现Banner界面广告图片循环轮播(包括实现手动滑动循环)

前言:经常会看到有一些app的banner界面可以实现循环播放多个广告图片和手动滑动循环.本以为单纯的ViewPager就可以实现这些功能.但是蛋疼的事情来了,ViewPager并不支持循环翻页.所以要实现循环还得需要自己去动手.自己在网上也找了些例子,本博文的Demo是结合自己找到的一些相关例子的基础上去改造,也希望对读者有用. Demo实现的效果图如下: Demo代码: 工程目录如下图: 废话不多说,上代码. 1.主Activity代码如下: package com.stevenhu.and

Android中使用imageviewswitcher 实现图片切换轮播导航的方法

前面写过了使用ViewFlipper和ViewPager实现屏幕中视图切换的效果(ViewPager未实现轮播)附链接: ANDROID中使用VIEWFLIPPER类实现屏幕切换(关于坐标轴的问题已补充更改) Android 中使用 ViewPager实现屏幕页面切换和页面轮播效果 今天我们在换一种实现方式ImageViewSwitcher. ImageSwitcher是Android中控制图片展示效果的一个控件,如:幻灯片效果 ImageSwitcher粗略的理解就是ImageView的选择器

Android ViewPager实现无限循环轮播广告位Banner效果

现在一些app通常会在头部放一个广告位,底部放置一行小圆圈指示器,指示广告位当前的页码,轮播展示一些图片,这些图片来自于网络.这个广告位banner是典型的android ViewPager实现,但是如果自己实现这样的ViewPager,要解决一系列琐碎的问题,比如: (1)这个广告位ViewPager要支持无限循环轮播,例如,有3张图片,A,B,C,当用户滑到最后C时候再滑就要滑到A,反之亦然. (2)ViewPager要实现自动播放,比如每个若干秒如2秒,自动切换播放到下一张图片. (3)通

Android仿京东淘宝自动无限循环轮播控件思路详解

在App的开发中,很多的时候都需要实现类似京东淘宝一样的自动无限轮播的广告栏,所以就自己写了一个,下面是我自定义控件的思路和过程. 一.自定义控件属性 新建自定义控件SliderLayout继承于RelativeLayout,首先要考虑的就是自定义的控件需要扩展那些属性,把这些属性列出来.在这里是要实现类似于京东淘宝的无限轮播广告栏,那么首先想到的就是轮播的时长.轮播指示器的样式等等.我在这里列举了一些并且结合到了代码中. 1.扩展属性 (1)是否开启自动轮播的功能. (2)指示器的图形样式,一

Android Viewpager实现无限循环轮播图

在网上找了很多viewpager实现图片轮播的,但是大多数通过以下方式在PagerAdapter的getCount()返回一个无限大的数,来实现 伪无限 @Override public int getCount() { return Integer.MAX_VALUE;//返回一个无限大的值,可以 无限循环 } 虽然通过这种方式是能达到效果,但是从严格意义上来说并不是真正的无限. 假如有五张轮播图 item的编号为(0,1,2,3,4) 要想实现 无限循环  我们在这五张的头部和尾部各加一张即

Android图片无限轮播的实现代码

本文实例为大家分享了AnAndroid图片无限轮播的具体代码,供大家参考,具体内容如下 public class MainActivity extends Activity { private ViewPager viewPager; private LinearLayout ll_dot; private String[] imageUrls = new String[] { "http://pic8.nipic.com/20100701/5290458_114840036316_2.jpg&