ViewPager
是开发中经常用到的控件,这里笔者总结了一些日常开发用到的一些扩展
1.渐变色背景效果
这是一种比较吸引眼球的效果,不过实现原理其实非常简单,就是监听一下 ViewPager
的 onPageScrolled
并动态改变背景色值就可以了。核心代码如下:
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int[] bgColors = new int[]{0XFFFF8080,0XFFFFBC00,0XFF199AFE,0XFF00AB96,0XFF5599FF};
int nextPosition = position + 1 == bgColors.length ? 0 : position + 1;
// ARGB求值器
ArgbEvaluator evaluator = new ArgbEvaluator();
int evaluate =(int) evaluator.evaluate(positionOffset, bgColors[position], bgColors[nextPosition]);
setBackgroundColor(evaluate);
}
就可以实现下面的效果了:
2.一屏内显示多页
一屏内显示多页常见有3种实现方法。
1. setPageMargin
通过 viewpager.setPageMargin(int marginPixels)
,给ViewPager的每页设置一个负数的 margin,然后在每一页的View留足够的空白给这个负值,来达到一屏显示多页的目的。但是此时会出现页面重叠的情况:
要解决这个问题也很简单,就要对页面显示内容做显示区域做一些限制,核心代码如下:
viewPager .setPageMargin(-(int) ((1.0 - 0.8) * getResources().getDisplayMetrics().widthPixels));
到 PagerAdapter
里面处理一下显示区域:
@Override
public Object instantiateItem(ViewGroup container, int position) {
//获取ViewPager显示的View
View contentView = getContentView();
Context context = container.getContext();
FrameLayout frameLayout = new FrameLayout(context);
if (contentView.getLayoutParams() != null) {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
//限制宽度,相当于设置了 margin
(int) (getResources().getDisplayMetrics().widthPixels * 0.8f),
ViewGroup.LayoutParams.MATCH_PARENT);
layoutParams.gravity = Gravity.CENTER;
contentView.setLayoutParams(layoutParams);
}
frameLayout.addView(contentView);
container.addView(frameLayout);
return frameLayout;
}
这时候就可以看到下面的效果:
存在的问题
使用这个方法会带来一个的体验上的问题,那就是本来向左稍微滑动可以达到的翻页效果,现在要把这一页完全翻过才能达到,给人一种奇怪的感觉。如果无需和用户产生交互,那就不用考虑这个影响。
2. getPageWidth
通过重写 PagerAdapter
的 getPageWidth
方法,此方法返回的是 ViewPager
中每一页占实际 ViewPager
的宽度百分比,默认是1.f,即100%:
现在我们来重写这个方法就可以达到一页多个子item的效果:
@Override
public float getPageWidth(int position) {
return 0.8f;
}
效果如下:
存在的问题
这个方法并没有上个方法那样存在的翻页问题,但是也正如上图所示,ViewPager
的页面始终居左,无法居中,如果需求不是这种样式的话,基本不会用到这个方法。
3. 设置clipChildren为false
我们可以通过将 ViewPager
和其父布局的 android:clipChildren
属性为 false
android:clipChildren
属性设置为 true,就表明我们要将 childrenView
给 clip 掉,就是说对于子元素来说,超出当前 view
的部分都会被切掉,那我们在这里把它设置成 false
,就表明超出 view
的部分,不要切掉,依然显示。
<RelativeLayout
android:id="@+id/viewPager_container"
android:layout_width="match_parent"
android:layout_height="200dp"
android:clipChildren="false"
android:layerType="software">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:clipChildren="false" />
</RelativeLayout>
存在的问题
setClipChildren(false)
在3.0以上版本中无法在硬件加速后运行。
在开启硬件加速的情况下,需要将其设置软件加速,可以使用 setLayerType(View.LAYER_TYPE_SOFTWARE, null);
开启软件加速,也可以如上所示直接在 xml
布局中添加 android:layerType="software"
3.轮播图组件需要的点
ViewPager
常给封装为轮播图组件,毕竟它有 item
回收的机制,不会浪费太多的资源。
怎么设置一个 timer
计时器任务这里就不提了,网上这方面的资源太多了,这里就提一下回收的问题。
在 ViewPager
滑出屏幕时需要停止 timer
,再次滑入屏幕时需要启动 timer
,例如在 ListView
和RecyclerView
中;
这个过程我们可以通过重写 ViewPager
里的一些方法实现:
@Override
protected void onAttachedToWindow() {
//进入屏幕时
super.onAttachedToWindow();
startTimer();
}
@Override
protected void onDetachedFromWindow() {
//离开屏幕时,listView在5.0以下不会回调这个方法
super.onDetachedFromWindow();
stopTimer();
}
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
//控件可见状态改变时
super.onVisibilityChanged(changedView, visibility);
if (visibility == View.VISIBLE) {
startTimer();
} else {
stopTimer();
}
}
@Override
public void onStartTemporaryDetach() {
//父容器将当前View移出时,listView在5.0以下回调这个方法
super.onStartTemporaryDetach();
stopTimer();
}
@Override
public void onFinishTemporaryDetach() {
//父容器将当前View填充时,listView在5.0以下回调这个方法
super.onFinishTemporaryDetach();
startTimer();
}
上面的方法已经将大部分的情况都兼容到了,目前笔者只有 ViewPager
+ fragment
的情况下,需要通过 setUserVisibleHint()
方法的回调来手动暂停 timer
,毕竟 ViewPager
是默认持有复数个页面的,上面的方法都是 View
给回收或隐藏时调用的,如果用户只是滑动到傍边的第二个页面,那就自己手动处理一下吧。