欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > Android -- [SelfView] 自定义多动画效果文本显示器

Android -- [SelfView] 自定义多动画效果文本显示器

2024/10/23 15:19:22 来源:https://blog.csdn.net/weixin_43738911/article/details/143052564  浏览:    关键词:Android -- [SelfView] 自定义多动画效果文本显示器

Android – [SelfView] 自定义多动画效果文本显示器

 PS: 滚动文本,不限长度;* 显示效果:* 1. 静态显示(左对齐、居中、右对齐)* 2. 向左滚动;(纵向居中)* 3. 向上滚动(一条一条);* 4. 向上滚动(整体);* * 可控接口:* 1. 字体大小、颜色;* 2. 背景颜色(可透明);* 3. 滚动速度(1-20);* 4. 修饰符(斜体、粗体、下划线、删除线);

效果:

在这里插入图片描述

1. 引用:

 <com.nepalese.harinetest.player.VirgoScrollTextViewandroid:id="@+id/vst_left"android:layout_width="match_parent"android:layout_height="40dp"android:layout_margin="10dp"/>
private VirgoScrollTextView scrollTextLeft;
private final String CONT ="《将进酒》 -- 李白\n" +"君不见黄河之水天上来,奔流到海不复回。\n" +"君不见高堂明镜悲白发,朝如青丝暮成雪。\n" +"人生得意须尽欢,莫使金樽空对月。\n" +"天生我材必有用,千金散尽还复来。\n" +"烹羊宰牛且为乐,会须一饮三百杯。\n" +"岑夫子,丹丘生,将进酒,杯莫停。\n" +"与君歌一曲,请君为我倾耳听。\n" +"钟鼓馔玉不足贵,但愿长醉不愿醒。\n" +"古来圣贤皆寂寞,惟有饮者留其名。\n" +"陈王昔时宴平乐,斗酒十千恣欢谑。\n" +"主人何为言少钱,径须沽取对君酌。\n" +"五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。";scrollTextLeft = findViewById(R.id.vst_left);//scrollTextLeft.setBgColr(Color.BLACK);//背景色:默认透明
//scrollTextLeft.setTextColor(Color.WHITE);//字体颜色:默认黑色
scrollTextLeft.setAnimMode(VirgoScrollTextView.MODE_SCROLL_LEFT);//动画效果:默认静态显示
scrollTextLeft.setTextCont(CONT);//设置文本,在字体样式设置之后
scrollTextLeft.startRolling();// 退出时调用
// scrollTextLeft.release(); 

2. VirgoScrollTextView.java

package com.nepalese.harinetest.player;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.text.Html;
import android.text.Layout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;import androidx.annotation.Nullable;import com.nepalese.harinetest.config.Constants;import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;/*** Created by Administrator on 2024/10/15.* Usage: 滚动文本,不限长度* 显示效果:* 1. 静态显示(左对齐、居中、右对齐)* 2. 向左滚动;(纵向居中)* 3. 向上滚动(一条一条);* 4. 向上滚动(整体);* <p>* 可控接口:* 1. 字体大小、颜色;* 2. 背景颜色(可透明);* 3. 滚动速度(1-20);* 4. 修饰符(斜体、粗体、下划线、删除线);*/public class VirgoScrollTextView extends SurfaceView implements SurfaceHolder.Callback {private static final String TAG = "VirgoScrollTextView";//速度: 1--20;private static final int MAX_SPEED = 20;private static final int MIN_SPEED = 1;public static final int MODE_STATIC_DRAW = 1;//无动画静态显示public static final int MODE_SCROLL_UP = 2;//纵向向上滚动public static final int MODE_SCROLL_UP_ALL = 3;//纵向向上滚动(整体)public static final int MODE_SCROLL_LEFT = 4;//横向向左滚动public static final int ALIGN_LEFT = 1;//左对齐public static final int ALIGN_CENTER = 2;//居中public static final int ALIGN_RIGHT = 3;//右对齐protected static final String TEXT_SPACE = "\u0020";protected static final String TXT_ENTER = "\u3000\u3000";//半角空格(英文符号)\u0020,代码中常用的;//全角空格(中文符号)\u3000,中文文章中使用;//默认样式protected static final int DEF_BG = Color.TRANSPARENT;protected static final int DEF_TEXT_COLOR = Color.BLACK;private static final int DEF_SPEED = 10;//默认速度protected static final float DEF_TEXT_SIZE = 30f;//pxprivate static final float SPEED_RATIO = 0.30f;//速度使用率private static final long DELAY_FLASH = 40L;//刷新频率private Paint paint;//画笔private List<BaseText> textList;//分块文本列表private SurfaceHolder surfaceHolder;//绘制画布载体private DrawRun runnable;private Handler drawHandler;//绘制线程protected int width, height;//控件宽高private String cont;//文本内容private int animMode;//动画模式private int alignMode;//对齐方式private int bgColr;//背景色private int pageIndex;//分页播放页private int padding;//内偏移private float textSpeed;//滚动速度private float lineSpace;//行间隔private float textSize;//字体大小 pxprivate float textHeight;//字体高度,与字体大小关联private float startY;//与字体高度绑定private float offset;//已滚动值private float allHeight;//竖向滚动文本总高度private boolean isHolderReady;// surfaceCreatedpublic VirgoScrollTextView(Context context) {this(context, null);}public VirgoScrollTextView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public VirgoScrollTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {surfaceHolder = getHolder();surfaceHolder.addCallback(this);setZOrderOnTop(true);//使surfaceview放到最顶层 看是否需要surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);//使窗口支持透明度paint = new Paint();paint.setAntiAlias(true);paint.setStyle(Paint.Style.FILL);paint.setFakeBoldText(false);textList = new ArrayList<>();setDefault();}private void setDefault() {width = 0;height = 0;animMode = MODE_STATIC_DRAW;offset = 0f;pageIndex = 0;padding = 0;lineSpace = 1.3f;alignMode = ALIGN_CENTER;allHeight = 0;if (runnable != null) {runnable.isRolling = false;}isHolderReady = false;setBgColr(DEF_BG);setTextColor(DEF_TEXT_COLOR);setTextSize(DEF_TEXT_SIZE);setTextSpeed(DEF_SPEED);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);if (w > 0 && h > 0) {initLayout(w, h);setTextCont(this.cont);}}///*** 设置|更改布局时调用** @param width  容器宽* @param height 容器高*/private void initLayout(int width, int height) {this.width = width;this.height = height;this.allHeight = height;Log.d(TAG, "initLayout: " + width + " - " + height);}/*** 停止滚动(调用继续滚动不影响滚动位置)*/public void stopRolling() {if (drawHandler != null) {if (runnable != null) {runnable.isRolling = false;runnable = null;}Looper mLooper = drawHandler.getLooper();if (Build.VERSION.SDK_INT >= 18) {mLooper.quitSafely();} else {mLooper.quit();}drawHandler = null;}}/*** 开始|继续滚动* 文本、画布校验*/public void startRolling() {if (!isHolderReady) {Log.w(TAG, "还没准备好");stopRolling();return;}if (textList == null || textList.isEmpty()) {Log.w(TAG, "文本为空");stopRolling();return;}if (animMode == MODE_STATIC_DRAW) {stopRolling();drawStatic();} else {if (drawHandler == null) {HandlerThread mHandlerThread = new HandlerThread("scheduleText_" + System.currentTimeMillis());mHandlerThread.start();drawHandler = new Handler(mHandlerThread.getLooper());}if (runnable != null && !runnable.isRolling) {runnable.isRolling = true;drawHandler.post(runnable);} else if (runnable == null) {runnable = new DrawRun();runnable.isRolling = true;drawHandler.post(runnable);}}}/*** 设置需显示的文本* 在设置完字体样式之后调用** @param cont str*/public void setTextCont(String cont) {if (TextUtils.isEmpty(cont)) {//todo 暂不处理return;}if (width <= 0) {//未设置宽度this.cont = cont;return;}//停止滚动stopRolling();//处理文本,缓存textList.clear();textList.addAll(parseCont(cont));//重置offset = 0f;pageIndex = 0;if (animMode == MODE_SCROLL_LEFT) {startY = height / 2f + textHeight / 3;} else {if (textList.size() > 0) {allHeight = textList.size() * (textHeight + lineSpace);} else {allHeight = height;}offset = -height;//从底部开始}}/*** 设置背景颜色** @param bgColr def white*/public void setBgColr(int bgColr) {this.bgColr = bgColr;}/*** 设置字体颜色** @param textColor def black*/public void setTextColor(int textColor) {if (paint != null) {paint.setColor(textColor);}}/*** 设置滚动速度** @param textSpeed [1,20] def 10*/public void setTextSpeed(int textSpeed) {if (textSpeed > MAX_SPEED || textSpeed < MIN_SPEED) {//不在支持范围,设为默认值this.textSpeed = DEF_SPEED * SPEED_RATIO;} else {this.textSpeed = textSpeed * SPEED_RATIO;}}/*** 设置字体大小** @param textSize px def 25*/public void setTextSize(float textSize) {if (this.textSize == textSize) {//相同return;}this.textSize = textSize;if (paint != null) {paint.setTextSize(textSize);}//获取字体高度this.textHeight = getContHeight2();}public void setAnimMode(int animMode) {this.animMode = animMode;}public void setAlignMode(int alignMode) {this.alignMode = alignMode;}public void setPadding(int padding) {this.padding = padding;}public void setLineSpace(float lineSpace) {this.lineSpace = lineSpace;}//修饰: 下划线,删除线public void setTextDec(String dec) {if (TextUtils.isEmpty(dec) || dec.equals(Constants.DEC.NONE)) {if (paint != null) {paint.setFlags(Paint.ANTI_ALIAS_FLAG);}return;}if (dec.equals(Constants.DEC.DELETE)) {if (paint != null) {paint.setFlags(Paint.STRIKE_THRU_TEXT_FLAG);//删除线}} else if (dec.equals(Constants.DEC.UNDERLINE)) {if (paint != null) {paint.setFlags(Paint.UNDERLINE_TEXT_FLAG);//下划线}}}//斜体public void setTextItalic(boolean isItalic) {if (paint != null) {paint.setTextSkewX(isItalic ? -0.25f : 0);}}//粗体public void setTextBold(boolean isBold) {if (paint != null) {paint.setFakeBoldText(isBold);// 1 + 1}}/*** 注销*/public void release() {stopRolling();if (textList != null) {textList.clear();textList = null;}}/*** 重置至初始值*/public void reset() {stopRolling();textList.clear();setDefault();}///surfaceHolder.lockCanvas(); 不能连续调用//静态显示private void drawStatic() {Log.d(TAG, "drawStatic: ");if (!textList.isEmpty() && surfaceHolder != null) {//多行就多行画Canvas canvas = surfaceHolder.lockCanvas();//支持透明背景canvas.drawColor(bgColr, PorterDuff.Mode.CLEAR);canvas.drawColor(bgColr);for (int i = 0; i < textList.size(); i++) {BaseText baseText = textList.get(i);float startX;switch (alignMode) {case ALIGN_LEFT:startX = padding;break;case ALIGN_RIGHT:startX = width - baseText.getLenght() - padding;break;case ALIGN_CENTER:default:startX = (width - baseText.getLenght()) / 2;break;}if (i > 0) {startY = padding + textHeight + i * (textHeight + lineSpace);} else {startY = padding + textHeight;}canvas.drawText(baseText.getCont(), startX, startY, paint);}surfaceHolder.unlockCanvasAndPost(canvas);}}//多页,横向滚动, 尽量减少计算private void drawHorizontal() {//三条一起绘制if (!textList.isEmpty() && surfaceHolder != null) {offset += textSpeed;int page = textList.size();//获取当前需绘制的三条文本(可空)BaseText baseText1, baseText2, baseText3;if (pageIndex - 2 < 0) {//第一格为空baseText1 = new BaseText("", 0);} else {baseText1 = textList.get((pageIndex - 2) % page);}if (pageIndex - 1 < 0) {//第二格为空baseText2 = new BaseText("", 0);} else {baseText2 = textList.get((pageIndex - 1) % page);}baseText3 = textList.get(pageIndex % page);String curText = baseText1.getCont() + baseText2.getCont() + baseText3.getCont();Canvas canvas = surfaceHolder.lockCanvas();if (canvas != null) {canvas.drawColor(bgColr, PorterDuff.Mode.CLEAR);canvas.drawColor(bgColr);canvas.drawText(curText, width - offset, startY, paint);surfaceHolder.unlockCanvasAndPost(canvas);}//当前三条文本实际长度, 严格按实际文本长度来控制if (offset >= (baseText1.getLenght() + baseText2.getLenght() + baseText3.getLenght())) {//最后一条触底,需追加一条pageIndex++;//重置if (pageIndex >= page * 2) {pageIndex -= page;}offset -= baseText1.getLenght();}}}//纵向滚动:一条一条往上private void drawVertical() {//两条一起绘制if (!textList.isEmpty() && surfaceHolder != null) {offset += textSpeed;int page = textList.size();//获取当前需绘制的两条文本BaseText baseText1, baseText2;if (page < 2) {//仅一行baseText1 = textList.get(0);baseText2 = textList.get(0);} else {baseText1 = textList.get((pageIndex) % page);baseText2 = textList.get((pageIndex + 1) % page);}Canvas canvas = surfaceHolder.lockCanvas();if (canvas != null) {canvas.drawColor(bgColr, PorterDuff.Mode.CLEAR);canvas.drawColor(bgColr);float startX;//第一行switch (alignMode) {case ALIGN_LEFT:startX = padding;break;case ALIGN_RIGHT:startX = width - baseText1.getLenght() - padding;break;case ALIGN_CENTER:default:startX = (width - baseText1.getLenght()) / 2;break;}startY = padding + textHeight - offset;canvas.drawText(baseText1.getCont(), startX, startY, paint);//第二行switch (alignMode) {case ALIGN_LEFT:startX = padding;break;case ALIGN_RIGHT:startX = width - baseText2.getLenght() - padding;break;case ALIGN_CENTER:default:startX = (width - baseText2.getLenght()) / 2;break;}startY = padding + textHeight + height - offset;canvas.drawText(baseText2.getCont(), startX, startY, paint);surfaceHolder.unlockCanvasAndPost(canvas);}if (offset >= height) {//最后一条触底,需追加一条pageIndex++;//重置if (pageIndex >= page * 2) {pageIndex -= page;}offset = 0;}}}//作为一个整体往上滚动private void drawVerticalAll() {if (!textList.isEmpty() && surfaceHolder != null) {offset += textSpeed;Canvas canvas = surfaceHolder.lockCanvas();canvas.drawColor(bgColr, PorterDuff.Mode.CLEAR);canvas.drawColor(bgColr);for (int i = 0; i < textList.size(); i++) {BaseText baseText = textList.get(i);float startX;switch (alignMode) {case ALIGN_LEFT:startX = padding;break;case ALIGN_RIGHT:startX = width - baseText.getLenght() - padding;break;case ALIGN_CENTER:default:startX = (width - baseText.getLenght()) / 2;break;}if (i > 0) {startY = padding + textHeight + i * (textHeight + lineSpace) - offset;} else {startY = padding + textHeight - offset;}canvas.drawText(baseText.getCont(), startX, startY, paint);}surfaceHolder.unlockCanvasAndPost(canvas);//放完一遍,从底部出来//第一遍if (offset >= allHeight) {//从底部进入offset = -height;}}}private class DrawRun implements Runnable {private boolean isRolling;//滚动?@Overridepublic void run() {while (isRolling) {if (textList == null || textList.isEmpty()) {isRolling = false;break;}try {if (animMode == MODE_SCROLL_LEFT) {drawHorizontal();} else if (animMode == MODE_SCROLL_UP) {drawVertical();} else if (animMode == MODE_SCROLL_UP_ALL) {drawVerticalAll();} else {isRolling = false;break;}} catch (Throwable e) {e.printStackTrace();}try {Thread.sleep(DELAY_FLASH);} catch (InterruptedException e) {
//                    e.printStackTrace();}}}}///文本处理////*** 1. 静态|竖向滚动(整体):换行符 & 溢出 -> 换行;* 2. 横向滚动:替换换行符,一行显示;* 3. 竖向滚动(一条一条):分段;** @param cont cont*/private List<BaseText> parseCont(String cont) {List<BaseText> tempList = new ArrayList<>();//横向滚动|竖向滚动(一条一条)if (animMode == MODE_SCROLL_LEFT || animMode == MODE_SCROLL_UP) {//1. 替换空格、换行if (animMode == MODE_SCROLL_UP) {cont = cont.replaceAll(" ", TEXT_SPACE).replaceAll("(\r\n|\n|\\\\n)", "").replaceAll("\\\\", "");} else {cont = cont.replaceAll(" ", TEXT_SPACE).replaceAll("(\r\n|\n|\\\\n)", TXT_ENTER).replaceAll("\\\\", "");}cont = cont.replaceAll(" ", TEXT_SPACE).replaceAll("(\r\n|\n|\\\\n)", TXT_ENTER).replaceAll("\\\\", "");//格式化try {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {cont = Html.fromHtml(cont, Html.FROM_HTML_MODE_LEGACY).toString();} else {cont = Html.fromHtml(cont).toString();}} catch (Throwable e) {//字符无法转编码}//2. 获取文本高、总长度float allLen = getContWidth2(cont);Log.d(TAG, "控件宽: " + width + ", 文本总长度: " + allLen + ", 高度: " + textHeight + ", len: " + cont.length());//短文本自动补加if (animMode == MODE_SCROLL_LEFT) {if (allLen < width) {String baseCont = cont;int times = (int) (width / allLen) + 1;StringBuilder contBuilder = new StringBuilder(cont);for (int i = 0; i < times; i++) {contBuilder.append(TEXT_SPACE);contBuilder.append(baseCont);}cont = contBuilder.toString();allLen = getContWidth2(cont);}}//3. 分区BaseText baseText;//每页需文字长度:向下取整(防重叠)int pLen = (int) Math.floor(width / allLen * cont.length());int start = 0;do {int end = start + pLen;String str;if (end > cont.length()) {//仅赋空到半格int num = end - cont.length();if (num > pLen / 2) {num = num - pLen / 2;}end = cont.length();//超出, 自动拼接空格str = cont.substring(start, end);for (int i = 0; i < num; i++) {str = str.concat(TEXT_SPACE);}} else {str = cont.substring(start, end);}baseText = new BaseText();baseText.setCont(str);baseText.setLenght(getContWidth2(str));tempList.add(baseText);start = end;} while (start < cont.length());Log.d(TAG, "每页需文字长度: " + pLen + ", 分页数: " + tempList.size());} else {//竖滚|静态//格式化换行cont = cont.replaceAll(" ", TEXT_SPACE).replaceAll("\\\\n|\n", "_n").replaceAll("\\\\", "");//格式化int lineWidth = (int) (width - 2 * padding - 2 * textSize);//每行最大可绘制宽度if (cont.contains("_n")) {String[] arr = cont.split("_n");for (String line : arr) {BaseText baseText;float lineLen = getContWidth2(line);if (lineLen > lineWidth) {//溢出行则分段//每页需文字长度:向下取整(防重叠)int pLen = (int) Math.floor(lineWidth / lineLen * line.length());int start = 0;do {int end = start + pLen;String str;if (end > line.length()) {//仅赋空到半格int num = end - line.length();if (num > pLen / 2) {num = num - pLen / 2;}end = line.length();//超出, 自动拼接空格str = line.substring(start, end);for (int i = 0; i < num; i++) {str = str.concat(TEXT_SPACE);}} else {str = line.substring(start, end);}baseText = new BaseText();baseText.setCont(str);baseText.setLenght(getContWidth2(str));tempList.add(baseText);start = end;} while (start < line.length());} else {//直接入队baseText = new BaseText();baseText.setCont(line);baseText.setLenght(lineLen);tempList.add(baseText);}}} else {//2. 获取文本高、总长度float allLen = getContWidth2(cont);BaseText baseText;//每页需文字长度:向下取整(防重叠)int pLen = (int) Math.floor(lineWidth / allLen * cont.length());int start = 0;do {int end = start + pLen;String str;if (end > cont.length()) {//仅赋空到半格int num = end - cont.length();if (num > pLen / 2) {num = num - pLen / 2;}end = cont.length();//超出, 自动拼接空格str = cont.substring(start, end);for (int i = 0; i < num; i++) {str = str.concat(TEXT_SPACE);}} else {str = cont.substring(start, end);}baseText = new BaseText();baseText.setCont(str);baseText.setLenght(getContWidth2(str));tempList.add(baseText);start = end;} while (start < cont.length());}}return tempList;}private float getContWidth(String txt) {TextPaint temp = new TextPaint();temp.setTextAlign(Paint.Align.LEFT);temp.setStyle(Paint.Style.FILL);temp.setTextSize(textSize);return Layout.getDesiredWidth(txt, temp);}//两个结果一样private float getContWidth2(String txt) {return paint.measureText(txt);}private float getContHeight() {Paint temp = new Paint();temp.setTextAlign(Paint.Align.CENTER);temp.setTextSize(textSize);Paint.FontMetrics fm = temp.getFontMetrics();return (float) Math.ceil(fm.descent - fm.ascent);}//先设置字体大小private float getContHeight2() {Paint.FontMetrics fm = paint.getFontMetrics();return (float) Math.ceil(fm.descent - fm.ascent);}///@Overridepublic void surfaceCreated(SurfaceHolder holder) {Log.d(TAG, "surfaceCreated: ");isHolderReady = true;startRolling();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {isHolderReady = false;stopRolling();}//static class BaseText implements Serializable {private String cont;private float lenght;public BaseText() {}public BaseText(String cont, float lenght) {this.cont = cont;this.lenght = lenght;}public String getCont() {return cont;}public void setCont(String cont) {this.cont = cont;}public float getLenght() {return lenght;}public void setLenght(float lenght) {this.lenght = lenght;}}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com