热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

TextView自动垂直滚动

先说说它的优点吧:1.当view的大小容不下文字的时候,这个view有循环滚动文字的能力。2.滚动的时候轻轻点击它,会停止滚动。3.停止滚动时轻轻点击它,又会继续滚动。4.可以通过手指拖动文字的显示位

先说说它的优点吧:
1.当view的大小容不下文字的时候,这个view有循环滚动文字的能力。
2.滚动的时候轻轻点击它,会停止滚动。
3.停止滚动时轻轻点击它,又会继续滚动。
4.可以通过手指拖动文字的显示位置。
5.当view的大小能容下文字的时候,它不会滚动,也不会响应手指拖动。


适用范围:
1.扩展成小说阅读器
2.公告栏、小窗口展示消息或通知
3.滚动新闻
4.可以扩展成支持多种字体滚动播放

技术难点提要:
1.换行处理及英文切词
2.测量view的长度和高度、能否滚动的判断条件
3.循环滚动的实现
4.动画的实现
6.手指托动文字
7.手指控制滚动

用到的api:

paint.measureText(string):测量paint画String所需要的宽度
view.requestLayout():重新布局
vew.invalidate():刷新view
canvas.drawText():画文字
textview.getLineHeight():获取行高

 

先说说中文的换行算法吧:

主要是用paint.measureText(string)方法去计算要画string的长度
例如有一个句子:你好,我是小明,很高兴认识大家!
首先得知道一行的最大宽度,比如最大宽度为120;
系统会先计算第一个字符“你”的长度,然后与最大宽度对比,如果小于最大宽度就计算前两个字符“你好”的长度,如果“你好”还是小于最大宽度120,就计算“你好,”,一直循环下去,假如到了“你好,我是小明,很高”时发现刚好超过120,那第一行就是“你好,我是小明,很”;然后对剩下的字符“高兴认识大家!”进行上述处理,把切出来的行保存到lineStrings里;
以下是代码与说明(以下代码把英文字符排除在外,只考虑中文字符):

 

/** 
* 获取一行的字符
*
* @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 char c = str.charAt(i);
String add = "";

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();

}


2.测量view的长度和高度、能否滚动的判断条件

/** 
* 测量高度
*
* @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;
}



 

view的高度可以通过xml配置得来,也就是onMeasure的时候,而absloutHeight是需要看文字有多少行。前面已经讲过换行算法,行数不难求出:lineString.size()
那么计算文字的真实高度就不难了:
可以把lineStrings.size()*getLineheight()就能算出真实高度。
代码就是这样实现的(exactlyHeight可以先无视):

 

3.循环滚动的实现
首先需要知道什么时候才会滚动:
当view的高度低于文字的高度的时候会出现滚动,也就是:
exactlyHeight 这里给一张示意图来表示exactlyHeight与absloutHeight的区别:黄色区域是文字区域,灰色区域是这个view的可见区域

注意:当xml里配置view的高度为wrap_content是不会滚动的,因为它刚好能容纳文字,只有当配置为fill_parent和具体值时,才会滚动.回顾一下exactlyHeight是如何赋值的:

/** 
* 测量高度
*
* @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;
}


以上的所有准备工作做好了,就可以开始画了:
如果不考虑滚动,那么就直接一个for循环把lineStrings画完就结束了,但现在要考虑滚动,必需在它们for循环的基础上做一个y方向上的位移,而且这个位移会变化,我们可以用一个变量来定义它currentY

 这里onDraw()方法是精髓。先看一张滚动示意图,此图描述了几个滚动的关键状态:

不难看出,当y值小于exactlyHeight - absloutHeight时就得让它循环画在view的可见范围内我信就让y=y+absloutHeight,但是当y
y >=exactlyHeight - absloutHeight&& y 详情如图示:

另外当向下滚动时如果y >= absloutHeight时也是需要在顶端画出一部分文字

 

 

@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 y = lineHeight * i + textSize + currentY;

float min = 0;
if (exactlyHeight > -1) {
min = Math.min(min, exactlyHeight - absloutHeight);
}
if (y
y = y + absloutHeight;

} else if (y >= min && y
//如果最顶端的文字已经到达需要循环从下面滚出的时候
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());
}
}


4.动画的实现
这一块简单,只需要不停的用handler发消息控制currentY自增操作就ok了,为了不让currentY越界,让它在absloutHeight与-absloutHeight之间

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;
}

}
};


5.手指托动文字
手指托动主要是在ontouch里写代码,在move的时候记录前一次y坐标,然后根据当前这次move事件与上次move事件的差值,得到滚动的距离。
move事件先上代码:

switch (event.getAction()) { 
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;


6.手指控制滚动

手指控制滚动主要在ontouch里的down和up/cancel事件里处理,当手指位移不超过performUpScrollStateDistance值时,表示手指是点击而不是拖动,那么就让它updateScrollStatus,这里updateScrollStatus就是让它更改滚动状态

/** 
* 更改滚动状态
*/
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;
}
}







case MotionEvent.ACTION_DOWN:
distanceY = lastY = event.getY();
distanceX = event.getX();
pause();

case MotionEvent.ACTION_CANCEL:
goOn();
float y = event.getY() - distanceY;
float x = event.getX() - distanceX;

if (Math.sqrt(y * y + x * x) updateScrollStatus();
}
return true;

}


 


推荐阅读
  • android 中 TextView.setText(CharSequence text, BufferType type)的了解
    我们经常用TextView.setText(“”);的方法,但是TextView有一个另外一个俩个入参,之前不太知道其用法,现在记录一下效果图image.png核心代码***实现同 ... [详细]
  • 概述Android开发过程中,经常遇到Textview展示不完全的情况。遇到此情况,通常的处理是:方案一Textview添加android:ellipsize属性,让展示不 ... [详细]
  • 如何在Android中利用TextView对字体颜色进行更改?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • Android中怎么利用TextView显示部分文字高亮
    这篇文章将为大家详细讲解有关Android中怎么利用TextView显示部分文字高亮,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇 ... [详细]
  • 效果如图显示:textView(TextView)findViewById(R.id.textview);SpannableStringBuilderbuildernewSpanna ... [详细]
  • 1.范例说明EditTextWidget设计的初衷是为了等待User输入而准备的,那么在User输入的同时,又该如何拦截所输入的文字呢?Android的多数Widget都有 ... [详细]
  • 转载至:http:blog.csdn.netlmj623565791articledetails24252901不过对原作进行了相关细节优化,所以才有此文..首先我们看一 ... [详细]
  • TextView属性详解:autoLink设置  是否当文本为URL链接email电话号码map时,文本显示为可点击的链接。可选值(nonewebemailphonema ... [详细]
  • Android TextView 设置多种颜色
    关键词Android、TextView、多种颜色摘要由于项目开发需要,一个TextView为了强调内容,需要显示不同的字体颜色和大小效果图TextView效果图.png方法一int ... [详细]
  • Android TextView预渲染研究
    AndroidTextView预渲染研究-Android中的TextView是整个framework中最复杂的控件之一,负责Android中显示文本的大部分工作,framwork中 ... [详细]
  • Android TextView利用measureText自适应文本字体大小宽度
    AndroidTextView利用measureText自适应文本字体大小宽度常常有这种情况,UI设计师限定一个文本TextView的宽度值比如80dip,但是该文本长度 ... [详细]
author-avatar
沉鈺发忠谦琪伦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有