ViewPager 实现自动循环轮播 高度自适应 显示前后部分界面 点击事件
来源:互联网 发布:唯一网络怎么样 编辑:程序博客网 时间:2024/06/03 23:44
游民星空 3.0 界面大改之后,发现首页的轮播图很有特色,一直想着实现一下。先看一下原 app 的效果:
其实是可以自动轮播的,不过等的时间太长,我就动手帮了一把。
要实现这种效果无非需要考虑到以下几个问题:
1. ViewPager 可以显示前后的一部分界面;
2. 要在不同分辨率的手机上保持图片的长宽比例;
3. 实现自动循环轮播;
4. 注意 Activity 的生命周期和手指对 ViewPager 操作时对自动播放的影响;
5. 页面的点击事件的处理。
那么我们就来一步一步解决这些问题:
1.实现 ViewPager 显示前后部分界面与高度自适应
直接上 xml 文件的代码:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ayuhani.viewpagerdemo.MainActivity"> <LinearLayout android:id="@+id/ll_parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipChildren="false" android:orientation="vertical"> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipChildren="false"></android.support.v4.view.ViewPager> </LinearLayout></LinearLayout>
在 ViewPager 和它的父布局我们都添加了一行android:clipChildren="false"
,这句代码的意思是:是否限制子 View 在其范围内显示,默认为 true,是限制的,我们这里改成 false。然后在代码中放上五张图片,这里怎么自适应高度呢?
图片占用屏幕的高度 / 占用屏幕的宽度 = 原始图片高度 / 图片宽度
根据这个公式,我们就可以计算出 ViewPager 所需要的高度了。
DisplayMetrics metrics = getResources().getDisplayMetrics();ViewGroup.LayoutParams params = viewPager.getLayoutParams();params.width = (int) (metrics.widthPixels * 0.86); // 宽度设置成屏幕宽度的86%,这里根据自己喜好设置params.height = params.width * 240 / 386; // 利用已知图片的宽高比计算高度viewPager.setLayoutParams(params);
由于我们还需要展示前后的部分界面,所以不能完全占据屏幕宽度,再根据公式计算出高度,设置给 ViewPager 就好了。我们看一下效果:
等一下,这和我们想要的结果完全不一样啊!
这里发现了两个比较严重的问题:
1.图片不居中显示,B 区域不显示,只显示 C 区域
关于这个问题我首先想到的是大概由于 gravity 的原因,于是我在 ViewPager 的属性里面加上android:layout_gravity="center_horizontal"
或者在它的父布局中加上android:gravity="center_horizontal"
都可以解决这个问题。
2.手指放在 C 区域滑动时,无法滑到下一页,只有在 A 处可以正常滑动
这个问题我们想一下也可以知道,由于限制了 ViewPager 的宽度,所以 C 区域已经不属于 ViewPager 的当前界面,所以滑动是没有效果的,我们只需要重写父布局的OnTouchListener
方法就好了:
// 这里处理触摸两端滑动无效果的问题parentLayout = (LinearLayout) findViewById(R.id.ll_parent);parentLayout.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { return viewPager.dispatchTouchEvent(motionEvent); }});
注意:给父布局重写这个方法之后,在父布局的任意位置滑动都能带动 ViewPager 的滑动,所以要给 ViewPager 单独包一个父布局。
这两个问题解决后,已经可以正常显示 B 区域了,并且滑动 B 和 C 区域也有了效果,但从游民的效果可以看到 A B C 之间都是有一小块间隙的,这个很简单:
viewPager.setPageMargin(20);
这个方法用来设置 ViewPager 页面之间的间距,单位是 px。现在再来看一下效果,是不是已经基本成型了?
2.实现循环功能
从网上找了查了一些资料,发现大多数实现这个功能用的都是一种“障眼法”:
我们当前只有 5 幅图片,但是我们在集合的首尾再增加两个元素,第一个显示图片 5,最后一个显示图片 1。当我们向后滑动到页面 6 的时候,通过viewPager.setCurrentItem(1)
方法跳转到页面 1;而向前滑动到页面 0 的时候,通过viewPager.setCurrentItem(5)
方法跳转到页面 5。这样就给人一种无限循环滑动的感觉。当然初始化的时候,我们要先设置viewPager.setCurrentItem(1)
来显示第一张图片。
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { if (position == 0) { handler.postDelayed(new Runnable() { @Override public void run() { viewPager.setCurrentItem(adapter.getCount() - 2, false); } }, 250); } else if (position == adapter.getCount() - 1) { handler.postDelayed(new Runnable() { @Override public void run() { viewPager.setCurrentItem(1, false); } }, 250); } } @Override public void onPageScrollStateChanged(int state) { } });
该方法的第二个参数是一个布尔类型,代表是否要平滑地滚动到指定的位置,如果 true 的话,会有一段动画的效果,具体可以自己实现看一下。这里写 false,代表立刻跳转。但是onPageSelected()
这个方法在下个页面还没有完全显示完成的时候就执行了,所以给人一种很突兀的感觉,我在这里写了 250 毫秒的延迟。
注意:这种方法在 ViewPager 的页面完全显示的时候是没有问题的,但是我们要显示前后的部分,所以给人的视觉效果不是很好。比如说当前滑动到了页面 6,这是时候是要跳转到页面 1 的,但由于我们写了 250 毫秒的延迟,导致图片 2 也被延迟加载,所以显示页面 6 的时候会有短暂的时间右边显示一小部分空白,然后才加载出图片 2。目前想到的办法就是在页面 6 之后再放一个页面 7 用来显示图片 2,这样能暂时解决问题,但是又进一步消耗了资源,不是太好的处理办法。
3.实现自动播放
利用viewPager.setCurrentItem(viewPager.getCurrentItem() + 1)
方法与定时功能实现。
private void autoPlay() { handler.postDelayed(runnable, 2000); }class PlayRunnable implements Runnable { @Override public void run() { viewPager.setCurrentItem(viewPager.getCurrentItem() + 1); autoPlay(); }}
只需要调用autoPlay()
方法,便可以自动轮播了。
4.生命周期的管理与触摸管理
为了减少内存的开销,一般都是在 Activity 暂停的时候停止自动播放,在恢复时开始自动播放,在销毁时移除 handler 的回调。
private void autoPlay() { handler.postDelayed(runnable, 2000); } private void stopAutoPlay(){ handler.removeCallbacks(runnable); } @Override protected void onResume() { super.onResume(); autoPlay(); } @Override protected void onPause() { super.onPause(); stopAutoPlay(); } @Override protected void onDestroy() { super.onDestroy(); // 页面被销毁时,移除所有的callbacks和messages handler.removeCallbacksAndMessages(null); }
而当用户手动滑动 ViewPager 时也要暂停播放,松手时再继续播放,所以需要重写ViewPager的OnTouchListener()
方法:
viewPager.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: stopAutoPlay(); break; case MotionEvent.ACTION_UP: autoPlay(); break; default: break; } return false; } });
5.点击事件的处理
我直接给 ImageView 设置了setOnClickListener()
方法,结果发现它拦截了 ViewPager 的 onTouchEvent 事件。尝试了解决这个问题,但是一时又没有找到好的办法,于是想着能不能用别的什么方案替代 ViewPager 的触摸管理或者点击事件,在同事的帮助下,做了如下改变:
不再利用 ViewPager 的OnTouchEvent()
方法进行手势的处理,而是利用onPageScrollStateChanged(int state)
这个方法:
@Overridepublic void onPageScrollStateChanged(int state) { //state: 0 空闲,1 是滑行中,2 加载完毕 if (state == 1 && isRunning) { // 当处于滑动中并且正在自动播放,则停止滑动 // 也就是自动播放的时候用手指去滑动,会停止播放 stopAutoPlay(); } else if (state == 0 && !isRunning) { // 当ViewPager处于空闲状态并且没有在自动播放的时候,才开始自动播放 // 也就是当手指离开屏幕时,再次启动自动播放 autoPlay(); }}private void autoPlay() { Log.e("isRunning", "true"); isRunning = true; handler.postDelayed(runnable, 3000);}private void stopAutoPlay() { Log.e("isRunning", "false"); isRunning = false; handler.removeCallbacks(runnable);}
这样处理之后基本解决了问题,用手指滑动时会停止自动播放,松手时又会继续,同时也可以响应点击事件。最终的效果:
6.遗留的两个问题
1.上文提到过的,首尾相互跳转的时候,导致相邻图片加载延迟问题;
2.利用onPageScrollStateChanged(int state)
方法之后,仅仅是触摸而不滑动 ViewPager 的话,自动播放不会暂停,不知这是否符合交互体验?(游民这个 app 只是触摸也不会暂停自动播放)
代码写的比较乱,其实完全可以写个类继承自 ViewPager,把需要用到的方法写在这个类里,这样更便于管理和拓展,也能减少 Activity 的代码量。如果大家有更好的实现方法或者思路,欢迎指教。
->->->点击下载源码<-<-<-
- ViewPager 实现自动循环轮播 高度自适应 显示前后部分界面 点击事件
- Android使用ViewPager实现图片轮播(高度自适应,左右循环,自动轮播)
- ViewPager轮播图:自动无限轮播,手指长按停止,实现点击事件(实用版)
- 使用ViewPager实现自动无限循环的轮播
- viewpager实现循环轮播
- Android实现ViewPager广告轮播控件在不同分辨率的屏幕下高度自适应
- 自动循环轮播的Viewpager
- Android viewpager实现无限循环轮播
- 自定义ViewGroup实现循环轮播ViewPager
- ViewPager 实现前后自由的“无限”轮播
- Android之ViewPager自动循环播放(轮播)效果实现(超简单)
- viewpager的无限轮播,能够响应点击事件
- Android实现Banner界面循环轮播
- Android自动滚动 轮播循环的ViewPager
- Android自动滚动 轮播循环的ViewPager
- Android自动滚动 轮播循环的ViewPager
- Android自动滚动 轮播循环的ViewPager
- Android自动滚动 轮播循环的ViewPager
- 使用原生JS的AJAX读取json全过程
- java IO
- 计算机图形学基础(2)——画圆,椭圆算法
- 表格单元格增加hover事件
- 将Nginx加入service服务中
- ViewPager 实现自动循环轮播 高度自适应 显示前后部分界面 点击事件
- java多线程笔记(1)
- 《软件工程》课程总结
- Android实现第三方登录
- C++虚函数表解析
- VS修改平台工具集
- 为什么C语言中使用的地址是假的
- IC设计基础系列之CDC篇12:异步FIFO设计资源推荐
- C++设计模式:观察者模式