Android 跑马灯 文字滚动
发布日期:2021-06-23 19:02:43 浏览次数:11 分类:技术文章

本文共 16946 字,大约阅读时间需要 56 分钟。

转载请注明出处

ruanjianjiagou@163.com

跑马灯有几种滚动方法

1.横向滚动 (从左至右)
2.横向滚动 (从右至左)
3.纵向滚动(从上到下)
4.纵向滚动(从下到上)
纵向滚动还涉及是否完全显示所有文字
1.横向滚动 (从左至右)

import android.annotation.TargetApi;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.os.Build;import android.util.AttributeSet;import android.widget.TextView;/** * Author:xuqiliang * Email:ruanjianjiagou@163.com * * @data:16/12/18 下午12:40 * @Description:${todo} */public class LeftToRightTextView extends TextView implements Runnable {
private int currentScrollX;// 当前滚动的位置 private boolean isStop = false; private int textWidth; private boolean isMeasure = false; public LeftToRightTextView(Context context) { super(context); } public LeftToRightTextView(Context context, AttributeSet attrs) { super(context, attrs); } public LeftToRightTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public LeftToRightTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (!isMeasure) { getTextWidth(); isMeasure = true; } } private void getTextWidth() { Paint paint = this.getPaint(); String str = this.getText().toString(); textWidth = (int) paint.measureText(str); } @Override public void setText(CharSequence text, BufferType type) { super.setText(text, type); this.isMeasure = false; } /** * Starts executing the active part of the class' code. This method is * called when a thread is started that has been created with a class which * implements {@code Runnable}. */ @Override public void run() { currentScrollX -= 2;// 滚动速度 scrollTo(currentScrollX, 0); if (isStop) { return; } if (getScrollX() <= -(this.getWidth())) { scrollTo(textWidth, 0); currentScrollX = textWidth; // return; } postDelayed(this, 5); } /** * 在Activity 或者Fragment OnResume 方法中调用 * mLeftToRightTextView.onStart(); * 开始滚动 */ public void onStart() { isStop = false; this.removeCallbacks(this); post(this); } public void onStop() { isStop = true; } /** * 从0开始滚动 */ public void onStartZero() { currentScrollX = 0; onStart(); }}

2.横向滚动 (从右至左)

import android.annotation.TargetApi;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.os.Build;import android.util.AttributeSet;import android.widget.TextView;/** * Email:ruanjianjiagou@163.com * * @data:16/12/18 上午11:49 * @Description:${todo} */public class MarqueeTextView extends TextView implements Runnable {
private int currentScrollX;// 当前滚动的位置 private boolean isStop = false; private float textWidth; private boolean isMeasure = false; public MarqueeTextView(Context context) { super(context); } public MarqueeTextView(Context context, AttributeSet attrs) { super(context, attrs); } public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (!isMeasure) { getTextWidth(); isMeasure = true; } } public float getTextWidth() { Paint paint = this.getPaint(); String mText = this.getText().toString(); textWidth = paint.measureText(mText); return textWidth; } @Override public void setText(CharSequence text, BufferType type) { super.setText(text, type); this.isMeasure = false; } /** * Starts executing the active part of the class' code. This method is * called when a thread is started that has been created with a class which * implements {@code Runnable}. */ @Override public void run() { currentScrollX += 1; scrollTo(currentScrollX, 0); if (isStop) { return; } if (getScrollX() >= textWidth) { scrollTo(-this.getWidth(), 0); currentScrollX = -this.getWidth(); } postDelayed(this, 10); } public void onStart() { isStop = false; this.removeCallbacks(this); post(this); } public void onStop() {// currentScrollX = 0; isStop = true; } public void onStartZero() { currentScrollX = 0; onStart(); }}

使用方法

布局文件如下  
com.ruanjianjiagou.view.text 为类所在的包名称。 以上控件可以放在任何容器布局中
Activity或fragment中 使用如下
private MarqueeTextView marquee_view;  //声明全局变量marquee_view = (MarqueeText)mRootView.findViewById(R.id.marquee_view);marquee_view.setText("")//此处可以是任何时候取到数据了放入的要滚动的字符串   @Override    public void onResume() {        marquee_view.onStart(); //启动跑马灯     }    @Override    public void onPause() {        super.onPause();        marquee_view.onStop();    }

3&4 垂直跑马灯

package com.viclee.verticalswitchtextview;import android.animation.Animator;import android.animation.ValueAnimator;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.text.TextUtils;import android.util.AttributeSet;import android.view.View;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class VerticalSwitchTextView extends TextView implements View.OnClickListener {
private static final int DEFAULT_SWITCH_DURATION = 500; private static final int DEFAULT_IDLE_DURATION = 2000; public static final int TEXT_ALIGN_CENTER = 0; public static final int TEXT_ALIGN_LEFT = 1; public static final int TEXT_ALIGN_RIGHT = 2; private Context mContext; private List
lists;//会循环显示的文本内容 private List
ellipsisLists; private int contentSize; private String outStr;//当前滑出的文本内容 private String inStr;//当前滑入的文本内容 private float textBaseY;//文本显示的baseline private int currentIndex = 0;//当前显示到第几个文本 private String ellipsis; private float ellipsisLen = 0; private int switchDuaration = DEFAULT_SWITCH_DURATION;//切换时间 private int idleDuaration = DEFAULT_IDLE_DURATION;//间隔时间 private int switchOrientation = 0; private int alignment = TEXT_ALIGN_CENTER; /** * 文本中轴线X坐标 */ private float inTextCenterX; private float outTextCenterX; private float currentAnimatedValue = 0.0f; private ValueAnimator animator; private TextUtils.TruncateAt mEllipsize; private int verticalOffset = 0; private int mWidth; private int mHeight; private int paddingLeft = 0; private int paddingBottom = 0; private int paddingTop = 0; private int paddingRight = 0; private Paint mPaint; //回调接口,用来通知调用者控件当前的状态 public VerticalSwitchTextViewCbInterface cbInterface; public VerticalSwitchTextView(Context context) { this(context, null); } public VerticalSwitchTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public VerticalSwitchTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.VerticalSwitchTextView); try { switchDuaration = array.getInt(R.styleable.VerticalSwitchTextView_switchDuaration, DEFAULT_SWITCH_DURATION); idleDuaration = array.getInt(R.styleable.VerticalSwitchTextView_idleDuaration, DEFAULT_IDLE_DURATION); switchOrientation = array.getInt(R.styleable.VerticalSwitchTextView_switchOrientation, 0); alignment = array.getInt(R.styleable.VerticalSwitchTextView_alignment, TEXT_ALIGN_CENTER); } finally { array.recycle(); } init(); } private void init() { setOnClickListener(this); mPaint = getPaint(); mPaint.setTextAlign(Paint.Align.CENTER); ellipsis = getContext().getString(R.string.ellipsis); ellipsisLen = mPaint.measureText(ellipsis); mEllipsize = getEllipsize(); animator = ValueAnimator.ofFloat(0f, 1f).setDuration(switchDuaration); animator.setStartDelay(idleDuaration); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentAnimatedValue = (float) animation.getAnimatedValue(); if (currentAnimatedValue < 1.0f) { invalidate(); } } }); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { currentIndex = (++currentIndex) % contentSize; if (cbInterface != null) { cbInterface.showNext(currentIndex); } outStr = lists.get(currentIndex); inStr = lists.get((currentIndex + 1) % contentSize); animator.setStartDelay(idleDuaration); animator.start(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } /** * 设置循环显示的文本内容 * * @param content 内容list */ public void setTextContent(List
content) { lists = content;// lists.clear();// lists = new ArrayList<>();// lists.add("1适当放松放松放松点水电费水电费史蒂夫水电费水电费");// lists.add("2适当放松放松放松点水电费水电费史蒂夫水电费水电费");// lists.add("3适当放松放");// lists.add("3适当放松放1111222333"); if (lists == null || lists.size() == 0) { return; } contentSize = lists.size(); if (contentSize > 0) { animator.start(); } } private void generateEllipsisText() { if (ellipsisLists != null) {
//防止重复计算 return; } ellipsisLists = new ArrayList<>(); if (lists != null && lists.size() != 0) { for (String item : lists) { int avail = mWidth - paddingLeft - paddingRight; float remaining = avail - ellipsisLen; if (avail <= 0) { ellipsisLists.add(""); } else { float itemWidth = mPaint.measureText(item, 0, item.length()); if (itemWidth < avail) { ellipsisLists.add(item); } else if (remaining <= 0) { ellipsisLists.add(ellipsis); } else { int len = item.length(); float[] widths = new float[len]; mPaint.getTextWidths(item, 0, item.length(), widths); if (mEllipsize == TextUtils.TruncateAt.END) { float blockWidth = 0f; for (int i = 0; i < len; i++) { blockWidth += widths[i]; if (blockWidth > remaining) { ellipsisLists.add(item.substring(0, i) + ellipsis); break; } } } else if (mEllipsize == TextUtils.TruncateAt.START) { float blockWidth = 0f; for (int i = len - 1; i >= 0; i--) { blockWidth += widths[i]; if (blockWidth > remaining) { ellipsisLists.add(ellipsis + item.substring(i, len - 1)); break; } } } else if (mEllipsize == TextUtils.TruncateAt.MIDDLE) { float blockWidth = 0f; for (int i = 0, j = len - 1; i < j; i++, j--) { blockWidth += (widths[i] + widths[j]); if (blockWidth > remaining) { if (blockWidth - widths[j] < remaining) { ellipsisLists.add(item.substring(0, i + 1) + ellipsis + item.substring(j, len - 1)); } else { ellipsisLists.add(item.substring(0, i) + ellipsis + item.substring(j, len - 1)); } break; } } } } } } } lists = ellipsisLists; } /** * 主要用来调整TextView的高度 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = MeasureSpec.getSize(widthMeasureSpec); Rect bounds = new Rect(); if (contentSize <= 0) { return; } String text = lists.get(0); mPaint.getTextBounds(text, 0, text.length(), bounds); int textHeight = bounds.height(); paddingLeft = getPaddingLeft(); paddingRight = getPaddingRight(); paddingBottom = getPaddingBottom(); paddingTop = getPaddingTop(); if (mEllipsize != null) { generateEllipsisText(); } outStr = lists.get(0); if (contentSize > 1) { inStr = lists.get(1); } else { inStr = lists.get(0); } mHeight = textHeight + paddingBottom + paddingTop; Paint.FontMetrics fontMetrics = mPaint.getFontMetrics(); //计算文字高度 float fontHeight = fontMetrics.bottom - fontMetrics.top; //计算文字的baseline textBaseY = mHeight - (mHeight - fontHeight) / 2 - fontMetrics.bottom; setMeasuredDimension(mWidth, mHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (contentSize <= 0) { return; } //计算绘制的文字中心位置 switch (alignment) { case TEXT_ALIGN_CENTER: inTextCenterX = outTextCenterX = (mWidth - paddingLeft - paddingRight) / 2 + paddingLeft; break; case TEXT_ALIGN_LEFT: inTextCenterX = paddingLeft + mPaint.measureText(inStr) / 2; outTextCenterX = paddingLeft + mPaint.measureText(outStr) / 2; break; case TEXT_ALIGN_RIGHT: inTextCenterX = mWidth - paddingRight - mPaint.measureText(inStr) / 2; outTextCenterX = mWidth - paddingRight - mPaint.measureText(outStr) / 2; break; } //直接使用mHeight控制文本绘制,会因为text的baseline的问题不能居中显示 verticalOffset = Math.round(2 * textBaseY * (0.5f - currentAnimatedValue));// L.d("verticalOffset is " + verticalOffset); if (switchOrientation == 0) {
//向上滚动切换 if (verticalOffset > 0) { canvas.drawText(outStr, outTextCenterX, verticalOffset, mPaint); } else { canvas.drawText(inStr, inTextCenterX, 2 * textBaseY + verticalOffset, mPaint); } } else { if (verticalOffset > 0) {
//向下滚动切换 canvas.drawText(outStr, outTextCenterX, 2 * textBaseY - verticalOffset, mPaint); } else { canvas.drawText(inStr, inTextCenterX, -verticalOffset, mPaint); } } } @Override public void onClick(View v) { if (contentSize > currentIndex) { if (cbInterface != null) { cbInterface.onItemClick(currentIndex); } } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mContext = null; if (animator != null) { animator.cancel(); } } //回调接口,用来通知调用者控件当前的状态,index表示开始显示哪一个文本内容 public interface VerticalSwitchTextViewCbInterface {
void showNext(int index); void onItemClick(int index); } public void setCbInterface(VerticalSwitchTextViewCbInterface cb) { cbInterface = cb; }}

垂直跑马灯部分转自

转载地址:https://blog.csdn.net/u014513456/article/details/53726705 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Thymeleaf 模板引擎简介 与 Spring Boot 整合入门
下一篇:Java SE 正则表达式 API Pattern 与 Matcher.

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年06月27日 03时11分51秒