package com.example.myscroltextview.view;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;
public class ScrollTextView extends TextView {
/**
* ÿ�е��ַ���
*/
ArrayList<String> lineStrings;
/**
* ��ǰ��λ��
*/
float currentY;
/**
* ���������Ϣ
*/
Handler handler;
/**
* Ҫ��ʾ��text
*/
String scrollText="";
/**
* ��ʵ���,������width="xxdp"��������
*/
private int exactlyWidth = -1;
/**
* ��ʵ�߶�,������height="xxdip"��������
*/
private int exactlyHeight = -1;
public String getScrollText() {
return scrollText;
}
public void setScrollText(String scrollText) {
this.scrollText = scrollText;
reset();
}
/**
* ����
*/
private void reset() {
requestLayout();
invalidate();
currentY = 0;
}
public ScrollTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public ScrollTextView(Context context) {
super(context);
init();
}
/**
* �Ƿ��ڹ���
*/
boolean scrolling = false;
/**
* ʵ�ʸ߶ȣ���������ʾ��ȫ��Ҫ�ĸ߶�
*/
float absloutHeight = 0;
/**
* handler����Ϣ��ʱ����
*/
int delayTime = 10;
/**
* ÿ�ι����ľ���
*/
float speed = 0.5f;
/**
* ��ʼ��
*/
void init() {
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (absloutHeight <= getHeight()) {
currentY = 0;
stop();
return;
}
switch (msg.what) {
case 0: {
currentY = currentY - speed;
resetCurrentY();
invalidate();
handler.sendEmptyMessageDelayed(0, delayTime);
break;
}
case 1: {
currentY += msg.arg1;
resetCurrentY();
invalidate();
}
}
}
/**
* ����currentY����currentY����absloutHeightʱ����������Ϊ0��
*/
private void resetCurrentY() {
if (currentY >= absloutHeight || currentY <= -absloutHeight || getHeight() <= 0) {
currentY = 0;
}
}
};
}
/**
* �ϴδ����¼�����ָy����
*/
float lastY = 0;
/**
* Ϊtrue����ԭ���ǹ���
*/
boolean needStop;
public void pause() {
if (scrolling) {
stop();
needStop = true;
}
}
public void goOn() {
if (needStop) {
play();
needStop = false;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
distanceY = lastY = event.getY();
distanceX = event.getX();
pause();
return true;
case MotionEvent.ACTION_MOVE:
float dy = event.getY() - lastY;
lastY = event.getY();
// currentY = currentY + dy;
Message msg = Message.obtain();
msg.what = 1;
msg.arg1 = (int)dy;
handler.sendMessage(msg);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
goOn();
float y = event.getY() - distanceY;
float x = event.getX() - distanceX;
if (Math.sqrt(y * y + x * x) < performUpScrollStateDistance) {
updateScrollStatus();
}
return true;
}
return super.onTouchEvent(event);
}
/**
* ����ָ�ƶ��˴�С�ľ������ڣ�����Ϊ�ǹ���״̬�ı���¼������ڸ�ֵ��������ָ����
*/
public static final long performUpScrollStateDistance = 5;
public float distanceY = 0;
public float distanceX = 0;
/**
* ���Ĺ���״̬
*/
public void updateScrollStatus() {
if (scrolling) {
stop();
} else {
play();
}
}
/**
* ��ʼ����
*/
public void play() {
if (!scrolling) {
handler.sendEmptyMessage(0);
scrolling = true;
}
}
/**
* ֹͣ����
*/
public void stop() {
if (scrolling) {
handler.removeMessages(0);
scrolling = false;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureWidth(widthMeasureSpec);
int height = MeasureHeight(width, heightMeasureSpec);
setMeasuredDimension(width, height);
currentY = 0;
if (height < absloutHeight) {
play();
} else {
stop();
}
}
/**
* �������
*
* @param widthMeasureSpec
* @return
*/
private int MeasureWidth(int widthMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
// �����wrap_content
if (mode == MeasureSpec.AT_MOST) {
double abwidth = getPaint().measureText(scrollText);
width = Math.min((int)Math.rint(abwidth), width);
exactlyWidth = -1;
}
if (mode == MeasureSpec.EXACTLY) {
exactlyWidth = width;
}
return width;
}
/**
* �����߶�
*
* @param width:���
* @param heightMeasureSpec
* @return
*/
private int MeasureHeight(int width, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
generateTextList(width);
int lines = lineStrings.size();
absloutHeight = lines * getLineHeight() + getPaddingBottom() + getPaddingTop();
// �����wrap_content
if (mode == MeasureSpec.AT_MOST) {
height = (int)Math.min(absloutHeight, height);
exactlyHeight = -1;
} else if (mode == MeasureSpec.EXACTLY) {
exactlyHeight = height;
}
return height;
}
/**
* �Ƿ�ΪӢ�ĵ��ʵ�����ĸ
*
* @param str
* @param i
* @return
*/
boolean isENWordStart(String str, int i) {
if (i == 0) {
return true;
} else if (str.charAt(i - 1) == ' ') {
return true;
}
return false;
}
/**
* ��ȡһ�е��ַ�
*
* @param MaxWidth
* @param str
* @return
*/
private String getLineText(int MaxWidth, String str) {
// ��ʵ��
StringBuffer trueStringBuffer = new StringBuffer();
// ��ʱ��
StringBuffer tempStringBuffer = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
String add = "";
// ���c����ĸ��Ҫ����Ӣ�ĵ��ʻ��й���
if (!isChinese(c) && isENWordStart(str, i)) {
int place = getNextSpecePlace(i, str);
// �ҵ���һ���ո�
if (place > -1) {
add = str.substring(i, place) + " ";
if (getPaint().measureText(add) > MaxWidth) {
add = "" + c;
} else {
i = place;
}
} else {
add = "" + c;
}
} else {
add = "" + c;
}
tempStringBuffer.append(add);
String temp = tempStringBuffer.toString();
float width = getPaint().measureText(temp.toString());
if (width <= MaxWidth) {
trueStringBuffer.append(add);
} else {
break;
}
}
return trueStringBuffer.toString();
}
/**
* �ҵ���һ���ո�ĵط�
*
* @param i
* @param str
* @return
*/
int getNextSpecePlace(int i, String str) {
for (int j = i; j < str.length(); j++) {
char c = str.charAt(j);
if (c == ' ') {
return j;
}
}
return -1;
}
/**
* ���ɶ����ַ����б�
*
* @param MaxWidth
*/
public void generateTextList(int MaxWidth) {
lineStrings = new ArrayList<String>();
String remain = scrollText;
while (!remain.equals("")) {
String line = getLineText(MaxWidth, remain);
lineStrings.add(line);
remain = remain.substring(line.length(), remain.length());
}
};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float x = getPaddingLeft();
float y = getPaddingTop();
float lineHeight = getLineHeight();
float textSize = getPaint().getTextSize();
for (int i = 0; i < lineStrings.size(); i++) {
y = lineHeight * i + textSize + currentY;
float min = 0;
if (exactlyHeight > -1) {
min = Math.min(min, exactlyHeight - absloutHeight);
}
if (y < min) {
y = y + absloutHeight;
} else if (y >= min && y < textSize + min) {
//�����˵������Ѿ�������Ҫѭ�������������ʱ��
canvas.drawText(lineStrings.get(i), x, y + absloutHeight, getPaint());
}
if (y >= absloutHeight) {
//�����˵������Ѿ�������Ҫѭ�������������ʱ��
canvas.drawText(lineStrings.get(i), x, y, getPaint());
y = y - absloutHeight;
}
canvas.drawText(lineStrings.get(i), x, y, getPaint());
}
}
/**
* �ж��Ƿ�Ϊ����
*
* @param c
* @return
*/
private static final boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
return true;
}
return false;
}
}