Android无限循环自动滚动信息展示

        第一次在csdn上写博客,心里还有点小激动,请问一下在座的各位,怎么样才能装得像不是第一次 写博客的样子?如果没人知道的话,我待会再来问问。    

        废话少说,目前正在开发一款彩票类应用。应用中UX有一个需求,需要轮训展示中奖用户信息。具体参考如下所示,就是图中中奖名单下那个不断滚动的小界面:

    

        第一次看到这个效果,感觉so easy!脑中至少三个方案闪过。1.动画方案。2.改造系统控件TextSwitcher,或者直接使用Viewpager,ViewFlipper等。3.让ListView 或者说RecyclerView,平滑滚动。因为以前接触过ViewFlipper这个控件,也比较简单,当时鬼使神差的就用了方案2,效果不太理想,具体后面再说。有时候,惰性真的太可怕了。


        方案1:先看第一种方案的具体实现:ViewFlipper

        ViewFlipper这个控件天生就是为循环滚动而生的,重点是这货简单粗暴,上手快。三个步骤,简单搞定。

        1.布局:具体的布局代码就不贴了。没有什么太多的意义。贴个图,大家都懂怎么布局吧。需要说明的是,一般ViewFlipper都是单个条目item滚动,为了实现让人感觉多个条目一起滚动,我将如下四个条目用一个layout包裹在一起作为一个条目.

    

        所以具体的布局应该如下   

 <ViewFlipper
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/comment_divide_broader"
        android:id="@+id/vf"
        android:inAnimation="@anim/anim_come_in"
        android:outAnimation="@anim/anim_get_out"
        android:flipInterval="5000">
        <LinearLayout.../>  <LinearLayout/>
</ViewFlipper>

         2.动画:

                 anim_come_in动画    

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator"
    android:shareInterpolator="true">
    <translate
        android:duration="5000"
        android:fromYDelta="100%"
        android:toYDelta="0" />
</set>

                anim_get_out动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
    android:interpolator="@android:anim/linear_interpolator">
    <translate
        android:duration="5000"
        android:fromYDelta="0"
        android:toYDelta="-100%p" />
</set>

        3.初始化:因为基本上ViewFlipper的初始化动作都在布局中文件中设置完了,所以代码中的初始化除了为各个View绑定数据外,没做什么事,核心startFlipping()方法直接让ViewFlipper飞起来。

mVF.startFlipping();

        方案一实践基本上完成。看看效果,还挺像回事,ViewFlipper内部应该是用视图动画实现该效果的,之前发现该默认的动画有加速和减速效果,后面在配置的动画中加了个均速效果,效果看上去没有那么突兀。但是,但是,我说但是,这个方案在我这里有个致命缺陷。在这里,我设置ViewFlipper轮训的时间为5s,即5s重复一次动画,并且动画的耗时也是5s。这样做的目的是,实现类似于弹性滑动的效果,动画至于突兀。于是5s成了一个漫长的等待。在首次进入界面的时候,ViewFlipper必须等待5s才能动起来。5s,5s,5s,是五秒啊,老板看到这个效果,差点拿起刀。。。。。所以这个方案被扼杀在成品之中。

        不过这个有没有办法解决?,有,绝对有。等我看完源码再来记录吧。


        方案2:让RecyclerView自己动起来

        这个方案其实也是比较简单的,实现思路也是比较简单,大概下面几方面吧

                1.RecyclerView适配器Adapter,返回无限个item(具体是Integer的最大值);

                2.让RecyclerView平滑的滑动到最后一个最后一个item;

                3.如果RecyclerView滑动到最后一个,定位到第一个item,重复2步骤.

        思路很清楚,那就动手吧,我两米长的西瓜刀已经饥渴难耐了。

                步骤1:让适配器返回无限个item.

  @Override
    public int getItemCount() {
        // 返回足够多的item
        return mData == null ? 0 :Integer.MAX_VALUE;
    }

            需要注意的是,我们设置的数据不可能那么多,所以,item 绑定数据的时候,需要一点小技巧,直接用取余的方法去取,这样就能保证我们每个item都能有数据。但是这样做就要求我们样本数据最好多一点。

 @Override
    public void onBindViewHolder(@NonNull AutoScrollViewHolder holder, int position) {
        if(!mData.isEmpty()){
            holder.setData(mData.get(position%mData.size()));
        }
    }

            步骤2:让RecyclerView平滑滑动到最后一个item

        在代码中的实现只有短短一句话

  //平滑滑动到最后一个
        smoothScrollToPosition(mAdapter.getItemCount());

        但是这句话的背后却比较复杂,所以最好点进去看看RecyclerView具体是怎么实现的

/**
     * Starts a smooth scroll to an adapter position.
     * <p>
     * To support smooth scrolling, you must override
     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
     * {@link SmoothScroller}.
     * <p>
     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
     * provide a custom smooth scroll logic, override
     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
     * LayoutManager.
     *
     * @param position The adapter position to scroll to
     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
     */
    public void smoothScrollToPosition(int position) {
        if (mLayoutFrozen) {
            return;
        }
        if (mLayout == null) {
            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
                    + "Call setLayoutManager with a non-null argument.");
            return;
        }
        mLayout.smoothScrollToPosition(this, mState, position);
    }

          请注意,我是把这个方法的注释也一起复制过来了。我觉得看得懂注释要比看得懂代码重要的多,虽然我平时很不喜欢写注释。注释的大概意思。1.这 个方法的功能是开始平滑的滑动到一个指定的位置。2.为了支持平滑滑动,你的RecyclerView 设置的LayoutManager必须实现smoothScrollToPosition(RecyclerView,State,int)方法,并且创建SmoothScroller对象。3.LayoutManager 这货负责创建实际的滑动动作,如果你想实现客制化的滑动逻辑,需要覆盖smoothScrollToPosition(RecyclerView,State,int)。英语不太好,大概这个意思,凑合着看吧。如果注释不是很理解的话,再看看方法

 mLayout.smoothScrollToPosition(this, mState, position);

     mLayout这货就是LayoutManager对象。原来这个方法最终走的是LayoutManager的smoothScrollToPosition(RecyclerView,state,int)方法。ok,再看看注释,原来这个意思,明白了。我需要客制化滑动效果(控制我的滑动速度),好吧,就按照注释走吧。

         按照注释走,自定义一个LayoutManager,为了减少代码量,直接继承LinearLayoutManager.然后直接复写smoothScrollToPosition方法。在该方法中创建一个Scroller对象,跟之前注释内容完美契合。不懂写的代码怎么办,直接看LinearLayoutManager,依样画葫芦。因为要控制速度,直接在Scroller对象中复写了calculateSpeedPerPixel(DisplaMetrics)方法。

public class AutoScrollLayoutManager extends LinearLayoutManager {

    public AutoScrollLayoutManager(Context context) {
        super(context);
    }

    //  客制化需要实现该方法;
    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int
            position) {
        LinearSmoothScroller linearSmoothScroller =
                new LinearSmoothScroller(recyclerView.getContext()) {
                    @Nullable
                    @Override
                    public PointF computeScrollVectorForPosition(int targetPosition) {
                        return AutoScrollLayoutManager.this.computeScrollVectorForPosition
                                (targetPosition);
                    }

                    @Override
                    protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                        // 计算滑动每个像素需要的时间,这里应该与屏幕适配;
                        return 15f / displayMetrics.density;
                    }
                };
        linearSmoothScroller.setTargetPosition(position);

        startSmoothScroll(linearSmoothScroller);

    }
}    

        将自定义的LayoutManager设置进RecyclerView中

        // list的初始化;
        AutoScrollLayoutManager autoScrollLayoutManager = new AutoScrollLayoutManager(context);
        this.setLayoutManager(autoScrollLayoutManager);
        mAdapter = new AutoScrollAdapter();
        mAdapter.setData(RandomUtils.getRandomWinLotteryObjList(20));
        setAdapter(mAdapter);

       基本上完成了,最后一个步骤,为RecyclerView加上滑动监听,如果自动滑动到最后一个item,再次重新定位到开始位置,然后再次重复平滑滑动。

this.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    // 如果自动滑动到最后一个位置,则此处状态为SCROLL_STATE_IDLE
                    AutoScrollLayoutManager lm = (AutoScrollLayoutManager) recyclerView
                            .getLayoutManager();

                    int position = lm.findLastCompletelyVisibleItemPosition();
                    int count = lm.getItemCount();
                    if(position == count-1){
                        lm.scrollToPosition(0);
                        AutoScrollRecyclerView.this.smoothScrollToPosition(mAdapter.getItemCount());
                    }
                }


            }
        });

        搞定!运行一下,还行。姑且先这样吧,哦,no,no,不,差点忘记了,用户点击,触摸等交互,会打断代码设置的平滑滑动操作,这个RecyclerView只负责展示,如果用户手贱,去摸了一下。交互是吧,割了。

    // 拦截事件;
    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        // 拦截触摸事件;
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // 消费事件;
        return true;
    }
        不要问我为什么这么写,我就是这么简单粗暴!能交互吗?不能。好的,完了,收工。

        

        方案3:动画

                动画方案比较简单,等做完项目再来补充吧。




            

          



















        



猜你喜欢

转载自blog.csdn.net/yuguqinglei/article/details/80177682