利用自定义可垂直滚动的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

时间: 2025-01-02 16:13:34

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

如何利用 Visual Studio 自带工具提高开发效率

原文:如何利用 Visual Studio 自带工具提高开发效率 Visual Stuido 是一款强大的Windows 平台集成开发工具,你是否好好地利用了它呢? 显示行号 有些时候(比如错误定位)的时候,显示行号将有利于我们进行快速定位. 如何显示 1. 工具 / 选项 / 文本编辑器 -> 选择对应的语言 2. 勾选 "行号" 使用书签 和平常意义的书签类似,当我们希望在日后某一时刻快速定位到一处代码时使用.比如在项目例会上,你需要演示本周你所做的一些改进,在开会前可以事先

自动滚动的Textview

又到了写博博的时候了!大家还好吧! 开始做机顶盒了,所以这里就有很多的界面需要做成一个自动化的界面.因为遥控盒手机触摸毕竟不一样咯!这里而且要方便大众的视觉体验的话,我们必须要考虑到这些因素.当然,我觉着,这次的自动滚动效果一定对大家也很有帮助的哦! 我们只要写一个类来继承我布局文件里面写的一个TextView就ok啦. package com.example.myscroltextview.view; import java.util.ArrayList; import android.con

【Android】利用自定义View的重绘实现拖动移动,获取组件的尺寸

下面利用一个app来说明如何利用自定义View的重绘实现拖动移动,获取组件的尺寸. 如下图,触摸拖动,或者轻轻点击屏幕都能移动图片.如果碰到文字,则会弹出提示. 这里是利用自定义View的重绘来实现的.就是点击屏幕一次,这个自定义View就会重绘一次.虽然这个自定义View里面就只有一个图片. 1.首先在res\values\strings.xml中定义各个字体文件,修改之后如下: <?xml version="1.0" encoding="utf-8"?&g

利用jQuery实现垂直菜单和水平菜单效果

1 利用jQuery实现垂直菜单 1.1 创建VerticalMenu.html文件,内容如下: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <link href="vertical.css" rel="stylesheet"> <script src="jquery-1

android如何写一个循环文字滚动的TextView

效果图: 在layout中这样来声明: <com.kaixin001.view.ScrollText android:id="@+id/news_statustxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="10dp" android:paddingRight="1

禁止uiscrollview垂直方向滚动,只允许水平方向滚动;或只允许垂直方向滚动

禁止UIScrollView垂直方向滚动,只允许水平方向滚动 scrollview.contentSize =  CGSizeMake(你要的长度, 0); 禁止UIScrollView水平方向滚动,只允许垂直方向滚动 scrollview.contentSize =  CGSizeMake(0, 你要的宽度); 在UIScrollView的SubView里面弹出一个pickerView的时候,想禁止UIScrollView滚动,如何实现? scrollView.scrollEnabled =

利用自定义View实现头像截取页面

在一些应用中,特别是有帐号体系的应用,通过相册选择图片和照相机拍照,然后对所获取的头像进行截取,最后获取选择框中的内容作为头像,一般效果会如下显示: 那么,如何制作这样一个效果呢,关键点在哪里呢? 做法其实可以有多种, 1)可以直接继承ImageView,然后在ImageView的OnDraw函数中直接绘制圆形或者方形的高亮圈: 2)也可以直接继承View,自己将Bitmap给传进来,在onDraw函数中先绘制图片,接着利用Path来绘制这个圆形或者方形的选择区域,利用Region.OP.DIF

翻译:Laravel-4-Generators 使用自定义代码生成工具快速进行Laravel开发

使用自定义代码生成工具快速进行Laravel开发 这个Laravle包提供了一种代码生成器,使得你可以加速你的开发进程,这些生成器包括: generate:model – 模型生成器 generate:view – 视图生成器 generate:controller – 控制器生成器 generate:seed – 数据库填充器 generate:migration – 迁移 generate:pivot – 关联表 generate:resource -资源 generate:scaffold

转:【译】Asp.net MVC 利用自定义RouteHandler来防止图片盗链

[译]Asp.net MVC 利用自定义RouteHandler来防止图片盗链 你曾经注意过在你服务器请求日志中多了很多对图片资源的请求吗?这可能是有人在他们的网站中盗链了你的图片所致,这会占用你的服务器带宽.下面这种方法可以告诉你如何在ASP.NET MVC中实现一个自定义RouteHandler来防止其他人盗链你的图片. 首先,我们来回顾一下当一个请求发往ASP.net MVC站点时的情景,IIS收到请求并将请求转到ASP.net,然后根据URL,或者更确切来说:被请求文件的扩展名.在IIS