在平时的Android开发中,有时根据项目经理的设计,我们需要实现一些"花里胡哨"的功能,本篇博文就是介绍其中一个很俗气的效果– 跑马灯
1. 简单实现及问题
1.1 简单实现:
其实在Android开发里,TextView中实现跑马灯非常简单,简单到只需要在xml文件中设置几行属性代码即可,如下:
<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"
/>
其实只要加上四行代码,即可实现:
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
是不是非常简单?看下效果:
1.2 出现问题及原因分析:
但是,上面代码里只显示了一个,如果需要加两个甚至更多呢?
测试一下:
<?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):
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控件布局:
<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代码
<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代码:
在第三个(三个参数那个)的构造方法中,增加如下代码
//在控件的内部让两个参数的构造函数调用的
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布局:
<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的代码,重写父类的一个方法:
//焦点切换调用的方法 //focused : 焦点是否释放 //direction : 焦点移动的方向 //previouslyFocusedRect : 焦点从哪个控件过来 @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { //当焦点被抢夺的时候,不能抢夺textview的焦点 //如果焦点没有被抢夺,调用系统的方法,帮我们保留焦点 //如果焦点被抢夺了,禁止调用系统的方法,禁止系统移除焦点 if (focused) { super.onFocusChanged(focused, direction, previouslyFocusedRect); } }
再次查看一下效果: