Banner竖向轮播实现

前言

除了常用横向轮播广告条,现在也有不少应用比如支付宝、美团外卖等首页会有竖向的轮播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实现更加简单易用。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值