<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(255, 255, 255);">实现思路:</span>
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(255, 255, 255);">实现思路:</span>
跑马灯上下滚动,需要三部分:
1.当前显示的图文信息区域,用类ItemInfo对象currentItem表示,ItemInfo类包括Rect信息与绘制图文信息,并提供绘制方法。
2.紧挨着当前区域上方的区域,用类ItemInfo对象preItem,如果跑马灯向上滚,此对象表示滚动后的最终位置。
3.紧挨着当前区域下方的区域,用类ItemInfo对象nextItem,如果跑马灯向上滚,此对象表示下一条图文信息,滚动到currentItem的位置。
class ItemInfo {
private Rect srcRect;
private Rect varyRect;
private boolean isAchieve;
private String content;
private Drawable icon;
private String time;
public ItemInfo(Rect rect) {
srcRect = rect;
varyRect = new Rect(rect);
isAchieve = true;
}
public void update(int recouseid, String content, String time) {
this.content = content;
this.time = time;
icon = getResources().getDrawable(recouseid);
varyRect = new Rect(srcRect);
isAchieve = false;
}
public Rect getSrcRect() {
return srcRect;
}
public void draw(Canvas canvas, Paint paint) {
BitmapDrawable bd = (BitmapDrawable) icon;
canvas.drawBitmap(bd.getBitmap(), getPaddingLeft(), varyRect.centerY() - icon.getIntrinsicHeight() / 2, paint);
m_paint.setTextAlign(Paint.Align.LEFT);
m_paint.setTextSize(getTextSize());
Paint.FontMetricsInt fontMetricsContent = m_paint.getFontMetricsInt();
int baselineContent = (varyRect.bottom + varyRect.top - fontMetricsContent.bottom - fontMetricsContent.top) / 2;
canvas.drawText(content, (float) (getCompoundDrawablePadding() + icon.getIntrinsicWidth()), baselineContent, paint);
m_paint.setTextAlign(Paint.Align.RIGHT);
m_paint.setTextSize(timeTextSize);
Paint.FontMetricsInt fontMetricsTime = m_paint.getFontMetricsInt();
int baselineTime = (varyRect.bottom + varyRect.top - fontMetricsTime.bottom - fontMetricsTime.top) / 2;
canvas.drawText(time, varyRect.width(), baselineTime, paint);
}
private Rect getStringRect(String strText) {
Rect rect = new Rect();
m_paint.getTextBounds(strText, 0, strText.length(), rect);
return rect;
}
public void moveTo(Rect rect, int gapX, int gapY) {
if (isAchieve) return;
if (varyRect.equals(rect)) {
isAchieve = true;
return;
}
if (Math.abs(rect.left - varyRect.left) < gapX) {
varyRect.left = rect.left;
varyRect.right = rect.right;
} else {
varyRect.left += gapX;
varyRect.right += gapX;
}
if (Math.abs(rect.top - varyRect.top) < gapY) {
varyRect.top = rect.top;
varyRect.bottom = rect.bottom;
} else {
varyRect.top += gapY;
varyRect.bottom += gapY;
}
}
public void reset() {
isAchieve = true;
varyRect = new Rect(srcRect);
}
public boolean isAchieve() {
return isAchieve;
}
}
Rect rect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
curItemInfo = new ItemInfo(rect);
nextItemInfo = new ItemInfo(new Rect(0, rect.height(), rect.width(), rect.height() * 2));
preItemInfo = new ItemInfo(new Rect(0, -rect.height(), rect.width(), 0));
实现向上滚动效果(向下同理):
以当前显示的currentItem移动到preItem位置时,为终止条件,每隔一定时间,更新OnDraw,Rect的Y轴位置增加变化量,向上移动一小段距离,更新绘制currentItem的位置,到preItem位置时停止刷新OnDraw。(跑马灯,向上移出旧的图文信息)
nextItem做为下一条图文信息显示,跟着currentItem做相同的行为,以当前显示的currentItem移动到preItem位置时,为终止条件。(跑马灯,向上移入新的图文信息)
@Override
public boolean run() {
// // TODO Auto-generated method stub
if (curItemInfo == null) {
sleep(200);
return true;
}
if (!curItemInfo.isAchieve()) {
nextItemInfo.moveTo(curItemInfo.getSrcRect(), 0, -GAP);
curItemInfo.moveTo(preItemInfo.getSrcRect(), 0, -GAP);
intervalDraw();
return true;
} else {
if (SystemClock.uptimeMillis() - stopTime > showDuration) {
startIntervalDraw();
return true;
}
sleep(1000);
return true;
}
}
private void intervalDraw() {
postInvalidate();
sleep(50);
}
private void startIntervalDraw() {
updateData();
}
每一次移动到目标位置后,会停止显示当前图文几秒,并更新下一轮显示的图文信息。
private void updateData() {
stopTime = SystemClock.uptimeMillis();
setTag(index);
curItemInfo.update(R.drawable.publisher_sign_official, listStrings.get(index), "2016.6.14");
if (index >= listStrings.size() - 1) {
index = 0;
} else {
index++;
}
nextItemInfo.update(R.drawable.publisher_sign_official, listStrings.get(index), "2016.6.14");
}
绘制
@Override
public void draw(Canvas canvas) {
if (nextItemInfo == null) {
return;
}
nextItemInfo.draw(canvas, m_paint);
curItemInfo.draw(canvas, m_paint);
}
全部代码:
package mktech.community.view.component;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import com.mktech.utils.LogUtils;
import java.util.List;
import mktech.community.R;
import mktech.community.UIContant;
import mktech.community.view.MyApplication;
public class MarqueeTextView extends TextView implements Runnable {
private Paint m_paint;
private Action mAction;
private int timeTextSize = 18;
private boolean isStopThread = true;
public void setList(List<String> list) {
if (list.size() == 0) return;
mAction = new Loop(list);
mAction.init();
if (isStopThread) {
isStopThread = false;
new Thread(this).start();
}
}
public void setOnClickListener(final OnClickListener listener) {
super.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onClick(v);
}
}
});
}
@Override
public void run() {
while (!isStopThread && !Thread.currentThread().isInterrupted()) {
if (mAction != null) {
if (!mAction.run()) {
break;
}
} else {
sleep(200);
}
}
isStopThread = true;
}
public void setText(final int icon, final String text) {
mAction = new One(icon, text);
if (isStopThread) {
isStopThread = false;
new Thread(this).start();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public MarqueeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
m_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
m_paint.setStrokeWidth(3);
m_paint.setColor(getTextColors().getDefaultColor());
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MarqueeTextView);
timeTextSize = typedArray.getDimensionPixelSize(R.styleable.MarqueeTextView_TimeTextSize, 18);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (mAction != null) {
mAction.draw(canvas);
}
}
interface Action {
void init();
void draw(Canvas canvas);
/**
* @return true不跳出循环,false不再循环。
*/
boolean run();
}
class ItemInfo {
private Rect srcRect;
private Rect varyRect;
private boolean isAchieve;
private String content;
private Drawable icon;
private String time;
public ItemInfo(Rect rect) {
srcRect = rect;
varyRect = new Rect(rect);
isAchieve = true;
}
public void update(int recouseid, String content, String time) {
this.content = content;
this.time = time;
icon = getResources().getDrawable(recouseid);
varyRect = new Rect(srcRect);
isAchieve = false;
}
public Rect getSrcRect() {
return srcRect;
}
public void draw(Canvas canvas, Paint paint) {
BitmapDrawable bd = (BitmapDrawable) icon;
canvas.drawBitmap(bd.getBitmap(), getPaddingLeft(), varyRect.centerY() - icon.getIntrinsicHeight() / 2, paint);
m_paint.setTextAlign(Paint.Align.LEFT);
m_paint.setTextSize(getTextSize());
Paint.FontMetricsInt fontMetricsContent = m_paint.getFontMetricsInt();
int baselineContent = (varyRect.bottom + varyRect.top - fontMetricsContent.bottom - fontMetricsContent.top) / 2;
canvas.drawText(content, (float) (getCompoundDrawablePadding() + icon.getIntrinsicWidth()), baselineContent, paint);
m_paint.setTextAlign(Paint.Align.RIGHT);
m_paint.setTextSize(timeTextSize);
Paint.FontMetricsInt fontMetricsTime = m_paint.getFontMetricsInt();
int baselineTime = (varyRect.bottom + varyRect.top - fontMetricsTime.bottom - fontMetricsTime.top) / 2;
canvas.drawText(time, varyRect.width(), baselineTime, paint);
}
private Rect getStringRect(String strText) {
Rect rect = new Rect();
m_paint.getTextBounds(strText, 0, strText.length(), rect);
return rect;
}
public void moveTo(Rect rect, int gapX, int gapY) {
if (isAchieve) return;
if (varyRect.equals(rect)) {
isAchieve = true;
return;
}
if (Math.abs(rect.left - varyRect.left) < gapX) {
varyRect.left = rect.left;
varyRect.right = rect.right;
} else {
varyRect.left += gapX;
varyRect.right += gapX;
}
if (Math.abs(rect.top - varyRect.top) < gapY) {
varyRect.top = rect.top;
varyRect.bottom = rect.bottom;
} else {
varyRect.top += gapY;
varyRect.bottom += gapY;
}
}
public void reset() {
isAchieve = true;
varyRect = new Rect(srcRect);
}
public boolean isAchieve() {
return isAchieve;
}
}
class Loop implements Action {
private static final int GAP = 10;
private static final int showDuration = 3000;
private List<String> listStrings;
private int index;
private long stopTime = 0;
private ItemInfo curItemInfo;
private ItemInfo nextItemInfo;
private ItemInfo preItemInfo;
public Loop(List<String> listStrings) {
this.listStrings = listStrings;
}
private void startDraw() {
if (listStrings == null && listStrings.size() == 0) return;
Rect rect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
curItemInfo = new ItemInfo(rect);
nextItemInfo = new ItemInfo(new Rect(0, rect.height(), rect.width(), rect.height() * 2));
preItemInfo = new ItemInfo(new Rect(0, -rect.height(), rect.width(), 0));
index = 0;
updateData();
}
@Override
public void init() {
startDraw();
}
private void updateData() {
stopTime = SystemClock.uptimeMillis();
setTag(index);
curItemInfo.update(R.drawable.publisher_sign_official, listStrings.get(index), "2016.6.14");
if (index >= listStrings.size() - 1) {
index = 0;
} else {
index++;
}
nextItemInfo.update(R.drawable.publisher_sign_official, listStrings.get(index), "2016.6.14");
}
@Override
public void draw(Canvas canvas) {
if (nextItemInfo == null) {
return;
}
nextItemInfo.draw(canvas, m_paint);
curItemInfo.draw(canvas, m_paint);
}
@Override
public boolean run() {
// // TODO Auto-generated method stub
if (curItemInfo == null) {
sleep(200);
return true;
}
if (!curItemInfo.isAchieve()) {
nextItemInfo.moveTo(curItemInfo.getSrcRect(), 0, -GAP);
curItemInfo.moveTo(preItemInfo.getSrcRect(), 0, -GAP);
intervalDraw();
return true;
} else {
if (SystemClock.uptimeMillis() - stopTime > showDuration) {
startIntervalDraw();
return true;
}
sleep(1000);
return true;
}
}
private void intervalDraw() {
postInvalidate();
sleep(50);
}
private void startIntervalDraw() {
updateData();
}
}
private void sleep(int time) {
try {
Thread.sleep(time);
} catch (Exception e) {
// TODO: handle exception
Thread.currentThread().interrupt();
}
}
class One implements Action {
private int icon;
private String text;
private ItemInfo curItemInfo;
public One(int icon, String text) {
this.icon = icon;
this.text = text;
}
@Override
public boolean run() {
while (getMeasuredWidth() == 0) {
sleep(100);
mAction.init();
}
return false;
}
@Override
public void init() {
Rect rect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
curItemInfo = new ItemInfo(rect);
postInvalidate();
}
@Override
public void draw(Canvas canvas) {
if (curItemInfo == null) return;
curItemInfo.update(icon, text, "2016.6.14");
curItemInfo.draw(canvas, m_paint);
}
}
}