前言
除了常用横向轮播广告条,现在也有不少应用比如支付宝、美团外卖等首页会有竖向的轮播Banner。其实横向轮播Banner在一个页面上如果有两个或以上,会让用户觉得很怪,这主要是因为只有横向切换展现方式过于单调。增加了竖向轮播控件,界面上又多了一种吸引用户的表现方式,这对提升用户体验很有帮助。接下来使用两种不同的方式实现竖向轮播。
实现效果
实现接口
在同一个位置有两个视图,这两个视图一个在可见位置向上到用户看不到的位置,另外一个从底部看不到的位置向上运动到用户完全看到的位置。可以用下图来描述整个运动过程:
这样就很容易想到一个属性translationY,可以看到隐藏和展示的两个View都在不停的变换自己的translationY属性值。现在就可以通过属性动画结合postDelay来实现不断的竖向切换效果。
还有另外一种实现就是使用ViewFlipper切换,查看ViewFlipper的源码会发现实现其实是在FrameLayout中把所有的View都加入,然后展示哪个View就先设置可见再执行translate动画,隐藏哪个View就先执行translate动画再隐藏View。
实现过程
TranslationY属性动画
观察上面的动画示意图会发现第一个要隐藏View的translationY是从(0, -view.getHeight()),而第二个要展示的View的translationY是从(view.getHeight(), 0)变化,找到这个规律之后很容易实现竖向的切换效果。
public class VerticalScrollView extends FrameLayout {
// 处于隐藏状态的View
private View recycleView;
// 处于可见状态的View
private View currentView;
// 正常的数据Adapter
private BaseAdapter adapter;
private int current = -1;
private Runnable playRunnable = new Runnable() {
@Override
public void run() {
// 每过一段时间切换数据
current = (current + 1) % adapter.getCount();
// 为隐藏的View更新数据,复用以前用过的View
View newView = adapter.getView(current, recycleView, VerticalScrollView.this);
// 定义隐藏属性动画
ObjectAnimator hide = ObjectAnimator.ofFloat(currentView, "translationY", 0, -recycleView.getHeight());
// 定义展示属性动画
ObjectAnimator show = ObjectAnimator.ofFloat(newView, "translationY", newView.getHeight(), 0);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(hide).with(show);
animatorSet.setDuration(1000);
animatorSet.start();
// 交换隐藏View和当前View的引用
recycleView = currentView;
currentView = newView;
// 3秒之后再进行一次
postDelayed(this, 3000);
}
};
public VerticalScrollView(@NonNull Context context) {
this(context, null);
}
public VerticalScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public VerticalScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
}
public void setAdapter(BaseAdapter adapter) {
this.adapter = adapter;
if (adapter == null || adapter.getCount() < 1) {
setVisibility(View.GONE);
return;
}
// 初始化第一次展示的View
currentView = adapter.getView(0, null, this);
// 初始化隐藏的View
recycleView = adapter.getView(1, null, this);
current = 0;
addView(recycleView);
addView(currentView);
}
public void pausePlay() {
removeCallbacks(playRunnable);
}
public void resumePlay() {
removeCallbacks(playRunnable);
postDelayed(playRunnable, 3000);
}
public void destroy() {
pausePlay();
playRunnable = null;
}
}
通过简单的对postDelay和ObjectAnimator做封装,用户只要调用setAdapter就可以实现竖向轮播的效果。
ViewFlipper实现
ViewFlipper的实现就更简单了,只需要提供展示和隐藏时候的动画效果就可以了。
// slide_in.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="100%p"
android:toXDelta="0"
android:toYDelta="0">
</translate>
// slide_out.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="-100%p">
</translate>
public class VerticalScrollView2 extends ViewFlipper {
private BaseAdapter adapter;
public VerticalScrollView2(@NonNull Context context) {
this(context, null);
}
public VerticalScrollView2(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// 初始化ViewFlipper,设置展示进入和隐藏退出的动画效果
setAutoStart(false);
setFlipInterval(3000);
setInAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.slide_in));
setOutAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.slide_out));
}
public void setAdapter(BaseAdapter adapter) {
this.adapter = adapter;
if (adapter == null || adapter.getCount() < 1) {
setVisibility(View.GONE);
return;
}
// 把Adapter中所有的ItemView都生成添加到ViewFlipper中
for (int i = 0, count = adapter.getCount(); i < count; i++) {
addView(adapter.getView(i, null, this));
}
}
public void pausePlay() {
stopFlipping();
}
public void resumePlay() {
startFlipping();
}
public void destroy() {
pausePlay();
}
}
查看全部实现代码请点击查看源码
总结
竖向轮播相对比较简单,但是属性动画的实现方式只会生成两个视图布局,一个是展示用,另外一个是隐藏时用,这里借鉴了ListView的复用思想。但是ViewFlipper要求一次把所有的视图都生成,如果数据量很大明显就不合适。所以如果很在乎不要大量产生视图对象,采用属性动画方式更好,如果数据量不大ViewFlipper实现更加简单易用。