版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】怎么在android中使用LabelView实现一个标签云效果
怎么在android中使用LabelView实现一个标签云效果?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。public
class
LabelView
extends
View
{
private
static
final
int
DIRECTION_LEFT
=
0;
//
向左
private
static
final
int
DIRECTION_RIGHT
=
1;
//
向右
private
static
final
int
DIRECITON_TOP
=
2;
//
向上
private
static
final
int
DIRECTION_BOTTOM
=
3;
//
向下
private
boolean
isStatic;
//
是否静止,
默认false,
可用干xml
:
label:is_static="false"
private
int[][]
mLocations;
//
每个label的位置
x/y
private
int[][]
mDirections;
//
每个label的方向
x/y
private
int[][]
mSpeeds;
//
每个label的x/y速度
x/y
private
int[][]
mTextWidthAndHeight;
//
每个labeltext的大小
width/height
private
String[]
mLabels;
//
设置的labels
private
int[]
mFontSizes;
//
每个label的字体大小
//
默认配色方案
private
int[]
mColorSchema
=
{0XFFFF0000,
0XFF00FF00,
0XFF0000FF,
0XFFCCCCCC,
0XFFFFFFFF};
private
int
mTouchSlop;
//
最小touch
private
int
mDownX
=
-1;
private
int
mDownY
=
-1;
private
int
mDownIndex
=
-1;
//
点击的index
private
Paint
mPaint;
private
Thread
mThread;
private
OnItemClickListener
mListener;
//
item点击事件
public
LabelView(Context
context,
AttributeSet
attrs)
{
this(context,
attrs,
0);
}
public
LabelView(Context
context,
AttributeSet
attrs,
int
defStyleAttr)
{
super(context,
attrs,
defStyleAttr);
TypedArray
ta
=
context.obtainStyledAttributes(attrs,
R.styleable.LabelView,
defStyleAttr,
0);
isStatic
=
ta.getBoolean(R.styleable.LabelView_is_static,
false);
ta.recycle();
mTouchSlop
=
ViewConfiguration.get(context).getScaledTouchSlop();
mPaint
=
new
Paint();
mPaint.setAntiAlias(true);
}
@Override
protected
void
onLayout(boolean
changed,
int
left,
int
top,
int
right,
int
bottom)
{
super.onLayout(changed,
left,
top,
right,
bottom);
init();
}
@Override
protected
void
onDraw(Canvas
canvas)
{
if(!hasContents())
{
return;
}
for
(int
i
=
0;
i
<
mLabels.length;
i++)
{
mPaint.setTextSize(mFontSizes[i]);
if(i
<
mColorSchema.length)
mPaint.setColor(mColorSchema[i]);
else
mPaint.setColor(mColorSchema[i-mColorSchema.length]);
canvas.drawText(mLabels[i],
mLocations[i][0],
mLocations[i][1],
mPaint);
}
}
@Override
public
boolean
onTouchEvent(MotionEvent
ev)
{
switch
(ev.getAction())
{
case
MotionEvent.ACTION_DOWN:
mDownX
=
(int)
ev.getX();
mDownY
=
(int)
ev.getY();
mDownIndex
=
getClickIndex();
break;
case
MotionEvent.ACTION_UP:
int
nowX
=
(int)
ev.getX();
int
nowY
=
(int)
ev.getY();
if
(nowX
-
mDownX
<
mTouchSlop
&&
nowY
-
mDownY
<
mTouchSlop
&&
mDownIndex
!=
-1
&&
mListener
!=
null)
{
mListener.onItemClick(mDownIndex,
mLabels[mDownIndex]);
}
mDownX
=
mDownY
=
mDownIndex
=
-1;
break;
}
return
true;
}
/**
*
获取当前点击的label的位置
*
@return
label的位置,没有点中返回-1
*/
private
int
getClickIndex()
{
Rect
downRect
=
new
Rect();
Rect
locationRect
=
new
Rect();
for(int
i=0;i<mLocations.length;i++)
{
downRect.set(mDownX
-
mTextWidthAndHeight[i][0],
mDownY
-
mTextWidthAndHeight[i][1],
mDownX
+
mTextWidthAndHeight[i][0],
mDownY
+
mTextWidthAndHeight[i][1]);
locationRect.set(mLocations[i][0],
mLocations[i][1],
mLocations[i][0]
+
mTextWidthAndHeight[i][0],
mLocations[i][1]
+
mTextWidthAndHeight[i][1]);
if(locationRersect(downRect))
{
return
i;
}
}
return
-1;
}
/**
*
开启子线程不断刷新位置并postInvalidate
*/
private
void
run()
{
if(mThread
!=
null
&&
mThread.isAlive())
{
return;
}
mThread
=
new
Thread(mStartRunning);
mThread.start();
}
private
Runnable
mStartRunning
=
new
Runnable()
{
@Override
public
void
run()
{
for(;;)
{
SystemClock.sleep(100);
for
(int
i
=
0;
i
<
mLabels.length;
i++)
{
if
(mLocations[i][0]
<=
getPaddingLeft())
{
mDirections[i][0]
=
DIRECTION_RIGHT;
}
if
(mLocations[i][0]
>=
getMeasuredWidth()
-
getPaddingRight()
-
mTextWidthAndHeight[i][0])
{
mDirections[i][0]
=
DIRECTION_LEFT;
}
if(mLocations[i][1]
<=
getPaddingTop()
+
mTextWidthAndHeight[i][1])
{
mDirections[i][1]
=
DIRECTION_BOTTOM;
}
if
(mLocations[i][1]
>=
getMeasuredHeight()
-
getPaddingBottom())
{
mDirections[i][1]
=
DIRECITON_TOP;
}
int
xSpeed
=
1;
int
ySpeed
=
2;
if(i
<
mSpeeds.length)
{
xSpeed
=
mSpeeds[i][0];
ySpeed
=
mSpeeds[i][1];
}
else
{
xSpeed
=
mSpeeds[i-mSpeeds.length][0];
ySpeed
=
mSpeeds[i-mSpeeds.length][1];
}
mLocations[i][0]
+=
mDirections[i][0]
==
DIRECTION_RIGHT
?
xSpeed
:
-xSpeed;
mLocations[i][1]
+=
mDirections[i][1]
==
DIRECTION_BOTTOM
?
ySpeed
:
-ySpeed;
}
postInvalidate();
}
}
};
/**
*
初始化位置、方向、label宽高
*
并开启线程
*/
private
void
init()
{
if(!hasContents())
{
return;
}
int
minX
=
getPaddingLeft();
int
minY
=
getPaddingTop();
int
maxX
=
getMeasuredWidth()
-
getPaddingRight();
int
maxY
=
getMeasuredHeight()
-
getPaddingBottom();
Rect
textBounds
=
new
Rect();
for
(int
i
=
0;
i
<
mLabels.length;
i++)
{
int[]
location
=
new
int[2];
location[0]
=
minX
+
(int)
(Math.random()
*
maxX);
location[1]
=
minY
+
(int)
(Math.random()
*
maxY);
mLocations[i]
=
location;
mFontSizes[i]
=
15
+
(int)
(Math.random()
*
30);
mDirections[i][0]
=
Math.random()
>
0.5
?
DIRECTION_RIGHT
:
DIRECTION_LEFT;
mDirections[i][1]
=
Math.random()
>
0.5
?
DIRECTION_BOTTOM
:
DIRECITON_TOP;
mPaint.setTextSize(mFontSizes[i]);
mPaint.getTextBounds(mLabels[i],
0,
mLabels[i].length(),
textBounds);
mTextWidthAndHeight[i][0]
=
textBounds.width();
mTextWidthAndHeight[i][1]
=
textBounds.height();
}
if(!isStatic)
run();
}
/**
*
是否设置label
*
@return
true
or
false
*/
private
boolean
hasContents()
{
return
mLabels
!=
null
&&
mLabels.length
>
0;
}
/**
*
设置labels
*
@see
setLabels(String[]
labels)
*
@param
labels
*/
public
void
setLabels(List<String>
labels)
{
setLabels((String[])
labels.toArray());
}
/**
*
设置labels
*
@param
labels
*/
public
void
setLabels(String[]
labels)
{
mLabels
=
labels;
mLocations
=
new
int[labels.length][2];
mFontSizes
=
new
int[labels.length];
mDirections
=
new
int[labels.length][2];
mTextWidthAndHeight
=
new
int[labels.length][2];
mSpeeds
=
new
int[labels.length][2];
for(int
speed[]
:
mSpeeds)
{
speed[0]
=
speed[1]
=
1;
}
requestLayout();
}
/**
*
设置配色方案
*
@param
colorSchema
*/
public
void
setColorSchema(int[]
colorSchema)
{
mColorSchema
=
colorSchema;
}
/**
*
设置每个item的x/y速度
*
<p>
*
speeds.length
>
labels.length
忽略多余的
*
<p>
*
speeds.length
<
labels.length
将重复使用
*
*
@param
speeds
*/
public
void
setSpeeds(int[][]
speeds)
{
mSpeeds
=
speeds;
}
/**
*
设置item点击的监听事件
*
@param
l
*/
public
void
setOnItemClickListener(OnItemClickListener
l)
{
getParent().requestDisallowInterceptTouchEvent(true);
mListener
=
l;
}
/**
*
item的点击监听事件
*/
public
interface
OnItemClickListener
{
public
void
onItemClick(int
index,
String
label);
}
}上来先弄了4个常量上去,干嘛用的呢?是要判断每个item的方向的,因为当达到某个边界的时候,item要向相反的方向移动。第二个构造方法中,获取了一个自定义属性,还有就是初始化的Paint。继续看onLayout,其实onLayout我们什么都没干,只是调用了init方法,来看看init方法。/**
*
初始化位置、方向、label宽高
*
并开启线程
*/
private
void
init()
{
if(!hasContents())
{
return;
}
int
minX
=
getPaddingLeft();
int
minY
=
getPaddingTop();
int
maxX
=
getMeasuredWidth()
-
getPaddingRight();
int
maxY
=
getMeasuredHeight()
-
getPaddingBottom();
Rect
textBounds
=
new
Rect();
for
(int
i
=
0;
i
<
mLabels.length;
i++)
{
int[]
location
=
new
int[2];
location[0]
=
minX
+
(int)
(Math.random()
*
maxX);
location[1]
=
minY
+
(int)
(Math.random()
*
maxY);
mLocations[i]
=
location;
mFontSizes[i]
=
15
+
(int)
(Math.random()
*
30);
mDirections[i][0]
=
Math.random()
>
0.5
?
DIRECTION_RIGHT
:
DIRECTION_LEFT;
mDirections[i][1]
=
Math.random()
>
0.5
?
DIRECTION_BOTTOM
:
DIRECITON_TOP;
mPaint.setTextSize(mFontSizes[i]);
mPaint.getTextBounds(mLabels[i],
0,
mLabels[i].length(),
textBounds);
mTextWidthAndHeight[i][0]
=
textBounds.width();
mTextWidthAndHeight[i][1]
=
textBounds.height();
}
if(!isStatic)
run();
}init方法中,上来先判断一下,是否设置了标签,如果没有设置直接返回,省得事多。10~13行,目的就是获取item在该view中移动的上下左右边界,毕竟item还是要在整个view中移动的嘛,不能超出了view的边界。17行,开始一个for循环,去遍历所有的标签。18~20行,是随机初始化一个位置,所以,我们的标签每次出现的位置都是随机的,并没有什么规律,但接下来的移动是有规律的,总不能到处乱蹦吧。接着,22行,保存了这个位置,因为我们下面要不断的去修改这个位置。23行,随机了一个字体大小,24、25行,随机了该标签x/y初始的方向。27行,去设置了当前标签的字体大小,28行,是获取标签的宽度和高度,并在下面保存在了一个二维数组中,为什么是二维数组,我们有多个标签嘛,每个标签都要保存它的宽度和高度。最后,如果我们没有显示的声明labelview是静止的,则去调用run方法。继续跟进代码,看看run方法的内脏。/**
*
开启子线程不断刷新位置并postInvalidate
*/
private
void
run()
{
if(mThread
!=
null
&&
mThread.isAlive())
{
return;
}
mThread
=
new
Thread(mStartRunning);
mThread.start();
}5~7行,如果线程已经开启,直接return防止多个线程共存,这样造成的后果就是标签越来越快。9、10行,去启动一个线程,并有一个mStartRunning的Runnable参数。那么我们继续来看看这个Runnable。private
Runnable
mStartRunning
=
new
Runnable()
{
@Override
public
void
run()
{
for(;;)
{
SystemClock.sleep(100);
for
(int
i
=
0;
i
<
mLabels.length;
i++)
{
if
(mLocations[i][0]
<=
getPaddingLeft())
{
mDirections[i][0]
=
DIRECTION_RIGHT;
}
if
(mLocations[i][0]
>=
getMeasuredWidth()
-
getPaddingRight()
-
mTextWidthAndHeight[i][0])
{
mDirections[i][0]
=
DIRECTION_LEFT;
}
if(mLocations[i][1]
<=
getPaddingTop()
+
mTextWidthAndHeight[i][1])
{
mDirections[i][1]
=
DIRECTION_BOTTOM;
}
if
(mLocations[i][1]
>=
getMeasuredHeight()
-
getPaddingBottom())
{
mDirections[i][1]
=
DIRECITON_TOP;
}
int
xSpeed
=
1;
int
ySpeed
=
2;
if(i
<
mSpeeds.length)
{
xSpeed
=
mSpeeds[i][0];
ySpeed
=
mSpeeds[i][1];
}else
{
xSpeed
=
mSpeeds[i-mSpeeds.length][0];
ySpeed
=
mSpeeds[i-mSpeeds.length][1];
}
mLocations[i][0]
+=
mDirections[i][0]
==
DIRECTION_RIGHT
?
xSpeed
:
-xSpeed;
mLocations[i][1]
+=
mDirections[i][1]
==
DIRECTION_BOTTOM
?
ySpeed
:
-ySpeed;
}
postInvalidate();
}
}
};这个Runnable其实才是标签云实现的关键,我们就是在这个线程中去修改每个标签的位置,并通知view去重绘的。而且可以看到,在run中是一个死循环,这样我们的标签才能无休止的移动,接下来就是让线程去休息100ms,总不能一个劲的去移动吧,速度太快了也不好,也要考虑性能问题。接下来第7行,去遍历所有的标签,8~23行,通过判断当前的位置是不是达到了某个边界,如果到了,则修改方向为相反的方向,例如现在到了view的最上面,那接下来,这个标签就得往下移动了。25、26行,默认了x/y的速度,为什么是说默认了呢,因为每个标签的x/y速度我们都可以通过方法去设置。接下来28~34行,做了一个判断,大体意思就是:如果设置的那些速度总数大于当前标签在标签s中的位置,则去找对应位置的速度,否则,重新从前面获取速度。36、37行就是根据x/y上的方向去修改当前标签的坐标了。最后,调用了postInvalidate(),通知view去刷新界面,这里是用的postInvalidate()因为我们是在线程中调用的,切记。postInvalidate()后,肯定就要走onDraw()去绘制这些标签了,那么我们就来看看onDraw吧。@Override
protected
void
onDraw(Canvas
canvas)
{
if(!hasContents())
{
return;
}
for
(int
i
=
0;
i
<
mLabels.length;
i++)
{
mPaint.setTextSize(mFontSizes[i]);
if(i
<
mColorSchema.length)
mPaint.setColor(mColorSchema[i]);
else
mPaint.setColor(mColorSchema[i-mColorSchema.length]);
canvas.drawText(mLabels[i],
mLocations[i][0],
mLocations[i][1],
mPaint);
}
}上来还是判断了一下,如果没有设置标签,直接返回。如果有标签,那么去遍历所有标签,并设置对应的字体大小,还记得吗?我们在初始化的时候随机了每个标签的字体大小,接下来去设置该标签的颜色,一个ifelse原理和设置速度那个是一样的,最关键的就是下面,调用了canvas.drawText()将该标签画到屏幕上,mLocations中我们是保存了每个标签的位置,而且是在线程中不断的去修改这个位置的。到这里,其实我们的LabelView就能动起来了,不过那几个设置标签,速度,颜色的方法还有说。其实很简单,来看一下吧。/**
*
设置labels
*
@see
setLabels(String[]
labels)
*
@param
labels
*/
public
void
setLabels(List<String>
labels)
{
setLabels((String[])
labels.toArray());
}
/**
*
设置labels
*
@param
labels
*/
public
void
setLabels(String[]
labels)
{
mLabels
=
labels;
mLocations
=
new
int[labels.length][2];
mFontSizes
=
new
int[labels.length];
mDirections
=
new
int[labels.length][2];
mTextWidthAndHeight
=
new
int[labels.length][2];
mSpeeds
=
new
int[labels.length][2];
for(int
speed[]
:
mSpeeds)
{
speed[0]
=
speed[1]
=
1;
}
requestLayout();
}
/**
*
设置配色方案
*
@param
colorSchema
*/
public
void
setColorSchema(int[]
colorSchema)
{
mColorSchema
=
colorSchema;
}
/**
*
设置每个item的x/y速度
*
<p>
*
speeds.length
>
labels.length
忽略多余的
*
<p>
*
speeds.length
<
labels.length
将重复使用
*
*
@param
speeds
*/
public
void
setSpeeds(int[][]
speeds)
{
mSpeeds
=
speeds;
}这几个蛋疼的方法中,唯一可说的就是setLabels(String[]labels)了,因为在这个方法中还做了点工作。仔细观察,这方法除了设置了标签s外,其他的就是初始化了几个数组,都表示什么,相信都应该很清楚了,还有就是在这里我们初始化了默认速度为1。刚上来做演示的时候,LabelView还能item点击,这是怎么做到的呢?普通的onClick肯定是不行的,因为我们并不知道点击的x/y坐标,所以只能通过onTouchEvent入手了。@Override
public
boolean
onTouchEvent(MotionEvent
ev)
{
switch
(ev.getAction())
{
case
MotionEvent.ACTION_DOWN:
mDownX
=
(int)
ev.getX();
mDownY
=
(int)
ev.getY();
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 语文考试命题思路剖析
- 螺蛳粉直播脚本(模板)
- DB37T 4714-2024 鲜食葡萄避雨栽培技术规程
- 2024年高、低能校正磁铁项目发展计划
- 2024年特种纤维布项目发展计划
- 电子线路装调 课件 B1-1能正确装接单相半波整流电路
- 2024年印刷机械专用配件:胶辊合作协议书
- 2024年止血用医用生物蛋白胶合作协议书
- 物流营销与客户关系 课件 项目六 提升物流客户满意度课件-顾婷
- 2024年人造纤维(纤维素纤维)项目合作计划书
- (公司)股权代持协议
- 医院保洁管理措施
- 县民政局开展安全生产专项整治工作情况汇报
- 塞焊技术要求及调整措施
- 上海建设工程现场用户统一登录平台操作说明
- 典范英语6-4Oh,Otto
- 对账与结账(精华版)ppt课件
- 文件签收回执单
- 分数的意义分数单位.ppt
- 逸出功的测定
- 上海市建设工程材料使用监督管理规定
评论
0/150
提交评论