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

时间:2024-10-19 08:00:07
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;//px private 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;//字体大小 px private float textHeight;//字体高度,与字体大小关联 private float startY;//与字体高度绑定 private float offset;//已滚动值 private float allHeight;//竖向滚动文本总高度 private boolean isHolderReady;// surfaceCreated public 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); } @Override protected 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