本文共 8754 字,大约阅读时间需要 29 分钟。
一、效果如图:
示例图:
应用程序图:———————————————————————
有需求者请加qq:136137465,非诚勿扰 (java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万) 01.高级架构师四十二个阶段高 02.Java高级系统培训架构课程148课时 03.Java高级互联网架构师课程 04.Java互联网架构Netty、Nio、Mina等-视频教程 05.Java高级架构设计2016整理-视频教程 06.架构师基础、高级片 07.Java架构师必修linux运维系列课程 08.Java高级系统培训架构课程116课时 (送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门) ——————————————————————–二、分析:
1、应用的地方:如未读数据的清除等
2、这个控件要实现哪些功能呢? 1)拖拽超出范围时,断开了,此时我们松手,图标消失 2)拖拽超出范围时,断开了,此时我们把图标移动回去,图标恢复原样 3)拖拽没有超出范围时,此时我们松手,图标弹回去3、如何实现:
1)我们先画个两个静态的圆圈,一个大的,一个小的 2)绘制中间连接的部分: 3)把静态的数值变成变量 4)不断地修改变量,重绘界面,就能动起来三、实现简单的
1、创建自定义view
/** * @描述 粘性控件 * @项目名称 App_imooc * @包名 com.android.imooc.goo * @类名 GooView * @author chenlin * @date 2015年6月2日 下午8:40:53 * @version 1.0 */public class GooView extends View { public GooView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GooView(Context context) { this(context, null); } public GooView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.RED); } @Override protected void onDraw(Canvas canvas) { }}
2、在onDraw里绘制个圆
private Paint mPaint;private float mCircleCenter = 150f;private float mRadius = 14f;//直径private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.RED);}@Overrideprotected void onDraw(Canvas canvas) { canvas.drawCircle(mCircleCenter, mCircleCenter, mRadius, mPaint);}
3、生成主页
/** * @描述 主页 * @项目名称 App_imooc * @包名 com.android.imooc.goo * @类名 GooActivity * @author chenlin * @date 2015年6月2日 下午8:57:16 * @version 1.0 */public class GooActivity extends Activity { private GooView mGooView; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); mGooView = new GooView(this); setContentView(mGooView); }}
4、开始画拖拽的大圆
private float mMoveCircleCenter = 70f;private float mMoveRadius = 20f;//直径canvas.drawCircle(mMoveCircleCenter, mMoveCircleCenter, mMoveRadius, mPaint);
5、画拖拽的填充部分,这是重点,绘制使用drawPath
canvas.drawPath(path, mPaint);
但如何才能实现有弯曲的填充物呢?使用了path,
只要一个控制点,如图: 有两个控制点:如图:在这里,我们先绘制一个这样的图,
p1到p2绘制一条贝塞尔曲线 p2到p3绘制直线 p3到p4绘制贝塞尔曲线 p4到p1绘制直线 如图:代码实现:
// 画连接部分Path path = new Path();// 50, 250 p2path.moveTo(250f, 250f);// 150f, 300f填充物的中间点// 50f, 250f p1path.quadTo(150f, 300f, 50f, 250f);// p3path.lineTo(50f, 350f);// p4path.quadTo(150f, 300f, 250f, 350f);//关闭后,会回到最开始的地方,形成封闭的图形path.close();canvas.drawPath(path, mPaint);
四、把静态点动态化
好了,现在我们会基本的绘制了,开始把这些点转为动态的吧!
如图:1、把上的圆的点全部转化成float类型的点
PointF staticPointF = new PointF(mStaicCircleCenter, mStaicCircleCenter);// 画小圆canvas.drawCircle(staticPointF.x, staticPointF.y, mStaicRadius, mPaint);PointF movewPointF = new PointF(mMoveCircleCenter, mMoveCircleCenter);// 画移动的大圆canvas.drawCircle(movewPointF.x, movewPointF.y, mMoveRadius, mPaint);
2、转化Path上面的点
我们分别使用两个集合存储静态的点与动态点//存储静态的两个点PointF[] staticPointFs = new PointF[] { new PointF(250f, 250f), new PointF(250f, 350f) };//存储移动的两个点PointF[] moviePointFs = new PointF[] { new PointF(50f, 250f), new PointF(50f, 350f) };//控制点PointF controlPointF = new PointF(150f, 300f);// 画连接部分Path path = new Path();// 50, 250 p2path.moveTo(staticPointFs[0].x, staticPointFs[0].y);// 150f, 300f填充物的中间点// 50f, 250f p1path.quadTo(controlPointF.x, controlPointF.y, moviePointFs[0].x, moviePointFs[0].y);// p3path.lineTo(moviePointFs[1].x, moviePointFs[1].y);// p4path.quadTo(controlPointF.x, controlPointF.y, staticPointFs[1].x, staticPointFs[1].y);// 关闭后,会回到最开始的地方,形成封闭的图形path.close();canvas.drawPath(path, mPaint);
如图:
3、接下来我们就是要把下面的曲型移动到上面两个圆圈之间,形成一个整体
在这里有,两个圆圈的中心点的坐标是已知的,半径也是知道的,现在就剩下求4个点的坐标了 第一个移动点的坐标如何求,看图 movePointfs[0].x = movePointf.x + mMoveRadius. cos(a); movePointfs[0].y = movePointf.y - mMoveRadius. sin(a);4、开始求得各个点
// 1、获得偏移量float yOffset = mStaticCenter.y - mMovewCenter.y;float xOffset = mStaticCenter.x - mMovewCenter.x;// 2、有了偏移量就可以求出两点斜率了Double lineK = 0.0;if (xOffset != 0f) { lineK = (double) (yOffset / xOffset);}// 3、通过工具求得两个点的集合mMovewPointFs = GeometryUtil.getIntersectionPoints(mMovewCenter, mMoveRadius, lineK);mStaticPointFs = GeometryUtil.getIntersectionPoints(mStaticCenter, mStaicRadius, lineK);// 4、通过公式求得控制点mControlPointF = GeometryUtil.getMiddlePoint(mStaticCenter, mMovewCenter);
5、得到如图效果:
五、开始实现拖动
1、复写onTouchEvent事件,记得在view里要返回true;
@Override public boolean onTouchEvent(MotionEvent event) { float downX = 0.0f; float downY = 0.0f; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 得到按下的坐标 downX = event.getRawX(); downY = event.getRawY(); // 更新移动的坐标 updateMoveCenter(downX, downY); break; case MotionEvent.ACTION_MOVE: // 得到按下的坐标 downX = event.getRawX(); downY = event.getRawY(); // 更新移动的坐标 updateMoveCenter(downX, downY); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return true; } /** * 更新移动的点 * * @param downX * @param downY */ public void updateMoveCenter(float downX, float downY) { mMovewCenter.set(downX, downY); invalidate(); }
2、此时看,我们的手总是离大圆圈有一点距离,这个距离就是状态栏的高度,如何才能让手指在中间呢?
方法1:此时必须先得到状态栏的高度/** * 获取状态栏高度 * * @param v * @return */ public static int getStatusBarHeight(View v) { if (v == null) { return 0; } Rect frame = new Rect(); v.getWindowVisibleDisplayFrame(frame); return frame.top; }
如果画布进行了平移或旋转动画,我们必须在平移钱,保存,在绘制完成后恢复
canvas.save();canvas.translate(0, -mBarHeight);canvas.restore();
方法2:不使用getRawX,而是使用getX,这样的话就不存在状态栏的问题了
downX = event.getX();downY = event.getY();// 更新移动的坐标updateMoveCenter(downX, downY);
3、当大圆圈移动到一定距离时,会断开,此时如果松手了,消失
怎么才能得到两个圆心的距离呢?我们以前学过勾股定理 就是:(y1-y2)的平方 + (x1-x2)的平方的和然后开根号/** * As meaning of method name. 获得两点之间的距离 * * @param p0 * @param p1 * @return */ public static float getDistanceBetween2Points(PointF p0, PointF p1) { float distance = (float) Math.sqrt(Math.pow(p0.y - p1.y, 2) + Math.pow(p0.x - p1.x, 2)); return distance; }
4、当移动时,小圆圈的半径会随着拉动的距离变化,如何实现
// 获取固定圆半径(根据两圆圆心距离) private float getTempStickRadius() { float distance = GeometryUtil.getDistanceBetween2Points(mMovewCenter, mStaticCenter); // if(distance> farestDistance){ // distance = farestDistance; // } distance = Math.min(distance, mMaxDistance); // 0.0f -> 1.0f float percent = distance / mMaxDistance; // percent , 100% -> 20% return ValueUtil.evalute(percent, mStaicRadius, mStaicRadius * 0.2f); }
然后在 onDraw里把得到的值设置到
float tempStaticRadius = getTempStickRadius();canvas.drawCircle(mStaticCenter.x, mStaticCenter.y, tempStaticRadius, mPaint);
5、现在实现到了一定距离后,断开,在onTouchevent的move事件里判断
//当超过最大值时断开float distance = GeometryUtil.getDistanceBetween2Points(mMovewCenter, mStaticCenter);if (distance > mMaxDistance) { isOutRange = true; invalidate();}
然后在onDraw里判断
if (!isOutRange) { //如果没有超出范围才绘制}
6、超出范围后,是否消失的实现在MotionEvent.ACTION_UP里实现,这里有三种可能,上面已经说了
if (isOutRange) {//1)拖拽超出范围时,断开了,此时我们松手,图标消失// 当超过最大值时断开distance = GeometryUtil.getDistanceBetween2Points(mMovewCenter, mStaticCenter);if (distance > mMaxDistance) { isDisappear = true; invalidate();}else { //2)拖拽超出范围时,断开了,此时我们把图标移动回去,图标恢复原样 //就是把移动的圆圈设置到原来的静态圆圈里 updateMoveCenter(mStaticCenter.x, mStaticCenter.y);}}else {//3)拖拽没有超出范围时,此时我们松手,图标弹回去//得到固定的点final PointF tempMovePointF = new PointF(mMovewCenter.x, mMovewCenter.y);ValueAnimator vAnim = ValueAnimator.ofFloat(1.0f);vAnim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float percent = animation.getAnimatedFraction(); //得到两点的 PointF pointF = GeometryUtil.getPointByPercent(tempMovePointF, mStaticCenter, percent); updateMoveCenter(pointF.x, pointF.y); }});vAnim.setInterpolator(new OvershootInterpolator(4));vAnim.setDuration(500);vAnim.start();}
到此,这个控件基本完成了,
六、源码下载
链接: 密码:rniy
———————————————————————
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万) 有需求者请进站查看,非诚勿扰https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084hc8wek&id=555888526201
01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时 03.Java高级互联网架构师课程 04.Java互联网架构Netty、Nio、Mina等-视频教程 05.Java高级架构设计2016整理-视频教程 06.架构师基础、高级片 07.Java架构师必修linux运维系列课程 08.Java高级系统培训架构课程116课时 (送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门) ——————————————————————–转载地址:https://lovoo.blog.csdn.net/article/details/51570143 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!