1. 程式人生 > >Android 中一個簡單垂直跑馬燈的實現

Android 中一個簡單垂直跑馬燈的實現

記錄一個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" +
                "在說我願意");

    }
}


最後,上一張效果圖