自定义控件:粘性控件_第1页
自定义控件:粘性控件_第2页
自定义控件:粘性控件_第3页
自定义控件:粘性控件_第4页
自定义控件:粘性控件_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、自定义控件:粘性控件GooView粘性控件了解几何图形工具的用法掌握画不规则图形的方法应用场景:未读提醒,效果图:绘制一帧的效果画一帧粘性控件的步骤分析 1画一个固定圆 2画一个拖拽圆 3画中间连接部分 将中间连接部分水平放置,四个角的坐标定为固定值,分别标记上点的编号,矩形中心的点为控件点,画曲线时用自定义一个GooView 继承Viewpublic class GooView extends View private Paint paint; public GooView(Context context) this(context,null); public GooView(Context

2、 context, AttributeSet attrs) this(context, attrs,0); public GooView(Context context, AttributeSet attrs, int defStyle) super(context, attrs, defStyle); /初始化画笔 paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.RED); Override protected void onDraw(Canvas canvas) super.onDraw(canvas)

3、; /画中间连接部分 Path path = new Path(); /跳到点1,默认为(0f,0f) path.moveTo(250f, 250f); /从点1->点2 画曲线 path.quadTo(150f, 300f, 50f, 250f); /从点2->点3 画直线 path.lineTo(50f, 350f); /从点3->点4 画曲线 path.quadTo(150f, 300f, 250f, 350f); canvas.drawPath(path, paint); /画拖拽圆 canvas.drawCircle(90f, 90f, 16f, paint); /

4、画固定圆 canvas.drawCircle(150f, 150f, 12f, paint); 第20-30 行用Path 画中间曲线部分 第25 行quadTo(x1,y1,x2,y2)方法可以画当前所在点到x2,y2 间的一条曲线,x1,y1 是当前点与x2,y2 间的一个控件点,它的位置决定曲线弯曲的方向和弧度,将GooView 显示到MainActivity 中public class MainActivity extends Activity Override protected void onCreate(Bundle savedInstanceState) requestWind

5、owFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(new GooView(this); 贝塞尔曲线二阶贝塞尔曲线,三阶贝塞尔曲线分别给拖拽圆,固定圆的圆心,半径,两个附着点命名,修改GooView 的onDraw()方法protected void onDraw(Canvas canvas) super.onDraw(canvas); /固定圆的两个附着点 PointF mStickPoints = new PointF new PointF(250f, 250f),ne

6、w PointF(250f, 350f) ; /固定圆的两个附着点 PointF mDragPoints = new PointF new PointF(50f, 250f),new PointF(50f, 350f) ; /控制点 PointF mControlPoint = new PointF(150f, 300f); /画中间连接部分 Path path = new Path(); /跳到点1,默认为(0f,0f) path.moveTo(mStickPoints0.x, mStickPoints0.y); /从点1->点2 画曲线 path.quadTo(mControlPoi

7、nt.x, mControlPoint.y, mDragPoints0.x, mDragPoints0.y); /从点2->点3 画直线 path.lineTo( mDragPoints1.x, mDragPoints1.y); /从点3->点4 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints1.x, mStickPoints1.y); canvas.drawPath(path, paint); /画拖拽圆 /拖拽圆圆心 PointF mDragCenter = new PointF(90f, 90f);

8、 /拖拽圆半径 float mDragRadius = 16f; canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, paint); /画固定圆 /固定圆圆心 PointF mStickCenter = new PointF(150f, 150f); /固定圆半径 float mStickRadius = 12f; canvas.drawCircle(mStickCenter.x, mStickCenter.y, mStickRadius, paint);第3-14 行替换附着点及控制点 第30-40 行替换拖拽圆及固定圆的

9、圆心及半径 将替换后的变量转换成GooView 的成员变量/ 固定圆圆心PointF mStickCenter = new PointF(150f, 150f);/ 固定圆半径float mStickRadius = 12f;/ 拖拽圆圆心PointF mDragCenter = new PointF(90f, 90f);/ 拖拽圆半径float mDragRadius = 16f;/ 固定圆的两个附着点PointF mStickPoints = new PointF new PointF(250f, 250f), new PointF(250f, 350f) ;/ 固定圆的两个附着点Poin

10、tF mDragPoints = new PointF new PointF(50f, 250f), new PointF(50f, 350f) ;/ 控制点PointF mControlPoint = new PointF(150f, 300f);Overrideprotected void onDraw(Canvas canvas) super.onDraw(canvas); / 画中间连接部分 Path path = new Path(); / 跳到点1,默认为(0f,0f) path.moveTo(mStickPoints0.x, mStickPoints0.y); / 从点1->

11、;点2 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mDragPoints0.x, mDragPoints0.y); / 从点2->点3 画直线 path.lineTo(mDragPoints1.x, mDragPoints1.y); / 从点3->点4 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints1.x, mStickPoints1.y); canvas.drawPath(path, paint); / 画拖拽圆 canvas.drawCircl

12、e(mDragCenter.x, mDragCenter.y, mDragRadius, paint); / 画固定圆 canvas.drawCircle(mStickCenter.x, mStickCenter.y, mStickRadius, paint);拖拽圆和固定圆的圆心和半径已知,角3 的正弦值为两圆心纵坐标之差比上横坐标之差,则角3 的角度可知,则角1 可知,AB,AC 的长度即可计算出来,mDragPoints0的坐标可以计算出来,同理其它三个附着点坐标也可知。mControlPoint 为两圆心连线的中点几何图形工具/* * 几何图形工具 */public class Geo

13、metryUtil /* * 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; /* * Get middle point between p1 and p2

14、. * 获得两点连线的中点 * param p1 * param p2 * return */ public static PointF getMiddlePoint(PointF p1, PointF p2) return new PointF(p1.x + p2.x) / 2.0f, (p1.y + p2.y) / 2.0f); /* * Get point between p1 and p2 by percent. * 根据百分比获取两点之间的某个点坐标 * param p1 * param p2 * param percent * return */ public static Poi

15、ntF getPointByPercent(PointF p1, PointF p2, float percent) return new PointF(evaluateValue(percent, p1.x , p2.x), evaluateValue(percent, p1.y , p2.y); /* * 根据分度值,计算从start 到end 中,fraction 位置的值。fraction 范围为0 -> 1 * param fraction * param start * param end * return */ public static float evaluateVal

16、ue(float fraction, Number start, Number end) return start.floatValue() + (end.floatValue() - start.floatValue() * fraction; /* * Get the point of intersection between circle and line. * 获取通过指定圆心,斜率为lineK 的直线与圆的交点。 * * param pMiddle The circle center point. * param radius The circle radius. * param l

17、ineK The slope of line which cross the pMiddle. * return */ public static PointF getIntersectionPoints(PointF pMiddle, float radius, DoublelineK) PointF points = new PointF2; float radian, xOffset = 0, yOffset = 0; if(lineK != null) radian= (float) Math.atan(lineK); xOffset = (float) (Math.sin(radia

18、n) * radius); yOffset = (float) (Math.cos(radian) * radius); else xOffset = radius; yOffset = 0; points0 = new PointF(pMiddle.x + xOffset, pMiddle.y - yOffset); points1 = new PointF(pMiddle.x - xOffset, pMiddle.y + yOffset); return points; 利用几何图形工具类计算四个附着点坐标及控件点坐标protected void onDraw(Canvas canvas)

19、 super.onDraw(canvas); float yOffset = mStickCenter.y - mDragCenter.y; float xOffset = mStickCenter.x - mDragCenter.x; Double lineK = null; if(xOffset != 0) /xOffset 分母不能为0 lineK = (double) (yOffset/xOffset); /计算四个附着点 mDragPoints = GeometryUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK);

20、 mStickPoints = GeometryUtil.getIntersectionPoints(mStickCenter, mStickRadius, lineK); /一个控制点 mControlPoint = GeometryUtil.getMiddlePoint(mDragCenter, mStickCenter); / 画中间连接部分 Path path = new Path(); / 跳到点1,默认为(0f,0f) path.moveTo(mStickPoints0.x, mStickPoints0.y); / 从点1->点2 画曲线 path.quadTo(mContr

21、olPoint.x, mControlPoint.y, mDragPoints0.x, mDragPoints0.y); / 从点2->点3 画直线 path.lineTo(mDragPoints1.x, mDragPoints1.y); / 从点3->点4 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints1.x, mStickPoints1.y); canvas.drawPath(path, paint); / 画拖拽圆 canvas.drawCircle(mDragCenter.x, mDragCent

22、er.y, mDragRadius, paint); / 画固定圆 canvas.drawCircle(mStickCenter.x, mStickCenter.y, mStickRadius, paint);第3-17 行计算四个附着点及控制点坐标1.4 计算固定圆半径GooView 重写onSizeChanged()方法,计算状态栏高度Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) super.onSizeChanged(w, h, oldw, oldh); /获取状态栏的高度,传入一个显示在屏幕

23、上的view 即可 statusBarHeight = Utils.getStatusBarHeight(this);Utils.Javapublic class Utils public static Toast mToast; public static void showToast(Context mContext, String msg) if (mToast = null) mToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT); mToast.setText(msg); mToast.show(); /

24、* * 获取状态栏高度 * * param v * return */ public static int getStatusBarHeight(View v) if (v = null) return 0; Rect frame = new Rect(); v.getWindowVisibleDisplayFrame(frame); return frame.top; 修改onDraw()方法protected void onDraw(Canvas canvas) super.onDraw(canvas); float yOffset = mStickCenter.y - mDragCent

25、er.y; float xOffset = mStickCenter.x - mDragCenter.x; Double lineK = null; if(xOffset != 0) /xOffset 分母不能为0 lineK = (double) (yOffset/xOffset); /计算四个附着点 mDragPoints = GeometryUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK); mStickPoints = GeometryUtil.getIntersectionPoints(mStickCenter,

26、mStickRadius, lineK); /一个控制点 mControlPoint = GeometryUtil.getMiddlePoint(mDragCenter, mStickCenter); /移动画布 canvas.save(); canvas.translate(0, -statusBarHeight); / 画中间连接部分 Path path = new Path(); / 跳到点1,默认为(0f,0f) path.moveTo(mStickPoints0.x, mStickPoints0.y); / 从点1->点2 画曲线 path.quadTo(mControlPoi

27、nt.x, mControlPoint.y, mDragPoints0.x, mDragPoints0.y); / 从点2->点3 画直线 path.lineTo(mDragPoints1.x, mDragPoints1.y); / 从点3->点4 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints1.x, mStickPoints1.y); canvas.drawPath(path, paint); / 画拖拽圆 canvas.drawCircle(mDragCenter.x, mDragCenter.y,

28、 mDragRadius, paint); / 画固定圆 canvas.drawCircle(mStickCenter.x, mStickCenter.y, mStickRadius, paint); canvas.restore();第18-20 行把画布向上移动状态栏的高度,移动前需要保存一下当前状态,做完操作后需要恢复一下状态,由于在onTouchEvent()中用的是getRawX(),getRawY()获取的是相对屏幕的坐标,所以GooView画图操作时需要向上移到一个状态栏的高度才能刚好和手指重合拖拽圆跟随手指移动时,随着拖拽与固定圆的距离的变大,固定圆的半径越来越小/允许的最大距

29、离float farestDistance = 80f;/* * 通过两圆圆心的距离,计算固定圆的半径 * return */private float computeStickRadius() /通过几何图形工具类可以计算出两圆圆心的距离,distance 是可以大于80f; float distance = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter); /需要的是0.0f -> 1.0f 的值,所在大于80f 让distance 等于80f distance = Math.min(farestDi

30、stance, distance); float percent = distance/farestDistance; /需要固定圆心半径在12f -> 3f 间变化,可以利用类型估值器 return evaluate(percent, mStickRadius, mStickRadius*0.25f);/FloatEvaluator.java 中拷贝public Float evaluate(float fraction, Number startValue, Number endValue) float startFloat = startValue.floatValue(); re

31、turn startFloat + fraction * (endValue.floatValue() - startFloat);protected void onDraw(Canvas canvas) super.onDraw(nvas); /通过两圆圆心的距离,计算固定圆的半径 float tempStickRadius = computeStickRadius(); float yOffset = mStickCenter.y - mDragCenter.y; float xOffset = mStickCenter.x - mDragCenter.x; Double lineK =

32、null; if(xOffset != 0) lineK = (double) (yOffset/xOffset); /计算四个附着点 mDragPoints = GeometryUtil.getIntersectionPoints(mDragCenter, mDragRadius,lineK); mStickPoints = GeometryUtil.getIntersectionPoints(mStickCenter, tempStickRadius,lineK); /一个控制点 mControlPoint = GeometryUtil.getMiddlePoint(mDragCenter

33、, mStickCenter); /移动画布 canvas.save(); canvas.translate(0, -statusBarHeight); / 画中间连接部分 Path path = new Path(); / 跳到点1,默认为(0f,0f) path.moveTo(mStickPoints0.x, mStickPoints0.y); / 从点1->点2 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mDragPoints0.x, mDragPoints0.y); / 从点2->点3 画直线 path.lineTo

34、(mDragPoints1.x, mDragPoints1.y); / 从点3->点4 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints1.x, mStickPoints1.y); canvas.drawPath(path, paint); / 画拖拽圆 canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, paint); / 画固定圆 canvas.drawCircle(mStickCenter.x, mStickCenter.y, tempS

35、tickRadius, paint); canvas.restore();第2 行定义最大的拖拽距离为80f 第7-24 行拖拽圆与固定圆的距离大于80f 时,取80f,通过两圆圆心的距离与80f 相对可以求出一个0.0f 到1.0f 的值,再通过估值器可以获得固定圆的半径在mStickRadius,mStickRadius*0.25f 间的变化值 第27-28 行通过两圆圆心的距离计算固定圆的半径tempStickRadius 第39,67 行将mStickRadius 替换成计算出来的半径tempStickRadius事件处理事件处理的分析1超出最大范围:拖拽圆与固定圆断开,松手后消失 2

36、超出最大范围:又放回去,恢复 3没有超出最大范围:松手,回弹动画,恢复事件处理的实现修改onTouchEvent()方法/是否已经消失private boolean isDisappear = false;/是否超出范围private boolean isOutOfRange = false;public boolean onTouchEvent(MotionEvent event) float x; float y; switch (event.getAction() case MotionEvent.ACTION_DOWN: /重置变量 isDisappear = false; isOut

37、OfRange = false; x = event.getRawX(); y = event.getRawY(); updateDragCenter(x, y); break; case MotionEvent.ACTION_MOVE: x = event.getRawX(); y = event.getRawY(); updateDragCenter(x, y); float d = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter); / 超出范围断开 if (d > farestDistance) i

38、sOutOfRange = true; invalidate(); break; case MotionEvent.ACTION_UP: if (isOutOfRange) / 刚刚超出了范围 float dis = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter); if (dis > farestDistance) / 超出范围,松手,断开,消失 isDisappear = true; invalidate(); else / 超出范围,断开,又放回去了,恢复 updateDragCenter(mSti

39、ckCenter.x, mStickCenter.y); else / 没有超出范围,松手,回弹,恢复 final PointF startP = new PointF(mDragCenter.x, mDragCenter.y); ValueAnimator animator = ValueAnimator.ofFloat(1.0f); animator.setDuration(500); / 插值器,回弹效果 animator.setInterpolator(new OvershootInterpolator(4); animator.addUpdateListener(new Animat

40、orUpdateListener() Override public void onAnimationUpdate(ValueAnimator animation) / 生成0.0f ->1.0f 间的值 float percent = animation.getAnimatedFraction(); / 计算从开始点startP 到mStickCenter 间的所有值 PointF p = GeometryUtil.getPointByPercent(startP, mStickCenter, percent); updateDragCenter(p.x, p.y); ); anima

41、tor.start(); break; default: break; return true;第1-2 行创建两个布尔变量记录是否已经消失及是否超出范围 第11-12 行手指重新按下时,重置变量 第21-27 行拖拽过程中记录是否超出范围 第32-38 行超出范围,松手,消失,标记当前为消失状态 第39-41 行超出范围,又放回去了,需要恢复,直接更新拖拽圆圆心为固定圆心即可 第45-62 行没有超出范围,松手,需要回弹动画,恢复 修改onDraw()方法protected void onDraw(Canvas canvas) super.onDraw(canvas); / 通过两圆圆心的距

42、离,计算固定圆的半径 float tempStickRadius = computeStickRadius(); float yOffset = mStickCenter.y - mDragCenter.y; float xOffset = mStickCenter.x - mDragCenter.x; Double lineK = null; if (xOffset != 0) lineK = (double) (yOffset / xOffset); / 计算四个附着点 mDragPoints = GeometryUtil.getIntersectionPoints(mDragCenter

43、, mDragRadius, lineK); mStickPoints = GeometryUtil.getIntersectionPoints(mStickCenter, tempStickRadius, lineK); / 一个控制点 mControlPoint = GeometryUtil.getMiddlePoint(mDragCenter, mStickCenter); / 移动画布 canvas.save(); canvas.translate(0, -statusBarHeight); / 画出最大范围(参考) / 只画边线 paint.setStyle(Style.STROKE

44、); canvas.drawCircle(mStickCenter.x, mStickCenter.y, farestDistance, paint); / 填充 paint.setStyle(Style.FILL); if(!isDisappear) /没有消失时,才绘制内容 if (!isOutOfRange) /没有超出范围时,才画连接部分和固定圆 / 画中间连接部分 Path path = ew Path(); / 跳到点1,默认为(0f,0f) path.moveTo(mStickPoints0.x, mStickPoints0.y); / 从点1->点2 画曲线 path.q

45、uadTo(mControlPoint.x, mControlPoint.y, mDragPoints0.x, mDragPoints0.y); / 从点2->点3 画直线 path.lineTo(mDragPoints1.x, mDragPoints1.y); / 从点3->点4 画曲线 path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints1.x, mStickPoints1.y); canvas.drawPath(path, paint); / 画固定圆 canvas.drawCircle(mStickCenter.x, mStickCenter.y, tempStickRadius, paint); / 画拖拽圆 ca

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论