记录一个Android中简单实现垂直跑马灯功能的方法:实现了垂直跑马灯的功能。记录一下以便日后查看:
首先在MainActivity下新建一个名为
ScrollTextView的.java 文件
ScrollTextView.java代码如下:
其中判断分行结束符以 "\n" 为准。。。
package com.example.chenhy.pmd; 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; /** * ?????????? * @author HeJx * */ 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; private float index = 0; public String getScrollText() { return scrollText; } public void setScrollText(String scrollText) { this.scrollText = scrollText; reset(); } /** * ???? */ private void reset() { if(lineStrings!=null) lineStrings.clear(); stop(); currentY = 0; absloutHeight = 0; this.setText(""); if(handler!=null){ handler.removeMessages(0); handler.removeMessages(1); handler.removeMessages(2); handler.removeMessages(3); } requestLayout(); invalidate(); } 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???????????? */ private int delayTime = 10; //??????? private int stopTime = 0; //?????? public int getDelayTime() { return delayTime; } public void setDelayTime(int delayTime) { this.delayTime = delayTime; } public int getStopTime() { return stopTime; } public void setStopTime(int stopTime) { this.stopTime = stopTime; } /** * ??ι???????? */ 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(); boolean flag = true; if (currentY >= absloutHeight || currentY <= -absloutHeight) { flag = false; handler.sendEmptyMessageDelayed(3, stopTime); } if(flag){ if (currentY >= index|| currentY <= -index || getHeight() <= 0) { handler.sendEmptyMessageDelayed(2, stopTime); }else{ handler.sendEmptyMessageDelayed(0, delayTime); } } break; } case 1: { currentY += msg.arg1; resetCurrentY(); invalidate(); } break; case 2:{ index = index+absloutHeight/lineStrings.size(); stop(); handler.sendEmptyMessageDelayed(0, delayTime); } break; case 3:{ currentY = 0; index = absloutHeight/lineStrings.size();; stop(); handler.sendEmptyMessageDelayed(0, delayTime); } } } /** * ????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(); index = absloutHeight/lines; // ?????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(); boolean isLine; for (int i = 0; i < str.length(); i++) { isLine = false; 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 { if(c=='\n') { //?ж????????е????? isLine = true; add = "" + c; }else add = "" + c; } tempStringBuffer.append(add); String temp = tempStringBuffer.toString(); float width = getPaint().measureText(temp.toString()); if(isLine) break; 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 == '\n') { return j; } } return -1; } /** * ?????????????б? * * @param MaxWidth */ public void generateTextList(int MaxWidth) { lineStrings = new ArrayList<String>(); // String remain = scrollText; String[] contenx = scrollText.split("\n"); //这里的"\n" 是标志 for (String string : contenx) { lineStrings.add(string); } /* 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; } }
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.chenhy.pmd.MainActivity">
<com.example.chenhy.pmd.ScrollTextView
android:id="@+id/marqueeview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="10sp"
>
</com.example.chenhy.pmd.ScrollTextView>
</LinearLayout>
最后是mainactivity中的代码
package com.example.chenhy.pmd;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ScrollTextView marqueeView = (ScrollTextView) findViewById(R.id.marqueeview);
marqueeView.setScrollText("塞纳河畔 左岸的咖啡\n" +
"我手一杯 品尝你的美\n" +
"留下唇印的嘴\n" +
"花店玫瑰 名字写错谁\n" +
"告白气球 风吹到对街\n" +
"微笑在天上飞\n" +
"你说你有点难追 想让我知难而退\n" +
"礼物不需挑最贵 只要香榭的落叶\n" +
"喔 营造浪漫的约会 不害怕搞砸一切\n" +
"拥有你就拥有 全世界\n" +
"亲爱的 爱上你 从那天起\n" +
"甜蜜的很轻易\n" +
"亲爱的 别任性 你的眼睛\n" +
"在说我愿意\n" +
"塞纳河畔 左岸的咖啡\n" +
"我手一杯 品尝你的美\n" +
"留下唇印的嘴\n" +
"花店玫瑰 名字写错谁\n" +
"告白气球 风吹到对街\n" +
"微笑在天上飞\n" +
"你说你有点难追 想让我知难而退\n" +
"礼物不需挑最贵 只要香榭的落叶\n" +
"喔 营造浪漫的约会 不害怕搞砸一切\n" +
"拥有你就拥有 全世界\n" +
"亲爱的 爱上你 从那天起\n" +
"甜蜜的很轻易\n" +
"亲爱的 别任性 你的眼睛\n" +
"在说我愿意\n" +
"亲爱的 爱上你 恋爱日记\n" +
"飘香水的回忆\n" +
"一整瓶 的梦境 全都有你\n" +
"搅拌在一起\n" +
"亲爱的别任性 你的眼睛\n" +
"在说我愿意"+"塞纳河畔 左岸的咖啡\n" +
"我手一杯 品尝你的美\n" +
"留下唇印的嘴\n" +
"花店玫瑰 名字写错谁\n" +
"告白气球 风吹到对街\n" +
"微笑在天上飞\n" +
"你说你有点难追 想让我知难而退\n" +
"礼物不需挑最贵 只要香榭的落叶\n" +
"喔 营造浪漫的约会 不害怕搞砸一切\n" +
"拥有你就拥有 全世界\n" +
"亲爱的 爱上你 从那天起\n" +
"甜蜜的很轻易\n" +
"亲爱的 别任性 你的眼睛\n" +
"在说我愿意\n" +
"塞纳河畔 左岸的咖啡\n" +
"我手一杯 品尝你的美\n" +
"留下唇印的嘴\n" +
"花店玫瑰 名字写错谁\n" +
"告白气球 风吹到对街\n" +
"微笑在天上飞\n" +
"你说你有点难追 想让我知难而退\n" +
"礼物不需挑最贵 只要香榭的落叶\n" +
"喔 营造浪漫的约会 不害怕搞砸一切\n" +
"拥有你就拥有 全世界\n" +
"亲爱的 爱上你 从那天起\n" +
"甜蜜的很轻易\n" +
"亲爱的 别任性 你的眼睛\n" +
"在说我愿意\n" +
"亲爱的 爱上你 恋爱日记\n" +
"飘香水的回忆\n" +
"一整瓶 的梦境 全都有你\n" +
"搅拌在一起\n" +
"亲爱的别任性 你的眼睛\n" +
"在说我愿意");
}
}
最后,上一张效果图