Android实现带指示点的自动轮播无限循环效果

作者:tmgg 时间:2021-12-09 19:22:44 

想要实现无限轮播,一直向左滑动,当到最后一个view时,会滑动到第一个,无限…

可以自己写ViewPager然后加handler先实现自动滚动,当然这里我为了项目的进度直接使用了Trinea的Android-auto-scroll-view-pager库,网址:点击进入github 引用库compile('cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:1.1.2') {
exclude module: 'support-v4'
之后

1布局为


<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/y150">
<cn.trinea.android.view.autoscrollviewpager.AutoScrollViewPager
android:id="@+id/viewpager1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!--点点的布局-->
<LinearLayout
android:id="@+id/ll_dot1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="8dp"
android:gravity="center"
android:orientation="horizontal" />
</RelativeLayout>

2 构建PagerAdapter
继承自RecyclingPagerAdapter (后面会贴出来源码)


`public class Indicator1Adapter extends RecyclingPagerAdapter {
private List<Integer> imageIdList;
Context context;
//是否循环(创造构造方法,在activity里设置是否)
//集合大小
private int size;

public Indicator1Adapter(List<Integer> mData, Context context) {
 this.imageIdList = mData;
 this.context = context;
 this.size = mData.size();
 isInfiniteLoop = false;
}
@Override
public int getCount() {
//是:最大(让集合的长度无限,从而模拟无限循环) 否,集合长度
 return isInfiniteLoop ? Integer.MAX_VALUE : imageIdList.size();
}

/**
 * @return the isInfiniteLoop
 */
public boolean isInfiniteLoop() {
 return isInfiniteLoop;
}

/**
 * @param是否无限循环
 */
public Indicator1Adapter setInfiniteLoop(boolean isInfiniteLoop) {
 this.isInfiniteLoop = isInfiniteLoop;
 return this;
}

/**
 * 真实的position
 *
 * @param position
 * @return
 */
private int getPosition(int position) {
 return isInfiniteLoop ? position % size : position;
}
@Override
public View getView(int position, View view, ViewGroup container) {
 ViewHolder holder;
 if (view == null) {
  holder = new ViewHolder();
  view = holder.imageView = new ImageView(context);
  view.setTag(holder);
 } else {
  holder = (ViewHolder)view.getTag();
 }
 holder.imageView.setImageResource(imageIdList.get(getPosition(position)));
 holder.imageView.setScaleType(ImageView.ScaleType.FIT_XY);
 return view;
}

private static class ViewHolder {

ImageView imageView;
}
}

3 在activity里或者fragment里就可以设置ViewPager

定义的成员变量:


//viewpager1
@BindView(R.id.viewpager1)
AutoScrollViewPager mPager1;
//承载小点点的控件容器(布局里有)
@BindView(R.id.ll_dot1)
LinearLayout mLlDot1;

Indicator1Adapter adapter1 = new Indicator1Adapter( mData,act).setInfiniteLoop(true);//开启无限循环
 mPager1.setAdapter(adapter1);
 mPager1.setInterval(PLAY_TIME);//轮播时间间隔
 mPager1.startAutoScroll();//开启自动轮播
 mPager1.setCurrentItem(Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % mData.size());

然后你嫌弃官方的换图间隔时间太短,一闪而过,可以通过反射 设置


//通过反射让滚动速度为自己的喜好的(这里设为1.2s)
 try {
  Field field = ViewPager.class.getDeclaredField("mScroller");
  field.setAccessible(true);
  FixedSpeedScroller scroller = new FixedSpeedScroller(mPager1.getContext(),
    new AccelerateInterpolator());
  field.set(mPager1, scroller);
  scroller.setmDuration(1200);
 } catch (Exception e) {
  Log.e(TAG, "Exception", e);
 }

4 然后我们的小点点还没有使用呢
这里我写了方法:


/**
* 设置状态点1
*/
private void setOvalLayout1() {
 for (int i = 0; i < mData.size(); i++) {
 /**
  * 生成对应数量的点点(布局,结果提供)
  */
  mLlDot1.addView(inflater.inflate(R.layout.dot, null));
 }
 // 默认显示第一页
 mLlDot1.getChildAt(0).findViewById(R.id.v_dot)
   .setBackgroundResource(R.drawable.dot_selected);
 mPager1.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  public void onPageSelected(int position) {
   //遍历图片数组
//    Toast.makeText(act, "position"+position, Toast.LENGTH_SHORT).show();
   for (int i = 0; i < mData.size(); i++) {
    if(i==position%mData.size()){
     // 圆点选中
     /**
     * 这里需要注意如果直接写position,由于我们是无限循环,他的position是无限往上
     *增加的,那么就会报空指针,因为我们总共才生成了mData.size()个点点,这里可以让当前的
     *position取余,得到的即是当前位置的点点
     */
     mLlDot1.getChildAt(position%mData.size())
       .findViewById(R.id.v_dot)
       .setBackgroundResource(R.drawable.dot_selected);
    }else{
     // 取消圆点选中
     mLlDot1.getChildAt(curIndex1%mData.size())
       .findViewById(R.id.v_dot)
       .setBackgroundResource(R.drawable.dot_normal);
    }
   }
   curIndex1 = position;
  }

public void onPageScrolled(int arg0, float arg1, int arg2) {
  }

public void onPageScrollStateChanged(int arg0) {
  }
 });

}

别忘了重写


@Override
public void onPause() {
 super.onPause();
 // stop auto scroll when onPause
 mPager1.stopAutoScroll();
}

@Override
public void onResume() {
 super.onResume();
 // start auto scroll when onResume
 mPager1.startAutoScroll();
}

好了,无限循环自动轮播,完成了.

5点点布局:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- 小圆点View -->
<View
 android:id="@+id/v_dot"
 android:layout_width="8dp"
 android:layout_height="8dp"
 android:layout_marginLeft="2dp"
 android:layout_marginRight="2dp"
 android:background="@drawable/dot_normal"/>
</RelativeLayout>

6 点点的background
dot_normal.xml


<?xml version="1.0" encoding="utf-8"?><!-- 圆点未选中 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/background_color" />

<corners android:radius="5dp" />
</shape>

dot_selected.xml


<?xml version="1.0" encoding="utf-8"?><!-- 圆点选中 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/red" />
<corners android:radius="5dp" />
</shape>

RecyclingPagerAdapter的源码依赖RecycleBin类,一并贴出来


public class RecycleBin {
/**
* Views that were on screen at the start of layout. This array is populated at the start of
* layout, and at the end of layout all view in activeViews are moved to scrapViews.
* Views in activeViews represent a contiguous range of Views, with position of the first
* view store in mFirstActivePosition.
*/
private View[] activeViews = new View[0];
private int[] activeViewTypes = new int[0];

/** Unsorted views that can be used by the adapter as a convert view. */
private SparseArray<View>[] scrapViews;

private int viewTypeCount;

private SparseArray<View> currentScrapViews;

public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
 throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//noinspection unchecked
SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount];
for (int i = 0; i < viewTypeCount; i++) {
 scrapViews[i] = new SparseArray<View>();
}
this.viewTypeCount = viewTypeCount;
currentScrapViews = scrapViews[0];
this.scrapViews = scrapViews;
}

protected boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
}

/** @return A view from the ScrapViews collection. These are unordered. */
View getScrapView(int position, int viewType) {
if (viewTypeCount == 1) {
 return retrieveFromScrap(currentScrapViews, position);
} else if (viewType >= 0 && viewType < scrapViews.length) {
 return retrieveFromScrap(scrapViews[viewType], position);
}
return null;
}

/**
* Put a view into the ScrapViews list. These views are unordered.
*
* @param scrap The view to add
*/
void addScrapView(View scrap, int position, int viewType) {
if (viewTypeCount == 1) {
 currentScrapViews.put(position, scrap);
} else {
 scrapViews[viewType].put(position, scrap);
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
 scrap.setAccessibilityDelegate(null);
}
}

/** Move all views remaining in activeViews to scrapViews. */
void scrapActiveViews() {
final View[] activeViews = this.activeViews;
final int[] activeViewTypes = this.activeViewTypes;
final boolean multipleScraps = viewTypeCount > 1;

SparseArray<View> scrapViews = currentScrapViews;
final int count = activeViews.length;
for (int i = count - 1; i >= 0; i--) {
 final View victim = activeViews[i];
 if (victim != null) {
 int whichScrap = activeViewTypes[i];

activeViews[i] = null;
 activeViewTypes[i] = -1;

if (!shouldRecycleViewType(whichScrap)) {
  continue;
 }

if (multipleScraps) {
  scrapViews = this.scrapViews[whichScrap];
 }
 scrapViews.put(i, victim);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  victim.setAccessibilityDelegate(null);
 }
 }
}

pruneScrapViews();
}

/**
* Makes sure that the size of scrapViews does not exceed the size of activeViews.
* (This can happen if an adapter does not recycle its views).
*/
private void pruneScrapViews() {
final int maxViews = activeViews.length;
final int viewTypeCount = this.viewTypeCount;
final SparseArray<View>[] scrapViews = this.scrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
 final SparseArray<View> scrapPile = scrapViews[i];
 int size = scrapPile.size();
 final int extras = size - maxViews;
 size--;
 for (int j = 0; j < extras; j++) {
 scrapPile.remove(scrapPile.keyAt(size--));
 }
}
}

static View retrieveFromScrap(SparseArray<View> scrapViews, int position) {
int size = scrapViews.size();
if (size > 0) {
 // See if we still have a view for this position.
 for (int i = 0; i < size; i++) {
 int fromPosition = scrapViews.keyAt(i);
 View view = scrapViews.get(fromPosition);
 if (fromPosition == position) {
  scrapViews.remove(fromPosition);
  return view;
 }
 }
 int index = size - 1;
 View r = scrapViews.valueAt(index);
 scrapViews.remove(scrapViews.keyAt(index));
 return r;
} else {
 return null;
}
}
}

RecyclingPagerAdapter


public abstract class RecyclingPagerAdapter extends PagerAdapter {
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;

private final RecycleBin recycleBin;

public RecyclingPagerAdapter() {
this(new RecycleBin());
}

RecyclingPagerAdapter(RecycleBin recycleBin) {
this.recycleBin = recycleBin;
recycleBin.setViewTypeCount(getViewTypeCount());
}

@Override public void notifyDataSetChanged() {
recycleBin.scrapActiveViews();
super.notifyDataSetChanged();
}

@Override public final Object instantiateItem(ViewGroup container, int position) {
int viewType = getItemViewType(position);
View view = null;
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
 view = recycleBin.getScrapView(position, viewType);
}
view = getView(position, view, container);
container.addView(view);
return view;
}

@Override public final void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
int viewType = getItemViewType(position);
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
 recycleBin.addScrapView(view, position, viewType);
}
}

@Override public final boolean isViewFromObject(View view, Object object) {
return view == object;
}

/**
* <p>
* Returns the number of types of Views that will be created by
* {@link #getView}. Each type represents a set of views that can be
* converted in {@link #getView}. If the adapter always returns the same
* type of View for all items, this method should return 1.
* </p>
* <p>
* This method will only be called when when the adapter is set on the
* the {@link AdapterView}.
* </p>
*
* @return The number of types of Views that will be created by this adapter
*/
public int getViewTypeCount() {
return 1;
}

/**
* Get the type of View that will be created by {@link #getView} for the specified item.
*
* @param position The position of the item within the adapter's data set whose view type we
*  want.
* @return An integer representing the type of View. Two views should share the same type if one
*   can be converted to the other in {@link #getView}. Note: Integers must be in the
*   range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
*   also be returned.
* @see #IGNORE_ITEM_VIEW_TYPE
*/
@SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses.
public int getItemViewType(int position) {
return 0;
}

/**
* Get a View that displays the data at the specified position in the data set. You can either
* create a View manually or inflate it from an XML layout file. When the View is inflated, the
* parent View (GridView, ListView...) will apply default layout parameters unless you use
* {@link android.view.LayoutInflater#inflate(int, ViewGroup, boolean)}
* to specify a root view and to prevent attachment to the root.
*
* @param position The position of the item within the adapter's data set of the item whose view
*  we want.
* @param convertView The old view to reuse, if possible. Note: You should check that this view
*  is non-null and of an appropriate type before using. If it is not possible to convert
*  this view to display the correct data, this method can create a new view.
*  Heterogeneous lists can specify their number of view types, so that this View is
*  always of the right type (see {@link #getViewTypeCount()} and
*  {@link #getItemViewType(int)}).
* @return A View corresponding to the data at the specified position.
*/
public abstract View getView(int position, View convertView, ViewGroup container);
}
标签:Android,轮播,循环
0
投稿

猜你喜欢

  • Spring Boot中配置定时任务、线程池与多线程池执行的方法

    2021-08-08 13:31:30
  • Java日常练习题,每天进步一点点(10)

    2022-08-11 21:55:53
  • C# 崩溃异常中研究页堆布局的详细过程

    2022-11-06 17:04:08
  • 详解Android Studio如何导入第三方类库、jar包和so库

    2022-01-04 22:27:54
  • Android采取ContentObserver方式自动获取验证码

    2023-07-31 16:20:48
  • C#图像处理之图像均值方差计算的方法

    2023-12-10 17:35:33
  • java创建子类对象设置并调用父类的变量操作

    2023-10-13 18:16:13
  • Android 仿京东、拼多多商品分类页的示例代码

    2023-05-02 13:55:30
  • 深入理解strcpy与memcpy的区别

    2023-02-23 21:45:32
  • java控制台输出图书馆管理系统

    2022-06-13 01:29:29
  • java 分转元与元转分实现操作

    2023-05-13 16:47:22
  • android 封装抓取网页信息的实例代码

    2021-11-28 09:40:32
  • Android 基于agora 开发视频会议的代码

    2021-11-30 02:53:04
  • 实现Android studio设置自动导包及自动导包快捷键

    2022-03-22 00:05:26
  • 详解SpringCloud服务认证(JWT)

    2023-11-14 11:19:43
  • Android WebView支持input file启用相机/选取照片功能

    2023-09-15 07:56:48
  • 一文教你如何使用原生的Feign

    2023-01-12 22:12:58
  • Java聊天室之实现一个服务器与多个客户端通信

    2021-06-03 11:34:45
  • Android11文件管理权限申请详细介绍

    2023-08-28 17:41:47
  • C#绘制中国象棋棋盘

    2021-05-27 15:08:44
  • asp之家 软件编程 m.aspxhome.com