Android之TextView跑马灯实现

在平时的Android开发中,有时根据项目经理的设计,我们需要实现一些”花里胡哨”的功能,本篇博文就是介绍其中一个很俗气的效果– 跑马灯

1. 简单实现及问题

1.1 简单实现:

其实在Android开发里,TextView中实现跑马灯非常简单,简单到只需要在xml文件中设置几行属性代码即可,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<TextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#FF0000"
android:layout_marginLeft="20dp"
android:singleLine="true"
android:text="在平时的Android开发中,版本更新是避免不了的事情(利用跨平台热更新另算),此博文主要介绍一下版本更新时候显示app下载进度以及下载完毕后直接跳转安装界面的操作."
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
/>

其实只要加上四行代码,即可实现:

1
2
3
4
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"

是不是非常简单?看下效果:

1.2 出现问题及原因分析:

但是,上面代码里只显示了一个,如果需要加两个甚至更多呢?

测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical"
tools:context=".activity.HomeActivity">


<TextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#FF0000"
android:layout_marginLeft="20dp"
android:singleLine="true"
android:text="在平时的Android开发中,版本更新是避免不了的事情(利用跨平台热更新另算),此博文主要介绍一下版本更新时候显示app下载进度以及下载完毕后直接跳转安装界面的操作."
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
/>

<TextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#FF0000"
android:layout_marginLeft="20dp"
android:singleLine="true"
android:text="在平时的Android开发中,版本更新是避免不了的事情(利用跨平台热更新另算),此博文主要介绍一下版本更新时候显示app下载进度以及下载完毕后直接跳转安装界面的操作."
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
/>
</LinearLayout>

只是在xml布局文件中增加一个一模一样的TextView,然后再次运行程序:

哎呀我去,怎么只有一个动了?另外一个没有反应?

其实原理很简单,设置跑马灯效果需要控件获取焦点而且是强制获取,而在第二份代码中,有两个控件强制获取焦点就造成了一个跑另外一个不跑了.

那么如何解决这个问题呢? 答案就是: 自定义控件

1.3 解决方法:

1.3.1 自定义控件(继承于TextView):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;

/**
* Time: 2018/12/28
* Author: CJ
* Description:
**/
public class HomeTextView extends TextView {

//在代码中使用的时候调用
public HomeTextView(Context context) {
this(context, null);
}

//在布局文件中使用的时候调用的方法
//布局文件中的控件最终都会通过反射的形式,转化成代码,在转化的代码中new的时候调用的方法
//控件的所有属性都会保存到AttributeSet
public HomeTextView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}

//在控件的内部让两个参数的构造函数调用的
public HomeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

// 重写系统的TextView,让其默认获得焦点
@Override
public boolean isFocused() {
return true;
}
}
1.3.2 修改xml控件布局:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<com.xlgz520.applicationdemo.views.HomeTextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#FF0000"
android:layout_marginLeft="20dp"
android:singleLine="true"
android:text="在平时的Android开发中,版本更新是避免不了的事情(利用跨平台热更新另算),此博文主要介绍一下版本更新时候显示app下载进度以及下载完毕后直接跳转安装界面的操作."
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"/>

<com.xlgz520.applicationdemo.views.HomeTextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#FF0000"
android:layout_marginLeft="20dp"
android:singleLine="true"
android:text="在平时的Android开发中,版本更新是避免不了的事情(利用跨平台热更新另算),此博文主要介绍一下版本更新时候显示app下载进度以及下载完毕后直接跳转安装界面的操作."
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"/>
1.3.3 效果图:

可以看出,两个都可以动了.

2. 拓展与延伸

在平时开发中,其实控件这块不光在xml中要进行布局,有时候也需要在java文件中进行一些属性设置,而且这种情况在自定义控件其实还是比较常见的,那么试一下在自定义控件中设置属性代码是否有一样的效果呢?

2.1 修改xml代码

1
2
3
4
5
6
7
8
<com.xlgz520.applicationdemo.views.HomeTextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#FF0000"
android:layout_marginLeft="20dp"
android:text="在平时的Android开发中,版本更新是避免不了的事情(利用跨平台热更新另算),此博文主要介绍一下版本更新时候显示app下载进度以及下载完毕后直接跳转安装界面的操作."
/>

2.2 修改自定义控件java代码:

在第三个(三个参数那个)的构造方法中,增加如下代码

1
2
3
4
5
6
7
8
9
//在控件的内部让两个参数的构造函数调用的
public HomeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

setSingleLine();//使用代码设置单行显示
setEllipsize(TextUtils.TruncateAt.MARQUEE);//使用代码设置滚动操作
setFocusableInTouchMode(true);//使用代码设置触摸获取焦点
setMarqueeRepeatLimit(-1);//设置滚动次数 -1代表无限
}

2.3 运行效果:

2.4 优点与问题:

  • 优点:

    如果项目中有很多地方使用TextView的跑马灯,那么如果按照xml的写法,会有很多的重复代码,因为每个TextView都需要设置上文的四个属性,而如果在自定义构造方法里实现的话,其他地方只需要设置TextView其他的属性即可.

  • 问题:

    ① 修改xml布局:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <com.xlgz520.applicationdemo.views.HomeTextView
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:textSize="20sp"
    android:textColor="#FF0000"
    android:layout_marginLeft="20dp"
    android:text="在平时的Android开发中,版本更新是避免不了的事情(利用跨平台热更新另算),此博文主要介绍一下版本更新时候显示app下载进度以及下载完毕后直接跳转安装界面的操作."
    />

    <EditText
    android:layout_marginTop="15dp"
    android:layout_marginLeft="20dp"
    android:layout_width="300dp"
    android:layout_height="wrap_content" />

    只是在自定义TextView下方增加了一个EditText,然后我们运行一下程序:

    看出问题了么?

    当我点击EditText的时候,跑马灯停止了,那么如何解决呢?

    ② 解决方法:

    修改自定义TextView的代码,重写父类的一个方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //焦点切换调用的方法
    //focused : 焦点是否释放
    //direction : 焦点移动的方向
    //previouslyFocusedRect : 焦点从哪个控件过来
    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
    //当焦点被抢夺的时候,不能抢夺textview的焦点
    //如果焦点没有被抢夺,调用系统的方法,帮我们保留焦点
    //如果焦点被抢夺了,禁止调用系统的方法,禁止系统移除焦点
    if (focused) {
    super.onFocusChanged(focused, direction, previouslyFocusedRect);
    }
    }

    再次查看一下效果:

-------------本文结束 感谢您的阅读-------------

本文标题:Android之TextView跑马灯实现

文章作者:西湖长剑

发布时间:2018年12月28日 - 17:12

最后更新:2018年12月28日 - 18:12

原始链接:http://www.xlgz520.com/2018/12/28/Android之TextView跑马灯实现/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%