使用定时器实现简单的字体闪烁并实现字体跑马灯

时间:2022-04-01 04:14:42

最近在项目中有一个小需求,实现TextView字体颜色闪烁,来表示某个功能正在运行,从网上扒了一些资料,发现实现这种效果有两种方法,一是用动画,二是用定时器

在这里,我就介绍一下用定时器如何实现字体闪烁

package com.example.timer;

import java.util.Timer;
import java.util.TimerTask;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.graphics.Color;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

private Timer timer;
private TimerTask task;
private TextView tv;
private int i;
private boolean change;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
if(change){
change = false;
tv.setTextColor(Color.BLUE);
}else{
change = true;
tv.setTextColor(Color.TRANSPARENT);
}

};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
}


public void start(View v){
i = i+1;
if(timer!=null && task!=null){
timer.cancel();
task.cancel();
}

if(timer==null){
Log.e("TAG", "Timer为空");
}
timer = new Timer();
task = new TimerTask() {

@Override
public void run() {

Log.e("TAG", "我是定时器"+i);
handler.sendEmptyMessage(1);

}
};

timer.schedule(task, 1, 300);
}
public void stop(View v){
if(timer!=null && task!=null){
timer.cancel();
task.cancel();
}
}
}

好了,以上就是实现字体闪烁的全部代码,简单吧?就是开了个定时器定时发送消息,让handle去处理字体的颜色变化,我们需要注意的是

要是定时器存在多次启动的现象,一定要在开启定时器之前先关闭之前的定时器。



ok,接下来我们再看看怎么实现字体跑马灯吧,其实这个是老技术了,但我做完字体闪烁后,在此基础上又把跑马灯加在了上面,发现效果还是不错的。

总的来说,实现字体跑马灯我搜集到了两种方法,一种是可控的,一种是不可控的,不可控的我们用的较多,先看看不可控的吧,所谓可控制就是控制

文字滚动速度、样式、字体等属性。


TextView实现文字滚动需要以下几个要点:
1.文字长度长于可显示范围:android:singleLine="true"
2.设置可滚到,或显示样式:android:ellipsize="marquee"

3.TextView只有在获取焦点后才会滚动显示隐藏文字,因此需要在包中新建一个类,继承TextView。重写isFocused方法,这个方法默认行为是,

如果TextView获得焦点,方法返回true,失去焦点则返回false。跑马灯效果估计也是用这个方法判断是否获得焦点,所以把它的返回值始终设置为true。

package com.example.timer;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;

public class AbTextView extends TextView{

public AbTextView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public AbTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

public AbTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

@Override
public boolean isFocused() {
// TODO Auto-generated method stub
return true;
}

}

在布局XML中加入自定义的控件

 <com.example.timer.AbTextView
android:id="@+id/tv"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="#008001" />

ellipsize属性
设置当文字过长时,该控件该如何显示。有如下值设置:”start”—–省略号显示在开头;”end”——省略号显示在结尾;”middle”—-省略号显示在中间;”marquee” ——以跑马灯的方式显示(动画横向移动)

marqueeRepeatLimit属性
在ellipsize指定marquee的情况下,设置重复滚动的次数,当设置为marquee_forever时表示无限次。

focusable属性
自己猜测的,应该是能否获得焦点,同样focusableInTouchMode应该是滑动时能否获得焦点。


第二种实现方法,可以控制

控件类:AutoScrollTextView 继承了TextView并做了一些修改,实现了宽度的判断,文本自动滚动及开始和停止滚动等功能

package com.example.timer;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.Display;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.TextView;

public class AutoScrollTextView extends TextView implements OnClickListener {

public final static String TAG = AutoScrollTextView.class.getSimpleName();

private float textLength = 0f;// 文本长度
private float viewWidth = 0f;
private float step = 0f;// 文字的横坐标
private float y = 0f;// 文字的纵坐标
private float temp_view_plus_text_length = 0.0f;// 用于计算的临时变量
private float temp_view_plus_two_text_length = 0.0f;// 用于计算的临时变量
public boolean isStarting = false;// 是否开始滚动
private Paint paint = null;// 绘图样式
private String text = "";// 文本内容

public AutoScrollTextView(Context context) {
super(context);
initView();
}

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

public AutoScrollTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}

private void initView() {
setOnClickListener(this);
}

public void init(WindowManager windowManager) {
paint = getPaint();
text = getText().toString();
textLength = paint.measureText(text);
viewWidth = getWidth();
if (viewWidth == 0) {
if (windowManager != null) {
Display display = windowManager.getDefaultDisplay();
viewWidth = display.getWidth();
}
}
step = textLength;
temp_view_plus_text_length = viewWidth + textLength;
temp_view_plus_two_text_length = viewWidth + textLength * 2;
y = getTextSize() + getPaddingTop();
}

@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);

ss.step = step;
ss.isStarting = isStarting;

return ss;

}

@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());

step = ss.step;
isStarting = ss.isStarting;
}

public static class SavedState extends BaseSavedState {
public boolean isStarting = false;
public float step = 0.0f;

SavedState(Parcelable superState) {
super(superState);
}

@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeBooleanArray(new boolean[] { isStarting });
out.writeFloat(step);
}

public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {

public SavedState[] newArray(int size) {
return new SavedState[size];
}

@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
};

private SavedState(Parcel in) {
super(in);
boolean[] b = null;
in.readBooleanArray(b);
if (b != null && b.length > 0)
isStarting = b[0];
step = in.readFloat();
}
}

public void startScroll() {
isStarting = true;
invalidate();
}

public void stopScroll() {
isStarting = false;
invalidate();
}

@Override
public void onDraw(Canvas canvas) {
canvas.drawText(text, temp_view_plus_text_length - step, y, paint);
if (!isStarting) {
return;
}
step += 0.5;// 0.5为文字滚动速度。
if (step > temp_view_plus_two_text_length)
step = textLength;
invalidate();
}

@Override
public void onClick(View v) {
if (isStarting)
stopScroll();
else
startScroll();

}

}

在布局XML中的使用

<com.example.timer.AutoScrollTextView
android:id="@+id/tv2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#EEE"
android:inputType="text"
android:text="哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"
android:textColor="#000"
android:textSize="20sp" />

Activity中使用的方法

  tv2 = (AutoScrollTextView) findViewById(R.id.tv2);
        tv2.init(getWindowManager());
        tv2.startScroll();

注:如果想改变跑马灯的文字内容或者文字效果,则在调用完setText方法之后,需要再调用一下init方法,重新进行初始化和相关参数的计算。

在这里有个小问题,在使用自定义的控件时,不要再用TextView,而是使用继承TextView的那个类