目标
目标是实现一个简单的轮播图,特征如下:
- 只展示本地图片
- 可以无限轮播,在第一帧时也可以向前轮播
- 可以自动轮播
code
先上代码,需要事先准备几张本地图片当素材
MainActivity:
package com.example.loopapplication;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import com.example.loopapplication.looper.LooperPagerAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
private ViewPager mViewPager;
private LooperPagerAdapter mLooperPagerAdapter;
private List<Integer> mPictures = new ArrayList<>();
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化数据
mPictures.add(R.mipmap.pic1);
mPictures.add(R.mipmap.pic2);
mPictures.add(R.mipmap.pic3);
mPictures.add(R.mipmap.pic4);
mPictures.add(R.mipmap.pic5);
mPictures.add(R.mipmap.pic6);
// 初始化视图
initView();
}
private void initView() {
// 找到控件
mViewPager = this.findViewById(R.id.loop_pager);
// 创建适配器
mLooperPagerAdapter = new LooperPagerAdapter();
// 控件设置适配器
mViewPager.setAdapter(mLooperPagerAdapter);
// 适配器设置数据
mLooperPagerAdapter.setData(mPictures);
// 设置数据之后要通知轮播图数据已经更新
mLooperPagerAdapter.notifyDataSetChanged();
// 将一开始的轮播组件序号设置为较大值,可以做到在第一帧向前滑动
mViewPager.setCurrentItem(mLooperPagerAdapter.getDataSize() * 100);
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
// 展示到屏幕上时,开始自动轮播
mHandler.post(mLoopTask);
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
// 不展示在屏幕上时,比如切后台,关闭自动轮播
mHandler.removeCallbacks(mLoopTask);
}
private Runnable mLoopTask = new Runnable() {
@Override
public void run() {
int currentItems = mViewPager.getCurrentItem();
// 将当前组件序号自增,达到自动轮播的效果
mViewPager.setCurrentItem(++currentItems);
// 自动轮播间隔1秒
mHandler.postDelayed(this, 1000);
}
};
}
LooperPagerAdapter
package com.example.loopapplication.looper;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import java.util.List;
public class LooperPagerAdapter extends PagerAdapter {
private List<Integer> mPictures = null;
/**
* 获取轮播组件的个数,返回多少就可以滑动多少次
*
* @return
*/
@Override
public int getCount() {
if (mPictures != null) {
// 设置为整型的最大值 近似于无限滑动
return Integer.MAX_VALUE;
}
return 0;
}
/**
* 判断当前轮播组件是否是视图
*
* @param view Page View to check for association with <code>object</code>
* @param object Object to check for association with <code>view</code>
* @return
*/
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
/**
* 初始化轮播组件
*
* @param container The containing View in which the page will be shown.
* @param position The page position to be instantiated.
* @return
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
// 需要将当前的位置取余 得到数据数组里真实的位置
int realPosition = position % mPictures.size();
ImageView imageView = new ImageView(container.getContext());
imageView.setImageResource(mPictures.get(realPosition));
container.addView(imageView);
return imageView;
}
/**
* 销毁轮播组件
*
* @param container The containing View from which the page will be removed.
* @param position The page position to be removed.
* @param object The same object that was returned by
* {@link #instantiateItem(View, int)}.
*/
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
/**
* 业务层设置轮播图数据
*
* @param pictures
*/
public void setData(List<Integer> pictures) {
this.mPictures = pictures;
}
/**
* 获取轮播图数据数量
* @return
*/
public int getDataSize() {
if (mPictures != null) {
return mPictures.size();
}
return 0;
}
}
视图xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager.widget.ViewPager
android:id="@+id/loop_pager"
android:layout_width="match_parent"
android:layout_height="200dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="轮播图!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
基本的轮播图实现不加赘述,这里仅记录下如何实现轮播图的一些功能
无限轮播
想要做到无限轮播,说明这个轮播图要有无限的轮播组件,这样才能一直轮播下去。
@Override
public int getCount() {
if (mPictures != null) {
// 设置为整型的最大值 近似于无限滑动
return Integer.MAX_VALUE;
}
return 0;
}
改写getCount方法,返回的数值就是轮播图的组件数量,我们设置为int的最大值,视作无限多。
这样会带来一个问题,我们的数据数组只有6个,当轮播超过6次之后怎么绑定正确的数据呢?需要在instantiateItem里做处理,通过取余操作获取数据在数组里的真实位置。
/**
* 初始化轮播组件
*
* @param container The containing View in which the page will be shown.
* @param position The page position to be instantiated.
* @return
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
// 需要将当前的位置取余 得到数据数组里真实的位置
int realPosition = position % mPictures.size();
ImageView imageView = new ImageView(container.getContext());
imageView.setImageResource(mPictures.get(realPosition));
container.addView(imageView);
return imageView;
}
另外还有一个问题,就是我们希望轮播图一开始可以往左滑动,而不是只能向右滑动,这样可以给用户一种无限循环的感觉。
因此在视图初始化时,应该手动给轮播图当前的组件设定一个足够大的值,使得用户可以不断地向左滑。
// 将一开始的轮播组件序号设置为较大值,可以做到在第一帧向前滑动
mViewPager.setCurrentItem(mLooperPagerAdapter.getDataSize() * 100);
需要注意,设定的值需要是数据数组长度的倍数,这样才能定位到第一个轮播组件。
自动轮播
自动定时轮播可以通过Handler的延时方式实现,首先要明确,在用户不可见的情况要停止自动轮播,因此要在上屏/移出屏幕时做处理。
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
// 展示到屏幕上时,开始自动轮播
mHandler.post(mLoopTask);
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
// 不展示在屏幕上时,比如切后台,关闭自动轮播
mHandler.removeCallbacks(mLoopTask);
}
在每个时间间隔自增轮播图的组件号,就可以实现自动轮播了。
private Runnable mLoopTask = new Runnable() {
@Override
public void run() {
int currentItems = mViewPager.getCurrentItem();
// 将当前组件序号自增,达到自动轮播的效果
mViewPager.setCurrentItem(++currentItems);
// 自动轮播间隔1秒
mHandler.postDelayed(this, 1000);
}
};