美文网首页AndroidAndroid知识android
Android实现任意View的跑马灯效果

Android实现任意View的跑马灯效果

作者: 愚蠢的高小星 | 来源:发表于2016-08-25 11:22 被阅读4287次

    效果图:
    (截取的gif最小间隔50ms,所以看起来有点卡,实际还是很流畅的~)

    说到跑马灯效果,最先想到的应该就是使用TextView自带的android:ellipsize="marquee"属性来实现。但是这个属性在简单易用的同时也有很大的局限性,例如由于ellipsize属性是只有当文字超出控件长度时才会生效,所以当文字较短时就无法产生跑马灯的效果。再比如不能指定滚动方向、不能指定滚动速度等。在开发中常常会遇到复杂的跑马灯需求用自带的属性无法实现的情况,之前接到一个需求要做文字的跑马灯,可是文字一共就只有4个字...那么就无法使用TextView自带的属性实现了,于是就自己实现了一个高度定制化的跑马灯View,可以实现包括图片跑马灯效果、TextView文字不超过宽度时的跑马灯效果等任意View的跑马灯效果,并且可以指定滚动的方向和速度。

    主要实现方式是继承HorizontalScrollView并实现Runnable接口,通过scrollTo方法刷新界面实现。以下是MarqueeView源码:

    package xyy.marqueeview;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.WindowManager;
    import android.widget.HorizontalScrollView;
    import android.widget.LinearLayout;
    
    /**
     * 跑马灯View
     * Created by xuyy on 2016/8/23.
     */
    public class MarqueeView extends HorizontalScrollView implements Runnable{
    
        private Context context;
        private LinearLayout mainLayout;//跑马灯滚动部分
        private int scrollSpeed = 5;//滚动速度
        private int scrollDirection = LEFT_TO_RIGHT;//滚动方向
        private int currentX;//当前x坐标
        private int viewMargin = 20;//View间距
        private int viewWidth;//View总宽度
        private int screenWidth;//屏幕宽度
    
        public static final int LEFT_TO_RIGHT = 1;
        public static final int RIGHT_TO_LEFT = 2;
    
        public MarqueeView(Context context) {
            this(context, null);
        }
    
        public MarqueeView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MarqueeView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            this.context = context;
            initView();
        }
    
        void initView() {
            WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
            screenWidth = wm.getDefaultDisplay().getWidth();
            mainLayout = (LinearLayout)LayoutInflater.from(context).inflate(R.layout.scroll_content, null);
            this.addView(mainLayout);
        }
    
        public void addViewInQueue(View view){
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT);
            lp.setMargins(viewMargin, 0, 0, 0);
            view.setLayoutParams(lp);
            mainLayout.addView(view);
            view.measure(0, 0);//测量view
            viewWidth = viewWidth + view.getMeasuredWidth() + viewMargin;
        }
    
        //开始滚动
        public void startScroll(){
            removeCallbacks(this);
            currentX = (scrollDirection == LEFT_TO_RIGHT ? viewWidth : -screenWidth);
            post(this);
        }
    
        //停止滚动
        public void stopScroll(){
            removeCallbacks(this);
        }
    
        //设置View间距
        public void setViewMargin(int viewMargin){
            this.viewMargin = viewMargin;
        }
    
        //设置滚动速度
        public void setScrollSpeed(int scrollSpeed){
            this.scrollSpeed = scrollSpeed;
        }
    
        //设置滚动方向 默认从左向右
        public void setScrollDirection(int scrollDirection){
            this.scrollDirection = scrollDirection;
        }
    
        @Override
        public void run() {
            switch (scrollDirection){
                case LEFT_TO_RIGHT:
                    mainLayout.scrollTo(currentX, 0);
                    currentX --;
    
                    if (-currentX >= screenWidth) {
                        mainLayout.scrollTo(viewWidth, 0);
                        currentX = viewWidth;
                    }
                    break;
                case RIGHT_TO_LEFT:
                    mainLayout.scrollTo(currentX, 0);
                    currentX ++;
    
                    if (currentX >= viewWidth) {
                        mainLayout.scrollTo(-screenWidth, 0);
                        currentX = -screenWidth;
                    }
                    break;
                default:
                    break;
            }
    
            postDelayed(this, 50 / scrollSpeed);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            return false;
        }
    }
    
    

    addViewInQueue()方法中先将添加的View加到滚动部分mainLayout中,再计算添加View的总宽度,便于下面计算滚动起点。

    startScroll()方法中,如果方向是从左向右,那就将滚动起点设为(viewWidth, 0),如果是从右向左,那就将滚动起点设为(-screenWidth, 0)。这里要注意的是scrollTo方法参数为正时在坐标系中是负向(向左)的,参数为负时在坐标系中是正向(向右)的。

    mainLayout就是滚动部分了。当mainLayout整体全部滚动出屏幕,即滚动距离=screenWidth+viewWidth时重置起点,实现循环跑马灯的效果。

    mainLayout的布局很简单,就是一个横向的LinearLayout。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
    
    </LinearLayout>
    

    使用:

    public class MainActivity extends Activity {
    
        private MarqueeView marqueeView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            marqueeView = (MarqueeView) findViewById(R.id.marquee_view);
            initMarqueeView();
        }
    
        private void initMarqueeView(){
            ImageView iv1 = new ImageView(this);
            iv1.setImageResource(R.drawable.pic1);
            marqueeView.addViewInQueue(iv1);
            ImageView iv2 = new ImageView(this);
            iv2.setImageResource(R.drawable.pic2);
            marqueeView.addViewInQueue(iv2);
            ImageView iv3 = new ImageView(this);
            iv3.setImageResource(R.drawable.pic3);
            marqueeView.addViewInQueue(iv3);
            ImageView iv4 = new ImageView(this);
            iv4.setImageResource(R.drawable.pic4);
            marqueeView.addViewInQueue(iv4);
            ImageView iv5 = new ImageView(this);
            iv5.setImageResource(R.drawable.pic5);
            marqueeView.addViewInQueue(iv5);
            ImageView iv6 = new ImageView(this);
            iv6.setImageResource(R.drawable.pic6);
            marqueeView.addViewInQueue(iv6);
    
            marqueeView.setScrollSpeed(8);
            marqueeView.setScrollDirection(MarqueeView.RIGHT_TO_LEFT);
            marqueeView.setViewMargin(15);
            marqueeView.startScroll();
        }
    }
    

    MainActivity布局:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <xyy.marqueeview.MarqueeView
            android:id="@+id/marquee_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="@color/black60"
            android:fillViewport="true"
            android:gravity="center"
            android:padding="5dp"/>
    </RelativeLayout>
    

    这里用的是ImageView,当然替换成其他任何View都是可以的~

    https://github.com/GITbiubiubiu/MarqueeView/

    相关文章

      网友评论

      • jiangbin1992:网络头像咋办
      • codeRL:可是6张图片跑完了,他还有要空白一轮才能继续循环,是不是有bug
        codeRL:@愚蠢的高小星 哦哦 懂了 多谢回复
        愚蠢的高小星:这个和TextView跑马灯的效果是一样的 整个view移出屏幕后再进行下一轮的循环:
        if (currentX >= viewWidth) {
        mainLayout.scrollTo(-screenWidth, 0);
        currentX = -screenWidth;
        }
      • a167796fd70b:放在fragment中 和viewpager的跑马效果一块用的时候 会出现短暂的卡顿现象
        837545155:这问题你有解决的办法了吗?
      • d047a73d7c0e:棒棒哒:confetti_ball:加油哦
      • JAFB:好
      • 汪简书: /**
        * 移除 view
        * @param view
        */
        public void removeViewInQueue(View view){
        mainLayout.removeView(view);
        }
      • 汪简书:很赞,若有个 移除view 就完美了。
      • Air青:缺少scroll_content的布局文件
        Air青:@愚蠢的高小星 确实没什么贴的必要,我自己加上了,还是懒啊 :joy:
        愚蠢的高小星:@Air青 就一个LinearLayout就没贴 既然你说了那就贴出来吧~ :flushed:

      本文标题:Android实现任意View的跑马灯效果

      本文链接:https://www.haomeiwen.com/subject/dpaosttx.html