上次已经实现了QQ汽泡的自定义View的效果【http://www.cnblogs.com/webor2006/p/7726174.html】,接着再将它应用到列表当中,这样才算得上跟QQ的效果匹配,下面开始:
RecyclerView的列表实现:
至于RecyclerView是如何使用的这里不过多讨论,不过之后有时间会对它的使用进行一个剖析滴,这里快速实现一个列表既可,如下:
要想使用它首先需要在gradle配置中添加它的依赖,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/ffd1d73fd92868ff5b83256dc80e26d0.png)
然后在布局中进行使用:
![](https://i-blog.csdnimg.cn/blog_migrate/c1bdf614cd29e72c62ca4e7d6c0222f9.png)
然后准备每个列表条目的布局:
![](https://i-blog.csdnimg.cn/blog_migrate/a3b59c6555d5a17f7584d19b213110aa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5feb547d71352c0792914cbaa7b501c4.png)
其汽泡背景定义如下:
![](https://i-blog.csdnimg.cn/blog_migrate/12199a9711b5cbc241d31296dbc08f16.png)
![](https://i-blog.csdnimg.cn/blog_migrate/aa51d21ade86b5ba651a1ecb2bd5ec95.png)
接着就可以给RecyclerView去绑定adapter并设置数据进行列表显示了:
MsgAdapter:
public class MsgAdapter extends Adapter<MsgAdapter.MyViewHolder> {
private List<Msg> msgList;
public MsgAdapter(List<Msg> msgList) {
this.msgList = msgList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_msg_view, null);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.tv_title.setText(msgList.get(position).title);
int unReadMsgCount = msgList.get(position).unReadMsgCount;
if (unReadMsgCount == 0) {//如果未读消息数为0则将汽泡隐藏
holder.tv_unReadMsgCount.setVisibility(View.INVISIBLE);
} else {
holder.tv_unReadMsgCount.setVisibility(View.VISIBLE);
holder.tv_unReadMsgCount.setText(unReadMsgCount + "");
}
}
@Override
public int getItemCount() {
return msgList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tv_title;
public TextView tv_unReadMsgCount;
public MyViewHolder(View itemView) {
super(itemView);
tv_title = (TextView) itemView.findViewById(R.id.tv_title);
tv_unReadMsgCount = (TextView) itemView.findViewById(R.id.tv_unReadMsgCount);
}
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/0608d2237d605e6739cf251582a70bb5.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/f8648f5323608545b580ffa547636891.png)
呃~~貌似显示不如预期,明显定义的条目布局的宽度是match_parent的,那为啥显示的效果却是wrap_content的呢?这个一百度就有网上的回答,具体原因这里不进行过多的探讨,这里按照网上的解答来进行修正:
![](https://i-blog.csdnimg.cn/blog_migrate/c9620a9cfe184fbc969a5ff6f78a5dda.png)
再次编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/a83074609518dc3bcae1a8134ed513eb.gif)
还是存在bug,也就是刚进来的时候还是wrap_content效果,但是一滑动列表之后就变成march_parent的了,那这又是为啥呢?继续百度找到了最终修复方案:
![](https://i-blog.csdnimg.cn/blog_migrate/b1294590bfe6ab1ff260b39e79f11e3c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1ed151635feffa856edefc31c504b7e2.png)
那修改代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/d36396e003cadb38f928a7695f39e5b2.png)
运行看效果:
![](https://i-blog.csdnimg.cn/blog_migrate/2ecc506b3a6a66208b749acaa5344577.gif)
ok,列表已经准备好了,继续下一步处理。
准备工作:
这一步来处理将GooView显示在列表当中,但是在显示之前,还得对咱们自定义的GooView进行一些额外的处理:
①、绘制文本:
为什么要绘制文本呢?这里分析一下最终的效果就可以知晓啦:
![](https://i-blog.csdnimg.cn/blog_migrate/fa28fdda88d6a3faf9e8d592cd80e976.png)
所以修改GooView代码,先写好绘制文本的框架:
/**
* Goo:是粘性的意思,QQ汽泡效果
* 先在拖拽圆上能显示出文本
*/
public class GooView extends View {
/* 拖拽圆移动的最大距离 */
private static final float MAX_DRAG_DISTANCE = 200f;
/* 固定圆缩放的最小半径 */
private static final float MIN_STABLE_RADIUS = 5f;
private Paint paint;
/* 固定圆的圆心 */
private PointF stableCenter = new PointF(200f, 200f);
/* 固定圆的半径 */
private float stableRadius = 20f;
/* 固定圆的两个附着点 */
private PointF[] stablePoints;
/* 拖拽圆的圆心 */
private PointF dragCenter = new PointF(100f, 100f);
/* 拖拽圆的半径 */
private float dragRadius = 20f;
/* 拖拽圆的两个附着点 */
private PointF[] dragPoints;
/* 绘制中间不规则的路径 */
private Path path;
/* 贝塞尔曲线的控制点 */
private PointF controlPoint;
/* 状态栏高度 */
private int statusBarHeight;
/* 是否拖拽已经超出最大范围了,超出则不绘制拖拽圆和中间图形实现拉断效果 */
private boolean isOutOfRange = false;
/* 是否全部消失,如果true则所有图形消失 */
private boolean isDisappear = false;
/* 在拖拽圆上显示的文本,由外部传进来 */
private String text;
public GooView(Context context) {
this(context, null);
}
public GooView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public GooView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
path = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(0, -statusBarHeight);//优雅的解决拖拽位置不是在圆心的问题,不用在触摸事件中单独处理了
//处理随着拖拽圆和固定圆的圆心距离越来越大,固定圆的半径越来越小:拖拽圆和固定圆圆心的距离百分比变化==固定圆半径的百分比变化
float distance = GeometryUtil.getDistanceBetween2Points(dragCenter, stableCenter);//获得两圆心的距离
float percent = distance / MAX_DRAG_DISTANCE;//计算百分比
float tempRadius = GeometryUtil.evaluateValue(percent, stableRadius, MIN_STABLE_RADIUS);//根据百分比来动态计算出固定圆的半径
if (tempRadius < MIN_STABLE_RADIUS)//处理最小半径
tempRadius = MIN_STABLE_RADIUS;
if (!isDisappear) {
if (!isOutOfRange) {//只有没有超出范围才绘制固定圆和中间图形
//绘制中间图形,直接基于两圆来绘制,其步骤为:
//一、基于两圆来计算真实的附着点与曲线的控制点
float dx = dragCenter.x - stableCenter.x;
float dy = dragCenter.y - stableCenter.y;
double linek = 0;
if (dx != 0) {//被除数不能为0,防止异常
linek = dy / dx;
}
//得到实际的附着点
dragPoints = GeometryUtil.getIntersectionPoints(dragCenter, dragRadius, linek);
stablePoints = GeometryUtil.getIntersectionPoints(stableCenter, tempRadius, linek);
controlPoint = GeometryUtil.getMiddlePoint(dragCenter, stableCenter);
//二、开始绘制中间图形
// 1、移动到固定圆的附着点1;
path.moveTo(stablePoints[0].x, stablePoints[0].y);
// 2、向拖拽圆附着点1绘制贝塞尔曲线;
path.quadTo(controlPoint.x, controlPoint.y, dragPoints[0].x, dragPoints[0].y);
// 3、向拖拽圆的附着点2绘制直线;
path.lineTo(dragPoints[1].x, dragPoints[1].y);
// 4、向固定圆的附着点2绘制贝塞尔曲线;
path.quadTo(controlPoint.x, controlPoint.y, stablePoints[1].x, stablePoints[1].y);
// 5、闭合;
path.close();
canvas.drawPath(path, paint);
path.reset();//解决滑动时路径重叠的问题
绘制固定圆
canvas.drawCircle(stableCenter.x, stableCenter.y, tempRadius, paint);
}
//绘制拖拽圆
canvas.drawCircle(dragCenter.x, dragCenter.y, dragRadius, paint);
drawText(canvas);
}
canvas.restore();
}
/**
* 绘制文本
*/
private void drawText(Canvas canvas) {
//TODO
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isOutOfRange = false;//方便调试
isDisappear = false;//方便调试
//event.getX();//点击的点距离当前自定义控件的左边缘的距离
float rawX = event.getRawX();//点击的点距离当前手机屏幕的左边缘的距离
float rawY = event.getRawY()/* - statusBarHeight*/;
dragCenter.set(rawX, rawY);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
rawX = event.getRawX();
rawY = event.getRawY();
dragCenter.set(rawX, rawY);
//当拖拽超出一定范围后,固定圆和中间图形都消失了,其"消失了"用程序来说就是在onDraw()中不绘制某一段了
//判断拖拽的距离,判断距离是否超出最大距离
float distance = GeometryUtil.getDistanceBetween2Points(stableCenter, dragCenter);
if (distance > MAX_DRAG_DISTANCE) {
//当超出最大距离则不再对固定圆和中间图形进行绘制
isOutOfRange = true;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
distance = GeometryUtil.getDistanceBetween2Points(stableCenter, dragCenter);
//判断在move的时候是否超出过最大范围
if (isOutOfRange) {
//判断up的时候是否在最大范围外
if (distance > MAX_DRAG_DISTANCE) {
isDisappear = true;
} else {
//up未超出最大范围,则将拖拽圆的圆心设置成固定圆圆心
dragCenter.set(stableCenter.x, stableCenter.y);
}
} else {
//move、up均未超出最大范围这时得有一个回弹还原效果:从拖拽抬手处到固定圆圆心之间来一个顺间平移动画,当到达固定圆
//圆心之后回弹一下
final PointF tempPointF = new PointF(dragCenter.x, dragCenter.y);//需要将up的瞬间拖拽圆的坐标记录下来以便进行平移动画
ValueAnimator va = ValueAnimator.ofFloat(distance, 0);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// float animatedValue = (float) animation.getAnimatedValue();//变化的具体值
float percent = animation.getAnimatedFraction();//变化的百分比
dragCenter = GeometryUtil.getPointByPercent(tempPointF, stableCenter, percent);
invalidate();
}
});
//动画插值器来实现回弹效果,然后回到原位
// va.setInterpolator(new OvershootInterpolator());
va.setInterpolator(new OvershootInterpolator(3));//其中回弹多少可以传参控制
va.setDuration(500);
va.start();
}
invalidate();
break;
}
//return super.onTouchEvent(event);
return true;//表示当前控件想要处理事件
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
statusBarHeight = getStatusBarHeight(this);
}
/**
* 获取状态栏高度
*
* @param view
* @return
*/
private int getStatusBarHeight(View view) {
Rect rect = new Rect();
//获取视图对应的可视范围,会把视图的左上右下的数据传入到一个矩形中
view.getWindowVisibleDisplayFrame(rect);
return rect.top;
}
public void setText(String text) {//具体的文本值由外面传过来
this.text = text;
}
}
而看一下绘制文本的函数:
![](https://i-blog.csdnimg.cn/blog_migrate/dbefe194c32ff57ccfa1c55012c2b2dd.png)
我们知道绘制文本的标准点是在左下角,那问题来了,如何将绘制的文本放到拖拽圆心中间呢?所以用下图来得出计算公式:
![](https://i-blog.csdnimg.cn/blog_migrate/11efd9f1460e0b57d5cdae5cd6bbf869.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a9e6ef9c7c00990470cc999852292f80.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6b54291b10537ec753f082eb56f1cfc1.png)
如何求得这个左下角的点呢?其实比较简单:
![](https://i-blog.csdnimg.cn/blog_migrate/1c8dd4ffca668efbd761271530a9d2f2.png)
![](https://i-blog.csdnimg.cn/blog_migrate/81efb0bbc10f8366f312d77fe5bf47fc.png)
所以x的值=拖拽圆圆心横坐标 - 文本宽度 * 0.5f,同理y的值=拖拽圆圆心纵坐标 + 文本高度 * 0.5f
那问题又来了,如何计算文本的这个矩形宽高呢?其实这个已经在之前学习过,有相应的API可以得到,如下:
/**
* Goo:是粘性的意思,QQ汽泡效果
* 先在拖拽圆上能显示出文本
*/
public class GooView extends View {
/* 拖拽圆移动的最大距离 */
private static final float MAX_DRAG_DISTANCE = 200f;
/* 固定圆缩放的最小半径 */
private static final float MIN_STABLE_RADIUS = 5f;
private Paint paint;
/* 固定圆的圆心 */
private PointF stableCenter = new PointF(200f, 200f);
/* 固定圆的半径 */
private float stableRadius = 20f;
/* 固定圆的两个附着点 */
private PointF[] stablePoints;
/* 拖拽圆的圆心 */
private PointF dragCenter = new PointF(100f, 100f);
/* 拖拽圆的半径 */
private float dragRadius = 20f;
/* 拖拽圆的两个附着点 */
private PointF[] dragPoints;
/* 绘制中间不规则的路径 */
private Path path;
/* 贝塞尔曲线的控制点 */
private PointF controlPoint;
/* 状态栏高度 */
private int statusBarHeight;
/* 是否拖拽已经超出最大范围了,超出则不绘制拖拽圆和中间图形实现拉断效果 */
private boolean isOutOfRange = false;
/* 是否全部消失,如果true则所有图形消失 */
private boolean isDisappear = false;
/* 在拖拽圆上显示的文本,由外部传进来 */
private String text;
/* 用来计算文本宽度的"空壳"矩形 */
private Rect textRect;
public GooView(Context context) {
this(context, null);
}
public GooView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public GooView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
path = new Path();
textRect = new Rect();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(0, -statusBarHeight);//优雅的解决拖拽位置不是在圆心的问题,不用在触摸事件中单独处理了
//处理随着拖拽圆和固定圆的圆心距离越来越大,固定圆的半径越来越小:拖拽圆和固定圆圆心的距离百分比变化==固定圆半径的百分比变化
float distance = GeometryUtil.getDistanceBetween2Points(dragCenter, stableCenter);//获得两圆心的距离
float percent = distance / MAX_DRAG_DISTANCE;//计算百分比
float tempRadius = GeometryUtil.evaluateValue(percent, stableRadius, MIN_STABLE_RADIUS);//根据百分比来动态计算出固定圆的半径
if (tempRadius < MIN_STABLE_RADIUS)//处理最小半径
tempRadius = MIN_STABLE_RADIUS;
if (!isDisappear) {
if (!isOutOfRange) {//只有没有超出范围才绘制固定圆和中间图形
//绘制中间图形,直接基于两圆来绘制,其步骤为:
//一、基于两圆来计算真实的附着点与曲线的控制点
float dx = dragCenter.x - stableCenter.x;
float dy = dragCenter.y - stableCenter.y;
double linek = 0;
if (dx != 0) {//被除数不能为0,防止异常
linek = dy / dx;
}
//得到实际的附着点
dragPoints = GeometryUtil.getIntersectionPoints(dragCenter, dragRadius, linek);
stablePoints = GeometryUtil.getIntersectionPoints(stableCenter, tempRadius, linek);
controlPoint = GeometryUtil.getMiddlePoint(dragCenter, stableCenter);
//二、开始绘制中间图形
// 1、移动到固定圆的附着点1;
path.moveTo(stablePoints[0].x, stablePoints[0].y);
// 2、向拖拽圆附着点1绘制贝塞尔曲线;
path.quadTo(controlPoint.x, controlPoint.y, dragPoints[0].x, dragPoints[0].y);
// 3、向拖拽圆的附着点2绘制直线;
path.lineTo(dragPoints[1].x, dragPoints[1].y);
// 4、向固定圆的附着点2绘制贝塞尔曲线;
path.quadTo(controlPoint.x, controlPoint.y, stablePoints[1].x, stablePoints[1].y);
// 5、闭合;
path.close();
canvas.drawPath(path, paint);
path.reset();//解决滑动时路径重叠的问题
绘制固定圆
canvas.drawCircle(stableCenter.x, stableCenter.y, tempRadius, paint);
}
//绘制拖拽圆
canvas.drawCircle(dragCenter.x, dragCenter.y, dragRadius, paint);
drawText(canvas);
}
canvas.restore();
}
/**
* 绘制文本
*/
private void drawText(Canvas canvas) {
//获得文本的边界:原理是将文本套入一个"完壳"矩形,这个矩形的宽高是文本的距离
paint.getTextBounds(this.text, 0, text.length(), textRect);
float x = dragCenter.x - textRect.width() * 0.5f;//为拖拽圆圆心横坐标 - 文本宽度 * 0.5f
float y = dragCenter.y + textRect.height() * 0.5f;//为拖拽圆圆心纵坐标 + 文本高度 * 0.5f
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isOutOfRange = false;//方便调试
isDisappear = false;//方便调试
//event.getX();//点击的点距离当前自定义控件的左边缘的距离
float rawX = event.getRawX();//点击的点距离当前手机屏幕的左边缘的距离
float rawY = event.getRawY()/* - statusBarHeight*/;
dragCenter.set(rawX, rawY);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
rawX = event.getRawX();
rawY = event.getRawY();
dragCenter.set(rawX, rawY);
//当拖拽超出一定范围后,固定圆和中间图形都消失了,其"消失了"用程序来说就是在onDraw()中不绘制某一段了
//判断拖拽的距离,判断距离是否超出最大距离
float distance = GeometryUtil.getDistanceBetween2Points(stableCenter, dragCenter);
if (distance > MAX_DRAG_DISTANCE) {
//当超出最大距离则不再对固定圆和中间图形进行绘制
isOutOfRange = true;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
distance = GeometryUtil.getDistanceBetween2Points(stableCenter, dragCenter);
//判断在move的时候是否超出过最大范围
if (isOutOfRange) {
//判断up的时候是否在最大范围外
if (distance > MAX_DRAG_DISTANCE) {
isDisappear = true;
} else {
//up未超出最大范围,则将拖拽圆的圆心设置成固定圆圆心
dragCenter.set(stableCenter.x, stableCenter.y);
}
} else {
//move、up均未超出最大范围这时得有一个回弹还原效果:从拖拽抬手处到固定圆圆心之间来一个顺间平移动画,当到达固定圆
//圆心之后回弹一下
final PointF tempPointF = new PointF(dragCenter.x, dragCenter.y);//需要将up的瞬间拖拽圆的坐标记录下来以便进行平移动画
ValueAnimator va = ValueAnimator.ofFloat(distance, 0);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// float animatedValue = (float) animation.getAnimatedValue();//变化的具体值
float percent = animation.getAnimatedFraction();//变化的百分比
dragCenter = GeometryUtil.getPointByPercent(tempPointF, stableCenter, percent);
invalidate();
}
});
//动画插值器来实现回弹效果,然后回到原位
// va.setInterpolator(new OvershootInterpolator());
va.setInterpolator(new OvershootInterpolator(3));//其中回弹多少可以传参控制
va.setDuration(500);
va.start();
}
invalidate();
break;
}
//return super.onTouchEvent(event);
return true;//表示当前控件想要处理事件
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
statusBarHeight = getStatusBarHeight(this);
}
/**
* 获取状态栏高度
*
* @param view
* @return
*/
private int getStatusBarHeight(View view) {
Rect rect = new Rect();
//获取视图对应的可视范围,会把视图的左上右下的数据传入到一个矩形中
view.getWindowVisibleDisplayFrame(rect);
return rect.top;
}
public void setText(String text) {
this.text = text;
}
}
那接下来就可以实现绘制文本的方法啦:
![](https://i-blog.csdnimg.cn/blog_migrate/aa0593ca956d0dfcf5081ed00c3c70ae.png)
另外由于目前画笔是红颜色的,而拖拽圆的颜色也是红色,所以需要改变一下画笔的颜色如下:
![](https://i-blog.csdnimg.cn/blog_migrate/959afa0ce01a286da7cc607d22921b9c.png)
这时代码已经写完了,接下来测试是否效果,先在主布局文件中声明一下它,然后在Activity中去给它设置一个文字,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/50c3f87f97c87d220f4b4d6f0c24243e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7898875a5275e376ee462c9f031aa409.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/2053775baa7ea5a552a0c777750c2fba.png)
嗯~~确实是达到预期的效果了,但是文本的字体大写太小了,下面来改一下:
![](https://i-blog.csdnimg.cn/blog_migrate/68413721ec45ab20f92421a0f97ce2d2.png)
再次运行:
![](https://i-blog.csdnimg.cn/blog_migrate/6adf33cab2686f6b42b418e04cf891d1.png)
②、修改GooView的位置:
接着还有一个准备工作需要处理,还是先看一下最终效果:
![](https://i-blog.csdnimg.cn/blog_migrate/ad3a7ed29d1ade5157f26a3f31d1b3f2.gif)
点击item中的汽泡view时实际是将我们自定义的GooView显示出来,然后将item的汽泡view隐藏从而达到最终效果,而是点到哪个位置,我们的GooView就显示在哪,而目前我们的拖拽圆和固定圆的圆心是写死的如下:
![](https://i-blog.csdnimg.cn/blog_migrate/11b45fcf87da880edb83f3149bb37976.png)
所以说需要一个方法可以动态去改变这两个圆心的值,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/73ef87dd1aebceac2b6fb8c1e8b1f46c.png)
这时再做下测试:
![](https://i-blog.csdnimg.cn/blog_migrate/7015c038d9bfa274564be2880698844e.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/549848c71f606d8d0c08a5ef37b15ae8.gif)
再更新一个位置:
![](https://i-blog.csdnimg.cn/blog_migrate/063fd87466c73917f5dd6e0d2bf7b30c.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/9a2153e76d958ed8a594ff04bea35fef.gif)
至此GooView的准备工作就已经到位了,还是将测试代码进行还原,继续下一个步骤。
为TextView设置触摸监听:
由于在点击Item中的汽泡TextView是需要将它隐藏并将我们写的GooView显示起来,另外还有一个滑动事件,所以需要给TextView设置触摸监听,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/cebe9e491ccc1e375a1cee8eb58e5bf3.png)
但是这样写在每一次bindView时都会new一个监听,这不是一种好的写法,应该只初始化一次既可,所以可以将这个匿名监听提出来,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/924b9fd094d541b25bc07d386074040c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9c8f647156172af12fc340234826af08.png)
这时运行看下监听是否打印出来了:
![](https://i-blog.csdnimg.cn/blog_migrate/906328fc224c6d7c203b88e14a531e07.gif)
利用WindowManager添加GooView:
在DOWN的时候应该将Item的TextView隐藏并添加我们的GooView,所以修改DOWN事件:
![](https://i-blog.csdnimg.cn/blog_migrate/00e860db7db64837907bafbd588bdd49.png)
接下来将咱们的GooView添加进来,如何添加呢?用WindowManager,像360安全卫士显示的悬浮效果其实就是用它来做的,在实际商用中也大量被用到,而像我们经常用的Toast也是用它,因为它允许在任务界面上添加一个额外的视图,修改代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/3d59721b566b807dc1d8c68c9381381d.png)
而此时这个监听就需要传一个Context过来了,所以需要修改调用代码:
![](https://i-blog.csdnimg.cn/blog_migrate/2cfa5ec626ffedf1e164b153f029212f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/38115987d1a67aa99e52255b68f8588c.png)
而在WindowManager中有一个addView方法,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/c67ff039028a1e72cc41bcfc3a9a249e.png)
其中要add的view既为我们的GooView,所以实例化它:
![](https://i-blog.csdnimg.cn/blog_migrate/c8fb4f6fe9bb73f4c7be047fae8d1916.png)
关于这个布局参数可以参考Toast的源码,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/27fc4b8091359b67ec7d160a431933db.png)
所以依葫芦画瓢:
![](https://i-blog.csdnimg.cn/blog_migrate/cd4a30d20d46a05f36a7e5cc1bff2005.png)
编译运行,会发现报错了:
![](https://i-blog.csdnimg.cn/blog_migrate/e275bb6c5a455b5cea27cd96e49221c9.png)
哦~~由于目前还木有传文本参数,所以为空了,而GooView中木有做判空,所以修复下:
![](https://i-blog.csdnimg.cn/blog_migrate/90901c89a1b9b1054baef59943be6e53.png)
再次运行:
![](https://i-blog.csdnimg.cn/blog_migrate/f1e680e8abc85543978bb12e5ffb8da4.gif)
嗯~~虽说是有bug,但是最起码能将咱们的GooView显示在屏幕上了,至于这BUG之后会慢慢去解决滴。
修复GooView的初始化文本和位置:
对于上一步可以看出其文本和当前x、y木有初始化,所以这里解决一下,其中x、y坐标应该是获取屏幕的x、y坐标,而非当前控件的x,y,所以如下:
![](https://i-blog.csdnimg.cn/blog_migrate/581bb58819bb3eaefc21b154ec04138d.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/aaa95ff426bb0a3ab8103f561058e1c9.gif)