Android(安卓)自定义View之MarqueeText,实现跑马灯效果
1. 背景
需要做一个跑马灯效果的文字展示,本方案更适用于开发机顶盒应用的同学们。
2. 需求
2.1 展示书籍的页面,文字过多的时候需要折叠
2.2 当焦点在此书籍上的时候,需要将折叠的文字滚动起来
3. 需求剖析
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. 解决思路
4.1 我需要有一个不需要焦点,仍然能够展现跑马灯的控件,避免与recycleView之间的冲突
4.2 同时我这个控件也需要有TextView原有的功能,便于我在简单情况下使用跑马灯效果
5. 代码
5.1 布局文件 resource_item.xml
<?xml version="1.0" encoding="utf-8"?>
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); // 展示焦点效果的viewMarqueeText 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. 问题
6.1 在布局文件中,设置了singleline = true,结果,调用 startScroll 时无法滚动。
6.2 在滚动之前必须将本TextView的text设置为空串,否则将出现两个文本。
6.3 先存疑,以后找到结果了再贴上去
7. 参考文档:
7.1 http://blog.csdn.net/u014702653/article/details/51985821
7.2 https://www.cnblogs.com/hyhy904/p/11329139.html
更多相关文章
- Android之玩转View
- Android(安卓)如何让EditText不自动获取焦点
- Android核心分析(14)------ Android(安卓)GWES之输入系统
- Android(安卓)ClickableSpan 应用实例
- Android—文字轮播
- Android自由选择TextView的文字
- android 控件 NumberPicker 简单使用
- Android(安卓)自定义控件 按钮滚动选择
- Android输入法扩展之外接键盘中文输入