版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 房产投资期权合同(2篇)
- 2024年粉末冶金自润滑材料合作协议书
- 2024年辐射自动观测仪项目建议书
- 2024年中枢兴奋药项目建议书
- 2024年淡水捕捞产品项目合作计划书
- 2024年皮革化学品:浸水助剂项目建议书
- 汽车销售居间服务合同
- 2024版个人门面房出租合同
- 2024版服装订购合同范本-0
- 2024版合同管理机构
- 污水处理厂药剂及材料采购投标文件
- 智慧图书馆系统建设方案
- 保靖县供水工程扩建项目可行性研究报告
- 电气工程专业英语词汇表
- 金龙JLCZ型化工流程泵说明书
- 新视野大学英语第三版读写教程Book1-Unit1-sectionB课文及翻译
- 火力发电厂系统简介
- 蒸汽管道试运行方案
- 九年级上册1-5单元测试
- 财务主管绩效考核指标
- 国开(电大)《商业银行经营管理》形考作业1-4参考答案
评论
0/150
提交评论