Android TextView实现跑马灯效果

android 文字实现跑马灯效果,网上也有很多说明,此处做个人记录。

网上有很多方式实现,在这总结一下自己实现和其他大佬实现方式。
方式一:
  1. 自定义MarqueeTextView 继承自TextView,重写isFocused()方法
public class MarqueeTextView extends TextView {
    public MarqueeTextView(Context context) {
        super(context);
    }

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

    public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean isFocused() {
    	//必须重写,且返回值是true,表示始终获取焦点
        return true;
    }

}
  1. 在XML文件中添加对应属性设置
    主要属性:
    android:ellipsize=“marquee” //设置超出显示区域的内容以跑马灯效果呈现,该值还可设置成 END,START 等,就是我们常见的末尾 “…” 显示
    android:focusable=“true” //获取焦点
    android:focusableInTouchMode=“true”
    android:marqueeRepeatLimit=“marquee_forever” //设置循环次数,-1,marquee_forever 表示无线循环
    android:singleLine=“true” //设置单行显示

    参数说明:
    android:ellipsize = "end"    省略号在结尾
    android:ellipsize = "start"   省略号在开头
    android:ellipsize = "middle"     省略号在中间
    android:ellipsize = "marquee"  跑马灯
    

在这贴出我使用的,可能有一些不需要,根据需求参考:

<com.washroom.view.MarqueeTextView
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/tv_bulletin_board"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:gravity="center_vertical"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:scrollHorizontally="true"
        android:singleLine="true"
        android:text="公告:1、我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView"
        android:textColor="@color/color_cewei_title"
        android:textSize="20sp"
        android:padding="3dp"

        tools:ignore="MissingDefaultResource" />
  1. 文字长度要比 MarqueeTextView长,
方式二:

该方式是贴出其他大佬使用的方法:
原文链接
该方式已经尝试,可以实现横向文字跑马灯效果且效果较顺滑,如有问题,请在原链接中查找解决方法。

Gradle集成使用:

implementation 'anylife.scrolltextview:ScrollTextviewLib:1.x,y'

Maven集成使用:

<dependency>
  <groupId>anylife.scrolltextview</groupId>
  <artifactId>ScrollTextviewLib</artifactId>
  <version>1.x.y</version>
  <type>pom</type>
</dependency>

使用:

 <anylife.scrolltextview.ScrollTextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@+id/imageView2"
        android:layout_marginRight="6dp"
        android:singleLine="true"
        customAttr:clickEnable="true"
        customAttr:isHorizontal="true"
        customAttr:speed="1"
        customAttr:text="ScrollTextView Auto Scroll.1234567890,"
        customAttr:text_size="14sp"
        customAttr:text_color="#ffffffff"
        customAttr:times="567" />

或者在代码中:

 	scrollText = findViewById(R.id.scrollText);
    scrollText.setSpeed(4);
    scrollText.setText("new text");
    scrollText.setTextColor(0xffad43ae);
方式三:

原文链接
此方式可以放置多种view,imageview,textview等
该方式已经测试,可以实现文字跑马灯效果,但是唯一不足,跑马灯有卡顿现象,我在模拟器上运行的,也可能是模拟器问题,根据实际情况参考。

主要实现方式是继承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;
    }
	/**
	* 移除 view
	* @param view
	* 此方法是通过评论区大佬添加,暂未亲测
	*/
	public void removeViewInQueue(View view){
		mainLayout.removeView(view);
	}

    //开始滚动
    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>
其实总结这些,主要原因是公司项目使用的是方法一,在我使用的时候,在部分版本低的模拟器中,text文字移动的时候有卡顿现象,找了一圈没有招到有用的信息。看到一个大佬 的代码需要更改 TextView 源码,小白一个,改了后不知道怎么使用,先贴出来吧,万一后面会用了,又找不到了。

原文链接

前段时间在维护Android系统的时候,遇到TextView,一旦添加了跑马灯后,UI就出现卡顿的情况。针对这个情况,对Android TextView的源码进行了一系列的分析和测试,最终找到了原因,如下:

在构造函数里面的一段代码
修改前:

public TextView(Context context, AttributeSet attrs, int defStyle) {
    ......
      switch (ellipsize) {
            case 1:
                setEllipsize(TextUtils.TruncateAt.START);
                break;
            case 2:
                setEllipsize(TextUtils.TruncateAt.MIDDLE);
                break;
            case 3:
                setEllipsize(TextUtils.TruncateAt.END);
                break;
            case 4:
                if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
                    setHorizontalFadingEdgeEnabled(true);
                    mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
                } else {
                    setHorizontalFadingEdgeEnabled(false);
                    mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
                }
                setEllipsize(TextUtils.TruncateAt.MARQUEE);
                break;
        }
    ......
}

修改后:

public TextView(Context context, AttributeSet attrs, int defStyle) {
    ......
      switch (ellipsize) {
            case 1:
                setEllipsize(TextUtils.TruncateAt.START);
                break;
            case 2:
                setEllipsize(TextUtils.TruncateAt.MIDDLE);
                break;
            case 3:
                setEllipsize(TextUtils.TruncateAt.END);
                break;
            case 4:
                /*if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
                    setHorizontalFadingEdgeEnabled(true);
                    mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
                } else {
                    setHorizontalFadingEdgeEnabled(false);
                    mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
                }*/
                setEllipsize(TextUtils.TruncateAt.MARQUEE);
                break;
        }
    ......
}

总结:
注释掉的地方的功能是TextView左右的阴影部分,注释掉后,跑马灯左右没有阴影效果,所以也不算是根治此问题,只能够说变相的优化了。但是我对比过要不要阴影的效果,不特别去注意的话是看不出有什么不同的。Android4.4绘制UI感觉是有些问问题,在Server使用WindowManager画UI的时候,明显比Android5.0以上卡很多,具体原因没有过多去跟了,有继续往下跟的大神,找到原因了的话,可以分享一下,感谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值