【移动应用开发技术】android中怎么实现长按选择文字功能_第1页
【移动应用开发技术】android中怎么实现长按选择文字功能_第2页
【移动应用开发技术】android中怎么实现长按选择文字功能_第3页
【移动应用开发技术】android中怎么实现长按选择文字功能_第4页
【移动应用开发技术】android中怎么实现长按选择文字功能_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】android中怎么实现长按选择文字功能

android中怎么实现长按选择文字功能,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1.实现原理原理其实也不难,简单总结就是:绘制文字时把显示的文字的坐标记录下来(记录文字的左上右上左下右下四个点坐标),作用就是为了计算滑动范围。执行了长按事件后,通过按的坐标,在当前显示的文字数据中根据点的坐标查找到按着的字,得到长按后选择的位置与文字。当执行滑动选择时,根据手指滑动的位置坐标与当前显示的文字数据匹配来确定选择的范围与文字。2.具体实现a.封装为了便于操作,首先对显示可见的字符、显示的行数据进行封装。ShowChar:public

class

ShowChar

{//可见字符数据封装

public

char

chardata

;//字符数据

public

Boolean

Selected

=false;//当前字符是否被选中

public

Point

TopLeftPosition

=

null;

public

Point

TopRightPosition

=

null;

public

Point

BottomLeftPosition

=

null;

public

Point

BottomRightPosition

=

null;

public

float

charWidth

=

0;//字符宽度

public

int

Index

=

0;//当前字符位置

}ShowLine:public

class

ShowLine

{//显示的行数据

public

List<ShowChar>

CharsData

=

null;

/**

*@return

*

*TODO

获取该行的数据

*

*/

public

String

getLineData(){

String

linedata

=

"";

if(CharsData==null||CharsData.size()==0)

return

linedata;

for(ShowChar

c:CharsData){

linedata

=

linedata+c.chardata;

}

return

linedata;

}

}说明:阅读器显示数据是一行一行的,每行都有不确定数量的字符,每个字符有自己的信息,比如字符宽度、字符在数据集合中的下标等。绘制时,通过绘制ShowLine去绘制每行的数据。b.数据转化绘制前,我们需要先要把数据转化为上面封装的格式数据以便我们使用。这个要怎么做?因为我们需要将字符串转化为一行一行的数据,同时每个字符的字符宽度需要测量出来。如果对绘制比较熟悉的话,应该会知道系统有个paint.measureText可以用来测量字符的宽度,这里可以借助这个来实现测量字符的宽度,同时转化为我们想要行数据。首先,写个方法,可以将传入的字符串转化为行数据:

/**

*@param

cs

*@param

medsurewidth

行测量的最大宽度

*@param

textpadding

字符间距

*@param

paint

测量的画笔

*@return

如果cs为空或者长度为0,返回null

*

*TODO

*

*/

public

static

BreakResult

BreakText(char[]

cs,

float

medsurewidth,

float

textpadding,

Paint

paint)

{

if(cs==null||cs.length==0){return

null;}

BreakResult

breakResult

=

new

BreakResult();

breakResult.showChars

=

new

ArrayList<ShowChar>();

float

width

=

0;

for

(int

i

=

0,

size

=

cs.length;

i

<

size;

i++)

{

String

mesasrustr

=

String.valueOf(cs[i]);

float

charwidth

=

paint.measureText(mesasrustr);

if

(width

<=

medsurewidth

&&

(width

+

textpadding

+

charwidth)

>

medsurewidth)

{

breakResult.ChartNums

=

i;

breakResult.IsFullLine

=

true;

return

breakResult;

}

ShowChar

showChar

=

new

ShowChar();

showChar.chardata

=

cs[i];

showChar.charWidth

=

charwidth;

breakResult.showChars.add(showChar);

width

+=

charwidth

+

textpadding;

}

breakResult.ChartNums

=

cs.length;

return

breakResult;

}

public

static

BreakResult

BreakText(String

text,

float

medsurewidth,

float

textpadding,

Paint

paint)

{

if

(TextUtils.isEmpty(text))

{

int[]

is

=

new

int[2];

is[0]

=

0;

is[1]

=

0;

return

null;

}

return

BreakText(text.toCharArray(),

medsurewidth,

textpadding,

paint);

}说明:BreakResult是对测量结果的简单封装:public

class

BreakResult

{

public

int

ChartNums

=

0;//测量了的字符数

public

Boolean

IsFullLine

=

false;//是否满一行了

public

List<ShowChar>

showChars

=

null;//测量了的字符数据

public

Boolean

HasData()

{

return

showChars

!=

null

&&

showChars.size()

>

0;

}

}完成了上面的工作后,我们可以实现将我们显示的数据转化为需要的数据了。下面是我们测试显示的字符串:String

TextData

=

"jEh话说天下大势,分久必合,合久必分。周末七国分争,并入于秦。及秦灭之后,楚、汉分争,又并入于汉。汉朝自高祖斩白蛇而起义,一统天下,后来光武中兴,传至献帝,遂分为三国。推其致乱之由,殆始于桓、灵二帝。桓帝禁锢善类,崇信宦官。及桓帝崩,灵帝即位,大将军窦武、太傅陈蕃共相辅佐。时有宦官曹节等弄权,窦武、陈蕃谋诛之,机事不密,反为所害,中涓自此愈横"

+

"建宁二年四月望日,帝御温德殿。方升座,殿角狂风骤起。只见一条大青蛇,从梁上飞将下来,蟠于椅上。帝惊倒,左右急救入宫,百官俱奔避。须臾,蛇不见了。忽然大雷大雨,加以冰雹,落到半夜方止,坏却房屋无数。建宁四年二月,洛阳地震;又海水泛溢,沿海居民,尽被大浪卷入海中。光和元年,雌鸡化雄。六月朔,黑气十余丈,飞入温德殿中。秋七月,有虹现于玉堂;五原山岸,尽皆崩裂。种种不祥,非止一端。帝下诏问群臣以灾异之由,议郎蔡邕上疏,以为堕鸡化,乃妇寺干政之所致,言颇切直。帝览奏叹息,因起更衣。曹节在后窃视,悉宣告左右;遂以他事陷邕于罪,放归田里。后张让、赵忠、封、段、曹节、侯览、蹇硕、程旷、夏恽、郭胜十人朋比为奸,号为“十常侍”。帝尊信张让,呼为“阿父”。朝政日非,以致天下人心思乱,盗贼蜂起。";我们需要将这段字符串转化为行数据,在初始化数据的操作,下面是初始化数据的方法initData:List<ShowLine>

mLinseData

=

null;

private

void

initData(int

viewwidth,

int

viewheight)

{

if

(mLinseData

==

null)

{

//将数据转化为行数据

mLinseData

=

BreakText(viewwidth,

viewheight);

}

}

private

List<ShowLine>

BreakText(int

viewwidth,

int

viewheight)

{

List<ShowLine>

showLines

=

new

ArrayList<ShowLine>();

while

(TextData.length()

>

0)

{

BreakResult

breakResult

=

TextBreakUtil.BreakText(TextData,

viewwidth,

0,

mPaint);

if

(breakResult

!=

null

&&

breakResult.HasData())

{

ShowLine

showLine

=

new

ShowLine();

showLine.CharsData

=

breakResult.showChars;

showLines.add(showLine);

}

else

{

break;

}

TextData

=

TextData.substring(breakResult.ChartNums);

}

int

index

=

0;

for

(ShowLine

l

:

showLines)

{

for

(ShowChar

c

:

l.CharsData)

{

c.Index

=

index++;

}

}

return

showLines;

}只要调用initData方法,我们就可以将TextData的数据转为显示的行数据Linedata集合mLinseData。值得注意的是,调用这个方法需求知道控件的长宽,根据view的生命周期,我们可以在onmeasures里面调用这个方法进行初始化。@Override

protected

void

onMeasure(int

widthMeasureSpec,

int

heightMeasureSpec)

{

super.onMeasure(widthMeasureSpec,

heightMeasureSpec);

int

viewwidth

=

getMeasuredWidth();

int

viewheight

=

getMeasuredHeight();

initData(viewwidth,

viewheight);

}数据转化完成后,接着我们需要把数据一行一行的绘制出来:

@Override

protected

void

onDraw(Canvas

canvas)

{

super.onDraw(canvas);

LineYPosition

=

TextHeight

+

LinePadding;//第一行显示的y坐标

for

(ShowLine

line

:

mLinseData)

{

DrawLineText(line,

canvas);//绘制每一行,并记录每个字符的坐标

}

}DrawLineText方法:private

void

DrawLineText(ShowLine

line,

Canvas

canvas)

{

canvas.drawText(line.getLineData(),

0,

LineYPosition,

mPaint);

float

leftposition

=

0;

float

rightposition

=

0;

float

bottomposition

=

LineYPosition

+

mPaint.getFontMetrics().descent;

for

(ShowChar

c

:

line.CharsData)

{

rightposition

=

leftposition

+

c.charWidth;

Point

tlp

=

new

Point();

c.TopLeftPosition

=

tlp;

tlp.x

=

(int)

leftposition;

tlp.y

=

(int)

(bottomposition

-

TextHeight);

Point

blp

=

new

Point();

c.BottomLeftPosition

=

blp;

blp.x

=

(int)

leftposition;

blp.y

=

(int)

bottomposition;

Point

trp

=

new

Point();

c.TopRightPosition

=

trp;

trp.x

=

(int)

rightposition;

trp.y

=

(int)

(bottomposition

-

TextHeight);

Point

brp

=

new

Point();

c.BottomRightPosition

=

brp;

brp.x

=

(int)

rightposition;

brp.y

=

(int)

bottomposition;

leftposition

=

rightposition;

}

LineYPosition

=

LineYPosition

+

TextHeight

+

LinePadding;

}运行一下,目前显示效果如下:实现这些后,接下来需要实现长按选择功能以及滑动选择文字功能。如何实现长按呢,自己写肯定可以,只是也太麻烦了,所以我们这里借助系统提供的长按事件就可以。我实现的思路是这样的,首先先将事件处理模式分四种:private

enum

Mode

{

Normal,

//正常模式

PressSelectText,//长按选中文字

SelectMoveForward,

//向前滑动选中文字

SelectMoveBack//向后滑动选中文字

}在没有做任何处理情况下是Normal模式,如果手势发生了,Down事件触发,记录当前Down的坐标,如果用户一直按着,必然触发长按事件,模式转化为PressSelectText,通过记录的Down的坐标,去数据集合中找到当前长按的字符,绘画出选择的文字的背景。思路是这样,那么就干吧。首先注册长按事件,在初始化使注册该事件。private

void

init()

{

mPaint

=

new

Paint();

mPaint.setAntiAlias(true);

mPaint.setTextSize(29);

mTextSelectPaint

=

new

Paint();

mTextSelectPaint.setAntiAlias(true);

mTextSelectPaint.setTextSize(19);

mTextSelectPaint.setColor(TextSelectColor);

mBorderPointPaint

=

new

Paint();

mBorderPointPaint.setAntiAlias(true);

mBorderPointPaint.setTextSize(19);

mBorderPointPaint.setColor(BorderPointColor);

FontMetrics

fontMetrics

=

mPaint.getFontMetrics();

TextHeight

=

Math.abs(fontMetrics.ascent)

+

Math.abs(fontMetrics.descent);

setOnLongClickListener(mLongClickListener);

}private

OnLongClickListener

mLongClickListener

=

new

OnLongClickListener()

{

@Override

public

boolean

onLongClick(View

v)

{

if

(mCurrentMode

==

Mode.Normal)

{

if

(Down_X

>

0

&&

Down_Y

>

0)

{//

说明还没释放,是长按事件

mCurrentMode

=

Mode.PressSelectText;

postInvalidate();//刷新

}

}

return

false;

}

};这里Down_X,Down_Y;初始化值都是-1,如果执行了down事件后它们肯定大于0,如果执行了Action_up事件,释放设置值为-1,只是为了判断使用而已。然后onDraw中需要判断一下并绘制选择的文字了。@Override

protected

void

onDraw(Canvas

canvas)

{

super.onDraw(canvas);

LineYPosition

=

TextHeight

+

LinePadding;//第一行的y坐标

for

(ShowLine

line

:

mLinseData)

{

DrawLineText(line,

canvas);//绘制每一

}

if

(mCurrentMode

!=

Mode.Normal)

{

DrawSelectText(canvas);//如果不是正常的话,绘制选择

}

}private

void

DrawSelectText(Canvas

canvas)

{

if

(mCurrentMode

==

Mode.PressSelectText)

{

DrawPressSelectText(canvas);//绘制长按选择的字符

}

else

if

(mCurrentMode

==

Mode.SelectMoveForward)

{//向前滑动选择

DrawMoveSelectText(canvas);//绘制滑动时选择的文字背景

}

else

if

(mCurrentMode

==

Mode.SelectMoveBack)

{//向后滑动选择

DrawMoveSelectText(canvas);//绘制滑动时选择的文字背景

}

}这时如果执行了长按事件,mCurrentMode==Mode.PressSelectText,将执行绘制长按选择的字符。

//绘制长按选中的数据

private

void

DrawPressSelectText(Canvas

canvas)

{

//根据按的坐标检测找到长按的字符

ShowChar

p

=

DetectPressShowChar(Down_X,

Down_Y);

if

(p

!=

null)

{//

找到了选择的字符

FirstSelectShowChar

=

LastSelectShowChar

=

p;

mSelectTextPath.reset();

mSelectTextPath.moveTo(p.TopLeftPosition.x,

p.TopLeftPosition.y);

mSelectTextPath.lineTo(p.TopRightPosition.x,

p.TopRightPosition.y);

mSelectTextPath.lineTo(p.BottomRightPosition.x,

p.BottomRightPosition.y);

mSelectTextPath.lineTo(p.BottomLeftPosition.x,

p.BottomLeftPosition.y);

//绘制文字背景

canvas.drawPath(mSelectTextPath,

mTextSelectPaint);

//绘制边界的线与指示块

DrawBorderPoint(canvas);

}

}检测点击点所在的字符方法:

/**

*@param

down_X2

*@param

down_Y2

*@return

*

*TODO

检测获取按压坐标所在位置的字符,没有的话返回null

*

*/

private

ShowChar

DetectPressShowChar(float

down_X2,

float

down_Y2)

{

for

(ShowLine

l

:

mLinseData)

{

for

(ShowChar

c

:

l.CharsData)

{

if

(down_Y2

>

c.BottomLeftPosition.y)

{

break;//

说明是在下一行

}

if

(down_X2

>=

c.BottomLeftPosition.x

&&

down_X2

<=

c.BottomRightPosition.x)

{

return

c;

}

}

}

return

null;

}基本上长按事件操作都完成了,我们运行长按文字看看效果:绘制了长按选择的字符后,我们需要实现按着左右的指示块进行左右或者上下滑动去选择文字。为了便于操作,向上滑动与向下滑动都有限制滑动范围,如下图:蓝色的区域是手指按着后触发允许滑动。按着左边的小蓝色区域,mCurrentMode==Mode.SelectMoveForward,允许向上滑动选择文字,就是手指滑动坐标滑动到黄色区域有效。按着右边的小蓝色区域,mCurrentMode==Mode.SelectMoveBack,允许向下滑动选择文字,就是手指滑动到绿色区域有效。选择时,我们只会记录两个字符,就是选择的文字的开始字符与结束字符:private

ShowChar

FirstSelectShowChar

=

null;

private

ShowChar

LastSelectShowChar

=

null;注意的是当长按选择一个字符后:FirstSelectShowChar=LastSelectShowChar;所以整个过程是:滑动时,如果按着左边的蓝色区域,将允许向前滑动,这时mCurrentMode==Mode.SelectMoveForward,向前滑动即在黄色区域滑动,这时就可以根据手指滑动坐标找到滑动后的FirstSelectShowChar,然后刷新界面。向下滑动同理。下面是代码实现:先在Action_Down里判断是向下滑动还是向下滑动,如果都不是,重置,使长按选择的文字恢复原样。case

MotionEvent.ACTION_DOWN:

Down_X

=

Tounch_X;

Down_Y

=

Tounch_Y;

if

(mCurrentMode

!=

Mode.Normal)

{

Boolean

isTrySelectMove

=

CheckIfTrySelectMove(Down_X,

Down_Y);

if

(!isTrySelectMove)

{//

如果不是准备滑动选择文字,转变为正常模式,隐藏选择框

mCurrentMode

=

Mode.Normal;

invalidate();

}

}

break;在滑动时判断,如果是向上滑动,检测获取当前滑动时的FirstSelectShowChar;如果是向下滑动,检测获取当前滑动时的LastSelectShowChar,然后刷新界面。case

MotionEvent.ACTION_MOVE:

if

(mCurrentMode

==

Mode.SelectMoveForward)

{

if

(CanMoveForward(event.getX(),

event.getY()))

{//

判断是否是向上移动

ShowChar

firstselectchar

=

DetectPressShowChar(event.getX(),

event.getY());//获取当前滑动坐标的下的字符

if

(firstselectchar

!=

null)

{

FirstSelectShowChar

=

firstselectchar;

invalidate();

}

}

}

else

if

(mCurrentMode

==

Mode.SelectMoveBack)

{

if

(CanMoveBack(event.getX(),

event.getY()))

{//

判断是否可以向下移动

ShowChar

lastselectchar

=

DetectPressShowChar(event.getX(),

event.getY());//获取当前滑动坐标的下的字符

if

(lastselectchar

!=

null)

{

LastSelectShowChar

=

lastselectchar;

invalidate();

}

}

}

break;判断是否向上滑动方法:private

boolean

CanMoveForward(float

Tounchx,

float

Tounchy)

{

Path

p

=

new

Path();

p.moveTo(LastSelectShowChar.TopRightPosition.x,

LastSelectShowChar.TopRightPosition.y);

p.lineTo(getWidth(),

LastSelectShowChar.TopRightPosition.y);

p.lineTo(getWidth(),

0);

p.lineTo(0,

0);

p.lineTo(0,

LastSelectShowChar.BottomRightPosition.y);

p.lineTo(LastSelectShowChar.BottomRightPosition.x,

LastSelectShowChar.BottomRightPosition.y);

p.lineTo(LastSelectShowChar.TopRightPosition.x,

LastSelectShowChar.TopRightPosition.y);

return

computeRegion(p).contains((int)

Tounchx,

(int)

Tounchy);

}判断是否向下滑动:private

boolean

CanMoveBack(float

Tounchx,

float

Tounchy)

{

Path

p

=

new

Path();

p.moveTo(FirstSelectShowChar.TopLeftPosition.x,

FirstSelectShowChar.TopLeftPosition.y);

p.lineTo(getWidth(),

FirstSelectShowChar.TopLeftPosition.y);

p.lineTo(getWidth(),

getHeight());

p.lineTo(0,

getHeight());

p.lineTo(0,

FirstSelectShowChar.BottomLeftPosition.y);

p.lineTo(FirstSelectShowChar.BottomLeftPosition.x,

FirstSelectShowChar.BottomLeftPosition.y);

p.lineTo(FirstSelectShowChar.TopLeftPosition.x,

FirstSelectShowChar.TopLeftPosition.y);

return

computeRegion(p).contains((int)

Tounchx,

(int)

Tounchy);

}private

Region

computeRegion(Path

path)

{

Region

region

=

new

Region();

RectF

f

=

new

RectF();

puteBounds(f,

true);

region.setPath(path,

new

Region((int)

f.left,

(int)

f.top,

(int)

f.right,

(int)

f.bottom));

return

region;

}手势操作处理完成了,剩

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论