需要做一个跑马灯效果的文字展示,本方案更适用于开发机顶盒应用的同学们。
2.1 展示书籍的页面,文字过多的时候需要折叠
2.2 当焦点在此书籍上的时候,需要将折叠的文字滚动起来
3.1 正常情况下使用跑马灯,只需要设置Textview的以下属性即可
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
设置完成之后,当这个TextView有焦点的时候,且文字过长的时候,就会展现跑马灯效果,注意当TextView请求焦点的时候,跑马灯效果才展示,否则无效。
3.2 但是在机顶盒上,app是需要使用焦点效果来展示你所选的当前item,也就是通常所说的“落焦”。
3.3 这时候使用跑马灯的时候会产生两个问题
问题一:我的item是在一个recycleView当中,充当跑马灯的TextView只是recycleView中子view 中的子view,我外层的布局需要拿到焦点,才能在recycleView中正常移动。
问题二:即使我将TextView设置请求焦点,那么当我从当前的item向上滑动的时候,recycleView就不再认为我是在他的子view间移动,recycleView的翻页效果会有问题,就是当你上一行展示了一半,但是我焦点已经在上一行了,但是上一行却没有展示完全。如下图所示:
4.1 我需要有一个不需要焦点,仍然能够展现跑马灯的控件,避免与recycleView之间的冲突
4.2 同时我这个控件也需要有TextView原有的功能,便于我在简单情况下使用跑马灯效果
5.1 布局文件 resource_item.xml
5.2 自定义view
package plat.skytv.main.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatTextView;
import plat.skytv.main.util.PLog;
public class MarqueeText extends AppCompatTextView implements Runnable {
private int xLocation;// 当前滚动的位置
private boolean isStop = false;
private int textWidth;
private boolean isMeasure = false;
private float speed = 1; // 移动速度
private String string; // 需要绘制得文字
public MarqueeText(Context context) {
super(context);
}
public MarqueeText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MarqueeText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
float textHeight;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isMeasure) {// 文字宽度只需获取一次就可以了
getTextWidth();
textHeight = getTextHeight(getPaint());
isMeasure = true;
string = this.getText().toString();
}
canvas.drawText(string, xLocation, getHeight() / 2 + textHeight / 2, getPaint());
}
/**
* http://blog.csdn.net/u014702653/article/details/51985821
* 详细解说了
*
* @param
* @return
*/
private float getTextHeight(Paint paint) {
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
return Math.abs((fontMetrics.bottom - fontMetrics.top)) / 2;
}
/**
* 获取文字宽度
*/
private void getTextWidth() {
Paint paint = this.getPaint();
string = this.getText().toString();
PLog.e("getTextWidth str = " + string);
textWidth = (int) paint.measureText(string);
}
@Override
public void run() {
xLocation -= speed;// 滚动速度
if (isStop) { // 停止滚动后恢复初始状态
xLocation = 0;
return;
}
if (textWidth <= (-xLocation)) {
//也就是说文字已经到头了
xLocation = getWidth();
}
postInvalidate();
postDelayed(this, 10);
}
// 开始滚动 针对不能请求焦点的情况
public void startScroll() {
if (textWidth <= this.getWidth()) { // 文字长度 <= view长度,不需要滚动
return;
}
setText("");// 需要设置空 否则展示两个文字
setSingleLine(false); // 需要设置false,否则无法滚动
isStop = false;
this.removeCallbacks(this);
post(this);
}
// 停止滚动
public void stopScroll() {
isStop = true;
setText(string);
setSingleLine(true);
}
}
5.3 使用
在recycleView绑定数据的时候设置焦点变化监听
ImageView mItemFocus = itemView.findViewById(R.id.item_focus); // 展示焦点效果的view
MarqueeText mItemName = itemView.findViewById(R.id.item_name);
mItemFocus.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
mItemName.startScroll();
} else {
mItemName.stopScroll();
}
}
});
6.1 在布局文件中,设置了singleline = true,结果,调用 startScroll 时无法滚动。
6.2 在滚动之前必须将本TextView的text设置为空串,否则将出现两个文本。
6.3 先存疑,以后找到结果了再贴上去
7.1 http://blog.csdn.net/u014702653/article/details/51985821
7.2 https://www.cnblogs.com/hyhy904/p/11329139.html