转载请注明出处
http://blog.csdn.net/u014513456/article/details/53726705
[email protected]
跑马灯有几种滚动方法
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:[email protected]
*
* @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:[email protected]
*
* @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.MarqueeText
android:id="@+id/marquee_view"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:text="我是跑马灯"
android:gravity="center_vertical"
android:textColor="@color/new_orange_color"
android:textSize="@dimen/font_size_13">
</com.ruanjianjiagou.view.text.MarqueeText>
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<String> lists;//会循环显示的文本内容
private List<String> 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<String> 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;
}
}
垂直跑马灯部分转自