Android自定义Layout实现QQ消息拖动效果
来源:互联网 发布:seo专员做什么的 编辑:程序博客网 时间:2024/05/21 08:48
Android自定义Layout实现QQ消息拖动效果
前些天看到有人发了片类似的文章,然后今天没事做就自己动手,搞了一个。
作为刚入门的新手,感觉有必要练练手。
先给大家看看效果。
接着分析下思路
自定义一个组件/布局
- 重写Draw方法
- 贝塞尔曲线
- 动画
第一种
第二种
首先了解下原理—— 一个小圆,一个大圆,外加两条贝塞尔曲线,然后填充即可。
两个圆还好,只要知道半径和圆心坐标即可。我们主要分析下这两条贝塞尔曲线应该怎么画,
在这里我就不介绍贝塞尔曲线了,大家可以去百度查查贝塞尔曲线绘制原理,
画一条贝塞尔曲线需要两个数据点,和一个控制点,这三个点应该怎么确定呢?
通过我画的图,应该大家马上就能知道了,a1-b1即为贝塞尔曲线的数据点,连接两个圆的圆心,
这条线的一半即可作为控制点,接下来就是计算a1 a2 b1 b2 以及控制点的坐标了,这个我就不啰嗦了,
无非就是三角函数,如果忘记的画,百度上复习下即可。
既然知道原理了,那么我们应该在哪里画呢?
有人可能会说自定义View即可,其实是错误的,View是一个控件 他是有一个范围的,比如一个按钮,
我们总不可能做出这样一个东西,只能在按钮范围里面拖动吧。
所以正在的做法是自定义一个ViewGroup,作为底层布局。
接着,很多同学可能会直接重写onDraw方法,接在在onDraw方法里面画,其实这样做也是错误的,为什么呢?
因为你画完后,然后在这个布局里面添加其他控件,你会发现你画的东西在控件地下,相当于背景了,
我们的本意是需要他绘制在顶层,这并不满足我们的要求,所以这里大家要注意了。
其实onDraw本来就是用来画背景的,
按照源码的注释,View的绘制过程是这样子的:
先绘制背景,如果有必要,保存画布层,绘制内容,绘制孩子,绘制渐变,绘制装饰物。
/* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */
先绘制背景,如果有必要,保存画布层,绘制内容,绘制孩子,绘制渐变,绘制装饰物。
由此可知,我们应该通过绘制子孩子,来达到绘制在顶层
我们正在需要重写的方法应该是dispatchDraw(),绘制方法同onDraw一样。
代码实现
首先计算各点坐标
第一种方法,通过三角函数计算
<span style="white-space:pre"></span> // 获得角度α x = (float) Math.atan((control.y - centerY) / (control.x - centerX)); // 获取定点到移动点的距离 distance = (float) Math.sqrt(Math.pow((control.y - centerY), 2) + Math.pow((control.x - centerX), 2)); // 第一个圆圈随着distance距离的增加而缩小 r = 70 * (1 - distance / 1000); // 第一个圆圈的贝塞尔曲线点 a1.set(centerX - r * (float) Math.sin(x), centerY + r * (float) Math.cos(x)); a2.set(centerX + r * (float) Math.sin(x), centerY - r * (float) Math.cos(x)); // 第二个圆圈的贝塞尔曲线点 b1.set(control.x - R * (float) Math.sin(x), control.y + R * (float) Math.cos(x)); b2.set(control.x + R * (float) Math.sin(x), control.y - R * (float) Math.cos(x)); // 贝塞尔曲线控制点 half_control.set((centerX + control.x) / 2, (centerY + control.y) / 2);
第二种方法,通过相似三角形计算
<span style="white-space:pre"></span> // 获取定点到移动点的距离 distance = (float) Math.sqrt(Math.pow((control.y - centerY), 2) + Math.pow((control.x - centerX), 2)); // 第一个圆圈随着distance距离的增加而缩小 r = 70 * (1 - distance / 1000); // 第一个圆圈的贝塞尔曲线点 a1.set(centerX - r / distance * (control.y - centerY), centerY + r / distance * (control.x - centerX)); a2.set(centerX + r / distance * (control.y - centerY), centerY - r / distance * (control.x - centerX)); // 第二个圆圈的贝塞尔曲线点 b1.set(control.x - R / distance * (control.y - centerY), control.y + R / distance * (control.x - centerX)); b2.set(control.x + R / distance * (control.y - centerY), control.y - R / distance * (control.x - centerX)); // 贝塞尔曲线控制点 half_control.set((centerX + control.x) / 2, (centerY + control.y) / 2);
第一个画圆还好,直接给定半径以及圆心坐标,然后调用drawCircle()方法即可,
// 画第一个圆 canvas.drawCircle(centerX, centerY, r, mPaint);在这里,我们看到一个mPaint参数,这个就是我们所谓的画笔
// 画笔属性设置 Paint mPaint = new Paint; mPaint.setAntiAlias(true); // 设置没有锯齿 //mPaint.setStyle(Paint.Style.STROKE); //空心 mPaint.setStyle(Paint.Style.FILL); //实心 mPaint.setColor(Color.RED); mPaint.setStrokeWidth(4);第二个圆,也就是我们拖动的圆,绘制方法同第一个圆一样,只是这个圆的圆心是跟随着我们的手变化的,所以圆心坐标也是变化的。
这个坐标可以通过onTouchEvent来获取
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: // 根据触摸位置更新控制点,并提示重绘 control.x = event.getX(); control.y = event.getY(); invalidate(); //重绘view break; default: break;<span style="white-space:pre"></span>return true }画贝塞尔曲线,
mPath.reset(); mPath.moveTo(a1.x, a1.y); mPath.quadTo(half_control.x, half_control.y, b1.x, b1.y); mPath.lineTo(b2.x, b2.y); mPath.quadTo(half_control.x, half_control.y, a2.x, a2.y); mPath.close(); canvas.drawPath(mPath, mPaint);注意事项,切记不可在Draw方法里new对象,因为,当我们重绘View时,就会生成非常多浪费的对象。
最后就是动画了
动画部分也很简单,直接用ValuAnimator
/** * 自定义动画回弹 */ public void MyAnimator(int startX, int startY, final int endX, int endY, int time) { ValueAnimator xValueAnimator = ValueAnimator.ofInt(startX, endX);<span style="white-space:pre"></span>//动画开始到结束 ValueAnimator yValueAnimator = ValueAnimator.ofInt(startY, endY); xValueAnimator.setDuration(time);<span style="white-space:pre"></span>//设置动画时间 yValueAnimator.setDuration(time); xValueAnimator.setInterpolator(new OvershootInterpolator());<span style="white-space:pre"></span>//动画效果 yValueAnimator.setInterpolator(new OvershootInterpolator()); xValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { control.x = (int) animation.getAnimatedValue();<span style="white-space:pre"></span>//获取动画变动的过程中的值 if (control.x == endX) { flog = true; } invalidate(); } }); yValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { control.y = (int) animation.getAnimatedValue(); invalidate(); } }); xValueAnimator.start(); yValueAnimator.start(); }动画:
AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始的时候向后然后向前甩
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束的时候弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 在动画开始的地方快然后慢
LinearInterpolator 以常量速率改变
OvershootInterpolator 向前甩一定值后再回到原来位置
如果android定义的interpolators不符合你的效果也可以自定义interpolators
源码下载:http://download.csdn.net/detail/qq970259858/9549605
0 0
- Android自定义Layout实现QQ消息拖动效果
- Android 自定义View 实现QQ红点拖动删除效果
- Android 自定义实现类似QQ消息贝塞尔拖拽效果BezierView
- 自定义QQ消息红点拖动的效果
- 模仿QQ消息红点拖动效果
- android实现拖动效果
- android 实现拖动效果
- Android实现拖动效果
- Android拖动效果实现
- 自定义LayoutManager实现android-pile-layout滑动卡片堆叠效果
- QQ聊天气泡拖动效果实现
- Android学习笔记-ScollView实现QQ消息界面滑动效果
- android实现QQ未读消息拖拽动态效果
- 仿手机QQ消息数拖动删除效果
- Android开发实现拖动效果
- Android实现控件拖动效果
- Android自定义View仿QQ消息拖拽气泡实现
- Android自定义ListView实现QQ空间顶部效果
- linux中的wget命令
- 暴雪hash
- 浅拷贝和深拷贝
- 王爽《汇编语言》检测点1.1详解
- 属性系统
- Android自定义Layout实现QQ消息拖动效果
- 如何用MathType插入公式编号
- 程序,控制期和view的生命周期
- mac_ScreenSaver_第1个屏幕保护程序
- OSG+Duilib显示osg
- 文章标题
- 二叉树的后序非递归遍历
- java通过Solr的Suggest实现提示词
- C++ 之IO类库