仿淘宝、京东滚动播放控件

项目需求 要做一个 上下 播放通知的 控件 于是。。。

/**
 * ViewFlipper 仿淘宝、京东滚动播放控件
 *
 */

public class ViewsFlipper extends FrameLayout {
    @SuppressWarnings("unused")
    private static final String TAG = ViewsFlipper.class.getSimpleName();

    private static final int DEFAULT_FLIP_DURATION = 500;

    private static final int DEFAULT_FLIP_INTERVAL = 3000;

    /**
     * animations interval
     */
    private long mFlipInterval = DEFAULT_FLIP_INTERVAL;

    /**
     * animations duration
     */
    private long mFlipDuration = DEFAULT_FLIP_DURATION;

    /**
     * 动画偏移量,涉及到View动画滚动距离,当onSizeChanged时候获取
     * (为什么不从onMeasure?是因为每次设置childView VISIBLE的时候都会触发重绘,每次都要执行onMeasure,感觉太频繁了)
     */
    private int mTranslationY;

    private int mTranslationX;

    /**
     * view in animator
     */
    private ObjectAnimator mInAnimator;

    /**
     * view out animator
     */
    private ObjectAnimator mOutAnimator;

    /**
     * is the view visible or not.
     * default is false, changed when view visibility change. or
     * {@link ViewsFlipper#onAttachedToWindow()}
     * {@link ViewsFlipper#onDetachedFromWindow()}
     */
    private boolean mVisible = false;

    /**
     * is view begin start flipping or not
     * when call {@link ViewsFlipper#startFlipping()} set mStart true.
     * when call {@link ViewsFlipper#stopFlipping()} ()} set mStart false.
     */
    private boolean mStarted = false;

    /**
     * if flipper is running or not
     * determined by mVisible && mStarted
     */
    private boolean mRunning = false;

    /**
     * current show view index
     */
    private int mWhichChild = 0;

    /**
     * current data index, get from {@link RecyclerView.Adapter#getItemCount())
     */
    private int mPosition = 0;

    /**
     * view scroll orientation
     */
    @RecyclerView.Orientation
    private int mOrientation = RecyclerView.VERTICAL;

    private RecyclerView.Adapter<RecyclerView.ViewHolder> mAdapter;

    /**
     * shadowed child view
     */
    private RecyclerView.ViewHolder shadowedVH;

    /**
     * showing child view
     */
    private RecyclerView.ViewHolder showingVH;

    private AnimatorSet animatorSet;

    private RecyclerView.AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            super.onChanged();
            // when data changed, reset view.
            reset();
            // if animator set is not null, maybe the view is in animation,
            // so we need end the animator first.
            if (animatorSet != null) {
                animatorSet.end();
            }

            if (mAdapter == null || mAdapter.getItemCount() <= 0) {
                throw new IllegalArgumentException("please call ViewsFlipper.setAdapter first and set non-empty data!");
            }

            // restart from first child view.
            mRunning = true;
            //show first child view immediately.
            mAdapter.bindViewHolder(showingVH, 0);
            setDisplayedChild(0, false);

            //cycling show next view.
            postDelayed(mFlipRunnable, mFlipInterval);
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            super.onItemRangeChanged(positionStart, itemCount);
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
            super.onItemRangeChanged(positionStart, itemCount, payload);
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            super.onItemRangeInserted(positionStart, itemCount);
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            super.onItemRangeRemoved(positionStart, itemCount);
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount);
        }
    };

    public ViewsFlipper(Context context) {
        this(context, null);
    }

    public ViewsFlipper(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    / public methods ///
    /     below     

    /**
     * Start a timer to cycle through child views
     * without show animation.
     */
    public void startFlipping() {
        if (mAdapter == null) {
            throw new IllegalArgumentException("you must call ViewsFlipper.setAdapter first!");
        }
        mStarted = true;
        updateRunning(false);
    }

    /**
     * stop flip
     */
    public void stopFlipping() {
        mStarted = false;
        updateRunning();
    }

    /**
     * How long to wait before flipping to the next view
     *
     * @param milliseconds times in milliseconds
     */
    public void setFlipInterval(long milliseconds) {
        this.mFlipInterval = milliseconds;
        if (mFlipInterval < mFlipDuration) {
            throw new IllegalArgumentException("flip interval must set bigger than flip duration!!!");
        }
    }

    @SuppressWarnings("unused")
    public long getFlipInterval() {
        return mFlipInterval;
    }

    /**
     * set how long flipping in/out animation cost
     *
     * @param milliseconds times in milliseconds
     */
    public void setFlipDuration(long milliseconds) {
        mFlipDuration = milliseconds;
        if (mFlipInterval < mFlipDuration) {
            throw new IllegalArgumentException("flip interval must set bigger than flip duration!!!");
        }
        mInAnimator.setDuration(milliseconds);
        mOutAnimator.setDuration(milliseconds);
    }

    @SuppressWarnings("unused")
    public long getFlipDuration() {
        return mFlipDuration;
    }

    /**
     * set the scroll orientation
     *
     * @param orientation {@link RecyclerView.Orientation}
     */
    public void setOrientation(@RecyclerView.Orientation int orientation) {
        mOrientation = orientation;
        boolean vertical = mOrientation == RecyclerView.VERTICAL;

        if (animatorSet != null) {
            // if animator set is not null, maybe the view is in animation,
            // so we need end the animator first.
            animatorSet.end();
        }

        // reset the shadowed view position,
        // because Animator change the View position forever, and we have move the shadowed view out of screen
        // so we need to reset the shadowed view to right position.
        if (shadowedVH != null) {
            if (vertical) {
                shadowedVH.itemView.setX(showingVH.itemView.getX());
            } else {
                shadowedVH.itemView.setY(showingVH.itemView.getY());
            }
        }

        if (mInAnimator != null) {
            mInAnimator.setPropertyName(vertical ? "translationY" : "translationX");
            mInAnimator.setFloatValues(vertical ? mTranslationY : mTranslationX, 0);
        }
        if (mOutAnimator != null) {
            mOutAnimator.setPropertyName(vertical ? "translationY" : "translationX");
            mOutAnimator.setFloatValues(0, vertical ? -mTranslationY : -mTranslationX);
        }
    }

    @SuppressWarnings("unused")
    public int getOrientation() {
        return mOrientation;
    }

    @SuppressWarnings("unchecked cast, unused")
    public <VH extends RecyclerView.ViewHolder, T extends RecyclerView.Adapter<VH>> void setAdapter(T adapter) {
        if (adapter == null || adapter.getItemCount() <= 0) {
            throw new IllegalArgumentException("please call ViewsFlipper.setAdapter first and set non-empty data!");
        }

        reset();
        this.removeAllViews();
        this.mAdapter = (RecyclerView.Adapter<RecyclerView.ViewHolder>) adapter;
        this.mAdapter.registerAdapterDataObserver(mObserver);
        showingVH = mAdapter.createViewHolder(this, 0);
        shadowedVH = mAdapter.createViewHolder(this, 0);
        //noinspection ConstantConditions
        if (showingVH == null || shadowedVH == null) {
            throw new IllegalArgumentException("ViewHolder must be not null!");
        }

        //add child view to parent
        addView(showingVH.itemView);
        addView(shadowedVH.itemView);
        showingVH.itemView.setVisibility(View.VISIBLE);
        shadowedVH.itemView.setVisibility(View.INVISIBLE);
        mAdapter.bindViewHolder(showingVH, 0);
    }

    private void reset() {
        removeCallbacks(mFlipRunnable);
        mPosition = 0;
        mWhichChild = 0;
        mRunning = false;
    }

    /**
     * init flipper animation and setting
     */
    private void init(Context context, AttributeSet attrs) {
        initAnimation();

        if (null != attrs) {
            /* get config from xml files */
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewsFlipper);
            setFlipDuration(a.getInteger(R.styleable.ViewsFlipper_flipDuration, DEFAULT_FLIP_DURATION));
            setFlipInterval(a.getInteger(R.styleable.ViewsFlipper_flipInterval, DEFAULT_FLIP_INTERVAL));
            a.recycle();
        }
    }

    private void initAnimation() {
        mInAnimator = defaultInAnimator();
        mOutAnimator = defaultOutAnimator();
    }

    private ObjectAnimator defaultInAnimator() {
        ObjectAnimator animY = ObjectAnimator.ofFloat(null, "translationY", mTranslationY, 0);
        ObjectAnimator animX = ObjectAnimator.ofFloat(null, "translationX", mTranslationX, 0);
        ObjectAnimator anim;
        if (mOrientation == RecyclerView.VERTICAL) {
            anim = animY;
        } else {
            anim = animX;
        }
        anim.setDuration(DEFAULT_FLIP_DURATION);
        return anim;
    }

    private ObjectAnimator defaultOutAnimator() {
        ObjectAnimator animY = ObjectAnimator.ofFloat(null, "translationY", 0, -mTranslationY);
        ObjectAnimator animX = ObjectAnimator.ofFloat(null, "translationX", 0, -mTranslationX);
        ObjectAnimator anim;
        if (mOrientation == RecyclerView.VERTICAL) {
            anim = animY;
        } else {
            anim = animX;
        }
        anim.setDuration(DEFAULT_FLIP_DURATION);
        return anim;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldW, int oldH) {
        super.onSizeChanged(w, h, oldW, oldH);
        mTranslationY = getMeasuredHeight();
        mTranslationX = getMeasuredWidth();
        setOrientation(mOrientation);
    }

    /**
     * update view
     */
    private void updateRunning() {
        updateRunning(true);
    }

    /**
     * only show current child index view. set other view invisible.
     *
     * @param childIndex child view index
     * @param animate    is showing with animation in
     */
    void showOnly(int childIndex, boolean animate) {
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            final View preChild = getChildAt((i == 0) ? count - 1 : i - 1);
            if (i == childIndex) {
                if (animate && mInAnimator != null) {
                    mOutAnimator.setTarget(preChild);
                    mInAnimator.setTarget(child);
                    animatorSet = new AnimatorSet();
                    animatorSet.playTogether(mOutAnimator, mInAnimator);
                    animatorSet.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                            super.onAnimationStart(animation);
                            child.setVisibility(View.VISIBLE);
                        }

                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            preChild.setVisibility(View.INVISIBLE);
                        }

                    });
                    animatorSet.start();
                } else {
                    // if not set animation, or animate is false,
                    // then show child view immediately.
                    child.setVisibility(View.VISIBLE);
                }
            }
        }
    }

    /**
     * begin running
     *
     * @param flipNow animation or not
     */
    private void updateRunning(boolean flipNow) {
        boolean running = mVisible && mStarted;
        if (running != mRunning) {
            if (running) {
                showOnly(mWhichChild, flipNow);
                postDelayed(mFlipRunnable, mFlipInterval);
            } else {
                removeCallbacks(mFlipRunnable);
            }
            mRunning = running;
        }
    }

    /**
     * show next view
     */
    protected void showNext() {
        // if the flipper is currently flipping automatically, and showNext() is called
        // we should we should make sure to reset the timer
        if (mRunning) {
            removeCallbacks(mFlipRunnable);
            postDelayed(mFlipRunnable, mFlipInterval);
        }

        //add child index and data index. cycling show.
        mPosition = mPosition >= mAdapter.getItemCount() - 1 ? 0 : mPosition + 1;
        mWhichChild = ((mWhichChild >= getChildCount() - 1) ? 0 : mWhichChild + 1);
        setDisplayedChild(mWhichChild);
    }

    /**
     * set display view by index in parent
     *
     * @param whichChild the display view index
     */
    private void setDisplayedChild(int whichChild) {
        setDisplayedChild(whichChild, true);
    }

    private void setDisplayedChild(int whichChild, boolean animate) {
        //swap shadowed view and showing view.
        if (showingVH.itemView.getVisibility() == View.VISIBLE) {
            swapViewHolder();
        }
        mAdapter.bindViewHolder(showingVH, mPosition);

        boolean hasFocus = getFocusedChild() != null;
        showOnly(whichChild, animate);
        if (hasFocus) {
            // Try to retake focus if we had it
            requestFocus(FOCUS_FORWARD);
        }
    }

    private void swapViewHolder() {
        RecyclerView.ViewHolder tmp = showingVH;
        showingVH = shadowedVH;
        shadowedVH = tmp;
    }

    private final Runnable mFlipRunnable = new Runnable() {
        @Override
        public void run() {
            if (mRunning) {
                ViewsFlipper.this.showNext();
            }
        }
    };

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mVisible = true;
        startFlipping();

    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mVisible = false;
        stopFlipping();
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mVisible = (visibility == VISIBLE);
        updateRunning(false);
    }
}
    <!-- ViewFlipper 仿淘宝、京东滚动播放控件-->
    <declare-styleable name="ViewsFlipper">
        <attr name="flipInterval" format="integer"/>
        <attr name="flipDuration" format="integer"/>
    </declare-styleable>

使用

    <ViewsFlipper
        android:id="@+id/share_flipper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:flipDuration="500"  
        app:flipInterval="2500" />

使用跟 RecyclerView 一样 图片随便复制的连接 勿怪

    //初始化 滚动 视图
    private List<Pair<String, String>> items = new ArrayList<>();
    private void initFlipper() {
        items.add(new Pair<>("https://pic2.zhimg.com/v2-8a9c5742806628253e7c0ea80fd76769_xs.jpg", "感受*** 成功邀请好友,获得50金币"));
        items.add(new Pair<>("https://pic3.zhimg.com/5608a69e052a0f1c1bd72f4785fa3819_xs.jpg", "如何*** 成功邀请好友,获得50金币"));
        items.add(new Pair<>("https://pic2.zhimg.com/da8e974dc_xs.jpg", "记住*** 成功邀请好友,获得50金币"));
        items.add(new Pair<>("https://pic2.zhimg.com/v2-8a9c5742806628253e7c0ea80fd76769_xs.jpg", "也许*** 成功邀请好友,获得50金币"));
        items.add(new Pair<>("https://pic2.zhimg.com/da8e974dc_xs.jpg", "面对***,成功邀请好友,获得50金币"));
        items.add(new Pair<>("https://pic2.zhimg.com/v2-8a9c5742806628253e7c0ea80fd76769_xs.jpg", "我们*** 成功邀请好友,获得50金币"));
        items.add(new Pair<>("https://pic3.zhimg.com/5608a69e052a0f1c1bd72f4785fa3819_xs.jpg", "漂浮*** 成功邀请好友,获得50金币"));
        flipperAdapter=new FlipperAdapter(items);
        flipper.setAdapter(flipperAdapter);
        flipper.setOrientation(RecyclerView.VERTICAL);
        flipper.startFlipping();
    }

   private class FlipperAdapter extends BaseQuickAdapter<Pair<String, String>, BaseViewHolder> {

        public FlipperAdapter(@Nullable List<Pair<String, String>> data) {
            super(R.layout.layout_flipper_child,data);
        }

        @Override
        protected void convert(BaseViewHolder helper, Pair<String, String> item) {
            helper.setText(R.id.tv_child,item.second)
            .setTextColor(R.id.tv_child,Color.WHITE);
            CircleImageView icon = helper.getView(R.id.iv_child);
            new ImageLoaderImpl().loadImage(mContext,item.first).into(icon);
        }
    }

好了。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值