首页 > 代码库 > 利用自定义可垂直滚动的Textview开发双色球滚动效果

利用自定义可垂直滚动的Textview开发双色球滚动效果

先看效果图:

技术分享

技术分享


这里。我们自定义了一个Textview


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 = 15.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;
    }


}


我们的Activity:

package com.example.myscroltextview;


import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;


import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;


import com.example.myscroltextview.view.MyScrollTextView;


public class MyMainActivity extends Activity {
private MyScrollTextView ball1, ball2, ball3, ball4, ball5, ball6, ball7;
private Button changeButton;
private TimerTask task;
private Timer timer = new Timer();


private final Boolean[] isUp = {false, true};
private int time;


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity_main);


ball1 = (MyScrollTextView) findViewById(R.id.myscroll1);
ball2 = (MyScrollTextView) findViewById(R.id.myscroll2);
ball3 = (MyScrollTextView) findViewById(R.id.myscroll3);
ball4 = (MyScrollTextView) findViewById(R.id.myscroll4);
ball5 = (MyScrollTextView) findViewById(R.id.myscroll5);
ball6 = (MyScrollTextView) findViewById(R.id.myscroll6);
ball7 = (MyScrollTextView) findViewById(R.id.myscroll7);
changeButton = (Button) findViewById(R.id.my_button_update);


// final List<String> strs = new ArrayList<String>();
// strs.add("xx01\t02\t03\t04\t05\t06\t07\t08\t09\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\t25\t26\t27\t28\t29\t30\t31\t32\t33\t34\t35");
// strs.add("yy01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35");
final List<String> ssqRed = new ArrayList<String>();
//ssqRed.add("01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33");
ssqRed.add("0102030405060708091011121314151617181920212223242526272829303132333");


final List<String> ssqBlue = new ArrayList<String>();
ssqBlue.add("01020304050607080910111213141516");


final List<String> dltRed = new ArrayList<String>();
dltRed.add("0102030405060708091011121314151617181920212223242526272829303132333435");


final List<String> dltBlue = new ArrayList<String>();
dltBlue.add("010203040506070809101112");


changeButton.setOnClickListener(new View.OnClickListener() {


@Override
public void onClick(View v) {
ball1.setText("");
ball2.setText("");
ball3.setText("");
ball4.setText("");
ball5.setText("");
ball6.setText("");
ball7.setText("");

time = 2;
task = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() { // UI thread
@Override
public void run() {
time--;

if (time <= 0) {
ball1.stop();
ball2.stop();
ball3.stop();
ball4.stop();
ball5.stop();
ball6.stop();
ball7.stop();

ball1.setScrollText("");
ball2.setScrollText("");
ball3.setScrollText("");
ball4.setScrollText("");
ball5.setScrollText("");
ball6.setScrollText("");
ball7.setScrollText("");

ball1.setText("02");
ball2.setText("06");
ball3.setText("24");
ball4.setText("06");
ball5.setText("14");
ball6.setText("27");
ball7.setText("11");
task.cancel();
} else {
int i = (int) (ssqRed.size() * Math.random());
ball1.setScrollText(ssqRed.get(i));
ball2.setScrollText(ssqRed.get(i));
ball3.setScrollText(ssqRed.get(i));
ball4.setScrollText(ssqRed.get(i));
ball5.setScrollText(ssqRed.get(i));
ball6.setScrollText(ssqRed.get(i));
ball7.setScrollText(ssqBlue.get(i));


Random random = new Random();
random.nextInt(2); // [0,2)


ball1.isUp = isUp[random.nextInt(2)];
ball2.isUp = isUp[random.nextInt(2)];
ball3.isUp = isUp[random.nextInt(2)];
ball4.isUp = isUp[random.nextInt(2)];
ball5.isUp = isUp[random.nextInt(2)];
ball6.isUp = isUp[random.nextInt(2)];
ball7.isUp = isUp[random.nextInt(2)];
}
}
});
}
};
timer.schedule(task, 0, 1000);
}
});


}


}


我们可以控制每个球的速度,这里我们采用随机数,是的每个球的速度都不一致,而且都在一定范围内,

我们还可以控制每个球的方向,向上或者向下。

这里提供一个demo的下载http://download.csdn.net/detail/lgx06/8295737


利用自定义可垂直滚动的Textview开发双色球滚动效果