一、实现效果图
二、实现方式
主要的实现方式有两种:
第一种是采用Adapter内的getCount()方法返回Integer.MAX_VALUE。 第二种在列表的最前面插入最后一条数据,在列表末尾插入第一个数据,造成循环的假象。
两种方式各有优缺点,第一种方式滑动更流畅,不过试过需要至少 4 个元素才能使用。否则要么报错要么就会有白屏。第二种方法的缺点是第一个和最后一个元素切换效果可能不是太好。
2.1 第一种实现方法Integer.MAX_VALUE
- 简单的布局
```
<?xml version=”1.0” encoding=”utf-8”?>
- 简单的布局
```
<?xml version=”1.0” encoding=”utf-8”?>
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_vp_test_vp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:id="@+id/ll_vp_test_indicator"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="@color/blue_74D3FF"
android:orientation="horizontal"
android:gravity="center"/>
- 2. activity
为了可以向左也能无限滑动,设置初始位置可以在中间,也可以是一个大一点的数字,一般不需要去处理滑到到 0 时的位置,如果要处理的话可以通过监听滚动,在 position = 0,设置新的位置。同理向右滑动到最后一个时,做相同的处理。
class VpTestActivity : BaseActivity(R.layout.activity_vp_test) { override fun initData() {
}
override fun initEvent() {
}
override fun initInterface() {
val dataList = arrayListOf<Fragment>()
for (i in 0..3){
dataList.add(VpTestFg.newInstance(i))
}
val adapter = VpTestAdapter(supportFragmentManager,dataList)
vp_vp_test_vp.adapter = adapter
//初始位置设置到比较大的位置
vp_vp_test_vp.currentItem = dataList.size * 1000
//设置圆点指示器
ll_vp_test_indicator.removeAllViews()
val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
for (i in 0..3){
val image = ImageView(this)
image.setBackgroundResource(R.drawable.circle_white)
ll_vp_test_indicator.addView(image)
//设置间隔
val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
layoutParams.setMargins(dimen,0,dimen,0)
image.layoutParams = layoutParams
}
//设置第一个指示器是红色
ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
//切换指示器
changeIndicator(position)
}
})
}
private fun changeIndicator(position: Int) {
val size = ll_vp_test_indicator.childCount
for (i in 0..size){
ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
}
ll_vp_test_indicator.getChildAt(position%size)?.setBackgroundResource(R.drawable.circle_red)
}
override fun onReload() {
}
}
- 3. adapter
在这里 getCount 返回一个很大的值,这样就可以滑动很久也不会滑到头。在获取 getItem时,不做处理,对 position 的处理要放在instantiateItem里才行,在 getItem 中会报错。
class VpTestAdapter(fragmentManager: FragmentManager, val data: ArrayList
override fun getItem(position: Int): Fragment = data[position]
override fun getCount(): Int = Int.MAX_VALUE
override fun instantiateItem(container: ViewGroup, position: Int): Any {
//处理position。让数组下标落在[0,fragmentList.size)中,防止越界
var position = position
position %= data.size
return super.instantiateItem(container, position)
}
}
- 4. 圆点指示器
只是两个简单的背景,可以是图片,也可以是 drawable 文件,这里用的是简单的文件,如下:<br />白色的圆:
<?xml version=”1.0” encoding=”utf-8”?>
红色的圆:
<?xml version=”1.0” encoding=”utf-8”?>
- 5. 简单的 fragment
布局非常简单,只有中间一个 TextView。
class VpTestFg: Fragment() {
companion object{
fun newInstance(type: Int): VpTestFg{
val bundle = Bundle()
bundle.putInt("type",type)
val fragment = VpTestFg()
fragment.arguments = bundle
return fragment
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fg_vp_test,container,false)
}
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val type = arguments?.getInt("type")
tv_fg_vp_test_text.text = "this is fragment $type"
tv_fg_vp_test_text.setOnClickListener {
//进入另一种方法
startActivity(Intent(context,VpTestTwoActivity::class.java))
}
}
}
- 6. 实现自动轮播
private val mDelayTime: Long = 3000
private val mHandler = @SuppressLint("HandlerLeak")
object : Handler(){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
}
}
override fun run() {
var currentItem = vp_vp_test_vp.currentItem
currentItem ++
if (currentItem == vp_vp_test_vp.childCount - 1){//滑到最后一个
currentItem = 0
vp_vp_test_vp.setCurrentItem(currentItem,false)
mHandler.postDelayed(this,mDelayTime)
}else{
vp_vp_test_vp.setCurrentItem(currentItem,true)
mHandler.postDelayed(this,mDelayTime)
}
}
override fun onResume() {
super.onResume()
mHandler.postDelayed(this,mDelayTime)
}
override fun onPause() {
super.onPause()
mHandler.removeCallbacks(this)
}
- 7. 手动滚动时,停止轮播
vp_vp_test_vp.setOnTouchListener { v, event -> when(event.action){ MotionEvent.ACTION_DOWN -> { LogUtils.e(“action-down”) mHandler.removeCallbacks(this) } MotionEvent.ACTION_UP -> { LogUtils.e(“action-up”) mHandler.postDelayed(this,mDelayTime) } MotionEvent.ACTION_MOVE -> { //LogUtils.e(“action-move”) //mHandler.removeCallbacks(this) } } false }
- 8. 完整的 activity 如下:
class VpTestActivity : BaseActivity(R.layout.activity_vp_test), Runnable {
private val mDelayTime: Long = 3000
private val mHandler = @SuppressLint("HandlerLeak")
object : Handler(){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
}
}
override fun run() {
var currentItem = vp_vp_test_vp.currentItem
currentItem ++
if (currentItem == vp_vp_test_vp.childCount - 1){//滑到最后一个
currentItem = 0
vp_vp_test_vp.setCurrentItem(currentItem,false)
mHandler.postDelayed(this,mDelayTime)
}else{
vp_vp_test_vp.setCurrentItem(currentItem,true)
mHandler.postDelayed(this,mDelayTime)
}
}
override fun onResume() {
super.onResume()
mHandler.postDelayed(this,mDelayTime)
}
override fun onPause() {
super.onPause()
mHandler.removeCallbacks(this)
}
override fun initData() {
}
override fun initEvent() {
}
@SuppressLint("ClickableViewAccessibility")
override fun initInterface() {
val dataList = arrayListOf<Fragment>()
for (i in 0..3){
dataList.add(VpTestFg.newInstance(i))
}
val adapter = VpTestAdapter(supportFragmentManager,dataList)
vp_vp_test_vp.adapter = adapter
//初始位置设置到比较大的位置
vp_vp_test_vp.currentItem = dataList.size * 1000
//设置圆点指示器
ll_vp_test_indicator.removeAllViews()
val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
for (i in 0..3){
val image = ImageView(this)
image.setBackgroundResource(R.drawable.circle_white)
ll_vp_test_indicator.addView(image)
//设置间隔
val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
layoutParams.setMargins(dimen,0,dimen,0)
image.layoutParams = layoutParams
}
//设置第一个指示器是红色
ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
//切换指示器
changeIndicator(position)
}
})
//设置切换动画
vp_vp_test_vp.setPageTransformer(true, DepthPageTransformer())
vp_vp_test_vp.setOnTouchListener { v, event ->
when(event.action){
MotionEvent.ACTION_DOWN -> {
LogUtils.e("action-down")
mHandler.removeCallbacks(this)
}
MotionEvent.ACTION_UP -> {
LogUtils.e("action-up")
mHandler.postDelayed(this,mDelayTime)
}
MotionEvent.ACTION_MOVE -> {
//LogUtils.e("action-move")
//mHandler.removeCallbacks(this)
}
}
false
}
}
private fun changeIndicator(position: Int) {
val size = ll_vp_test_indicator.childCount
for (i in 0..size){
ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
}
ll_vp_test_indicator.getChildAt(position%size)?.setBackgroundResource(R.drawable.circle_red)
}
override fun onReload() {
}
}
<a name="tOwub"></a>
### 2.2 第二种方法
- 1. 布局文件同上
- 2. activity
class VpTestTwoActivity: BaseActivity(R.layout.activity_vp_test) { override fun initData() {
}
override fun initEvent() {
}
lateinit var dataList: ArrayList<Fragment>
var mCurrent2 = 1
override fun initInterface() {
dataList = arrayListOf<Fragment>()
//第一个位置加上最后一个 fragment,最后一个位置加上第一个 fragment
dataList.add(VpTestFg.newInstance(3))
for (i in 0..3){
dataList.add(VpTestFg.newInstance(i))
}
dataList.add(VpTestFg.newInstance(0))
val adapter = VpTestAdapter2(supportFragmentManager,dataList)
vp_vp_test_vp.adapter = adapter
vp_vp_test_vp.currentItem = mCurrent2
//设置圆点指示器
ll_vp_test_indicator.removeAllViews()
val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
for (i in 0..3){
val image = ImageView(this)
image.setBackgroundResource(R.drawable.circle_white)
ll_vp_test_indicator.addView(image)
//设置间隔
val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
layoutParams.setMargins(dimen,0,dimen,0)
image.layoutParams = layoutParams
}
//设置第一个指示器是红色
ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
override fun onPageScrollStateChanged(state: Int) {
//判断是否滑动结束
if (state == ViewPager.SCROLL_STATE_IDLE){
if (mCurrent2 == 0){
vp_vp_test_vp.setCurrentItem(dataList.size - 2, false);//切換,不要動畫效果
}else if (mCurrent2 == dataList.size - 1){
vp_vp_test_vp.setCurrentItem(1, false);//切換,不要動畫效果
}
}
}
@SuppressLint("MissingSuperCall")
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
//这里可以自定义指示器切换动画效果
}
override fun onPageSelected(position: Int) {
mCurrent2 = position
//切换指示器
changeIndicator(position)
}
})
}
private fun changeIndicator(position: Int) {
val size = ll_vp_test_indicator.childCount
for (i in 0..size){
ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
}
when (position) {
0 -> {
ll_vp_test_indicator.getChildAt(size - 1)?.setBackgroundResource(R.drawable.circle_red)
}
dataList.size - 1 -> {
ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
}
else -> {
ll_vp_test_indicator.getChildAt(position - 1)?.setBackgroundResource(R.drawable.circle_red)
}
}
}
override fun onReload() {
}
}
- 3. adapter
class VpTestAdapter2(fragmentManager: FragmentManager, val data: ArrayList
override fun getItem(position: Int): Fragment = data[position]
override fun getCount(): Int = data.size
}
- 4. 指示器和 fragment 同上
- 5. 设置轮播
和上面设置轮播的方法基本类似,不同的地方就是runnable 里有些不同。
override fun run() { var currentItem = vp_vp_test_vp.currentItem currentItem ++ vp_vp_test_vp.currentItem = currentItem mHandler.postDelayed(this,mDelayTime) }
- 6. 完整的 activity 如下
class VpTestTwoActivity: BaseActivity(R.layout.activity_vp_test) ,Runnable{
private val mDelayTime: Long = 3000
private val mHandler = @SuppressLint("HandlerLeak")
object : Handler(){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
}
}
override fun run() {
var currentItem = vp_vp_test_vp.currentItem
currentItem ++
vp_vp_test_vp.currentItem = currentItem
mHandler.postDelayed(this,mDelayTime)
}
override fun onResume() {
super.onResume()
mHandler.postDelayed(this,mDelayTime)
}
override fun onPause() {
super.onPause()
mHandler.removeCallbacks(this)
}
override fun initData() {
}
override fun initEvent() {
}
lateinit var dataList: ArrayList<Fragment>
var mCurrent2 = 1
@SuppressLint("ClickableViewAccessibility")
override fun initInterface() {
dataList = arrayListOf<Fragment>()
//第一个位置加上最后一个 fragment,最后一个位置加上第一个 fragment
dataList.add(VpTestFg.newInstance(3))
for (i in 0..3){
dataList.add(VpTestFg.newInstance(i))
}
dataList.add(VpTestFg.newInstance(0))
val adapter = VpTestAdapter2(supportFragmentManager,dataList)
vp_vp_test_vp.adapter = adapter
vp_vp_test_vp.currentItem = mCurrent2
//设置圆点指示器
ll_vp_test_indicator.removeAllViews()
val dimen = resources.getDimensionPixelOffset(R.dimen.m10)
for (i in 0..3){
val image = ImageView(this)
image.setBackgroundResource(R.drawable.circle_white)
ll_vp_test_indicator.addView(image)
//设置间隔
val layoutParams: LinearLayout.LayoutParams = image.layoutParams as LinearLayout.LayoutParams
layoutParams.setMargins(dimen,0,dimen,0)
image.layoutParams = layoutParams
}
//设置第一个指示器是红色
ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
vp_vp_test_vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
override fun onPageScrollStateChanged(state: Int) {
//判断是否滑动结束
if (state == ViewPager.SCROLL_STATE_IDLE){
if (mCurrent2 == 0){
vp_vp_test_vp.setCurrentItem(dataList.size - 2, false);//切換,不要動畫效果
}else if (mCurrent2 == dataList.size - 1){
vp_vp_test_vp.setCurrentItem(1, false);//切換,不要動畫效果
}
}
}
@SuppressLint("MissingSuperCall")
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
//这里可以自定义指示器切换动画效果
}
override fun onPageSelected(position: Int) {
mCurrent2 = position
//切换指示器
changeIndicator(position)
}
})
//监听,手动滑动时取消轮播
vp_vp_test_vp.setOnTouchListener { v, event ->
when(event.action){
MotionEvent.ACTION_DOWN -> {
LogUtils.e("action-down")
mHandler.removeCallbacks(this)
}
MotionEvent.ACTION_UP -> {
LogUtils.e("action-up")
mHandler.postDelayed(this,mDelayTime)
}
MotionEvent.ACTION_MOVE -> {
//LogUtils.e("action-move")
//mHandler.removeCallbacks(this)
}
}
false
}
}
private fun changeIndicator(position: Int) {
val size = ll_vp_test_indicator.childCount
for (i in 0..size){
ll_vp_test_indicator.getChildAt(i)?.setBackgroundResource(R.drawable.circle_white)
}
when (position) {
0 -> {
ll_vp_test_indicator.getChildAt(size - 1)?.setBackgroundResource(R.drawable.circle_red)
}
dataList.size - 1 -> {
ll_vp_test_indicator.getChildAt(0)?.setBackgroundResource(R.drawable.circle_red)
}
else -> {
ll_vp_test_indicator.getChildAt(position - 1)?.setBackgroundResource(R.drawable.circle_red)
}
}
}
override fun onReload() {
}
三、参考
ViewPager两种方式实现无限轮播
ViewPager自动轮播,手指按住停止轮播
ViewPager结合Fragment进行无限滑动
ViewPager封装轮播效果+指示器 实现一行代码展示轮播图