11.粘性控件

粘性控件 (对View的自定义)
应用场景: 未读提醒的清除
功能实现:
> 1. 画静态图 OK
> 2. 把静态的数值变成变量(计算得到真实的变量) OK 
> 3. 不断地修改变量, 重绘界面, 动起来了.
> 4. 功能分析:
    a. 拖拽超出范围,断开, 松手, 消失
    b. 拖拽超出范围,断开,放回去了,恢复
    c. 拖拽没超出范围, 松手,弹回去
240px-Bezier_2_big.gif240px-Bezier_3_big.gif

没有布局:
MainActivity
  1. public class MainActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. requestWindowFeature(Window.FEATURE_NO_TITLE);
  6. setContentView(new GooView(MainActivity.this));
  7. }
  8. }
Utils
  1. public class Utils {
  2. public static Toast mToast;
  3. public static void showToast(Context mContext, String msg) {
  4. if (mToast == null) {
  5. mToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT);
  6. }
  7. mToast.setText(msg);
  8. mToast.show();
  9. }
  10. /**
  11. * dip 转换成 px
  12. * @param dip
  13. * @param context
  14. * @return
  15. */
  16. public static float dip2Dimension(float dip, Context context) {
  17. DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
  18. return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
  19. }
  20. /**
  21. * @param dip
  22. * @param context
  23. * @param complexUnit {@link TypedValue#COMPLEX_UNIT_DIP} {@link TypedValue#COMPLEX_UNIT_SP}}
  24. * @return
  25. */
  26. public static float toDimension(float dip, Context context, int complexUnit) {
  27. DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
  28. return TypedValue.applyDimension(complexUnit, dip, displayMetrics);
  29. }
  30. /** 获取状态栏高度
  31. * @param v
  32. * @return
  33. */
  34. public static int getStatusBarHeight(View v) {
  35. if (v == null) {
  36. return 0;
  37. }
  38. Rect frame = new Rect();
  39. v.getWindowVisibleDisplayFrame(frame);
  40. return frame.top;
  41. }
  42. public static String getActionName(MotionEvent event) {
  43. String action = "unknow";
  44. switch (MotionEventCompat.getActionMasked(event)) {
  45. case MotionEvent.ACTION_DOWN:
  46. action = "ACTION_DOWN";
  47. break;
  48. case MotionEvent.ACTION_MOVE:
  49. action = "ACTION_MOVE";
  50. break;
  51. case MotionEvent.ACTION_UP:
  52. action = "ACTION_UP";
  53. break;
  54. case MotionEvent.ACTION_CANCEL:
  55. action = "ACTION_CANCEL";
  56. break;
  57. case MotionEvent.ACTION_SCROLL:
  58. action = "ACTION_SCROLL";
  59. break;
  60. case MotionEvent.ACTION_OUTSIDE:
  61. action = "ACTION_SCROLL";
  62. break;
  63. default:
  64. break;
  65. }
  66. return action;
  67. }
  68. }
GooView
  1. /**
  2. * 粘性控件
  3. * @author poplar
  4. *
  5. */
  6. public class GooView extends View {
  7. private static final String TAG = "TAG";
  8. private Paint mPaint;
  9. public GooView(Context context) {
  10. this(context, null);
  11. }
  12. public GooView(Context context, AttributeSet attrs) {
  13. this(context, attrs , 0);
  14. }
  15. public GooView(Context context, AttributeSet attrs, int defStyle) {
  16. super(context, attrs, defStyle);
  17. // 做初始化操作
  18. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  19. mPaint.setColor(Color.RED);
  20. }
  21. PointF[] mStickPoints = new PointF[]{
  22. new PointF(250f, 250f),
  23. new PointF(250f, 350f)
  24. };
  25. PointF[] mDragPoints = new PointF[]{
  26. new PointF(50f, 250f),
  27. new PointF(50f, 350f)
  28. };
  29. PointF mControlPoint = new PointF(150f, 300f);
  30. PointF mDragCenter = new PointF(80f, 80f);
  31. float mDragRadius = 14f;
  32. PointF mStickCenter = new PointF(150f, 150f);
  33. float mStickRadius = 12f;
  34. private int statusBarHeight;
  35. float farestDistance = 80f;
  36. private boolean isOutofRange;
  37. private boolean isDisappear;
  38. @Override
  39. protected void onDraw(Canvas canvas) {
  40. // 计算连接点值, 控制点, 固定圆半径
  41. // 1. 获取固定圆半径(根据两圆圆心距离)
  42. float tempStickRadius = getTempStickRadius();
  43. // 2. 获取直线与圆的交点
  44. float yOffset = mStickCenter.y - mDragCenter.y;
  45. float xOffset = mStickCenter.x - mDragCenter.x;
  46. Double lineK = null;
  47. if(xOffset != 0){
  48. lineK = (double) (yOffset / xOffset);
  49. }
  50. // 通过几何图形工具获取交点坐标
  51. mDragPoints = GeometryUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK);
  52. mStickPoints = GeometryUtil.getIntersectionPoints(mStickCenter, tempStickRadius, lineK);
  53. // 3. 获取控制点坐标
  54. mControlPoint = GeometryUtil.getMiddlePoint(mDragCenter, mStickCenter);
  55. // 保存画布状态
  56. canvas.save();
  57. canvas.translate(0, -statusBarHeight);
  58. // 画出最大范围(参考用)
  59. mPaint.setStyle(Style.STROKE);
  60. canvas.drawCircle(mStickCenter.x, mStickCenter.y, farestDistance, mPaint);
  61. mPaint.setStyle(Style.FILL);
  62. if(!isDisappear){
  63. if(!isOutofRange){
  64. // 3. 画连接部分
  65. Path path = new Path();
  66. // 跳到点1
  67. path.moveTo(mStickPoints[0].x, mStickPoints[0].y);
  68. // 画曲线1 -> 2
  69. path.quadTo(mControlPoint.x, mControlPoint.y, mDragPoints[0].x, mDragPoints[0].y);
  70. // 画直线2 -> 3
  71. path.lineTo(mDragPoints[1].x, mDragPoints[1].y);
  72. // 画曲线3 -> 4
  73. path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints[1].x, mStickPoints[1].y);
  74. path.close();
  75. canvas.drawPath(path, mPaint);
  76. // 画附着点(参考用)
  77. mPaint.setColor(Color.BLUE);
  78. canvas.drawCircle(mDragPoints[0].x, mDragPoints[0].y, 3f, mPaint);
  79. canvas.drawCircle(mDragPoints[1].x, mDragPoints[1].y, 3f, mPaint);
  80. canvas.drawCircle(mStickPoints[0].x, mStickPoints[0].y, 3f, mPaint);
  81. canvas.drawCircle(mStickPoints[1].x, mStickPoints[1].y, 3f, mPaint);
  82. mPaint.setColor(Color.RED);
  83. // 2. 画固定圆
  84. canvas.drawCircle(mStickCenter.x, mStickCenter.y, tempStickRadius, mPaint);
  85. }
  86. // 1. 画拖拽圆
  87. canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, mPaint);
  88. }
  89. // 恢复上次的保存状态
  90. canvas.restore();
  91. }
  92. // 获取固定圆半径(根据两圆圆心距离)
  93. private float getTempStickRadius() {
  94. float distance = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
  95. // if(distance> farestDistance){
  96. // distance = farestDistance;
  97. // }
  98. distance = Math.min(distance, farestDistance);
  99. // 0.0f -> 1.0f
  100. float percent = distance / farestDistance;
  101. Log.d(TAG, "percent: " + percent);
  102. // percent , 100% -> 20%
  103. return evaluate(percent, mStickRadius, mStickRadius * 0.2f);
  104. }
  105. public Float evaluate(float fraction, Number startValue, Number endValue) {
  106. float startFloat = startValue.floatValue();
  107. return startFloat + fraction * (endValue.floatValue() - startFloat);
  108. }
  109. @Override
  110. public boolean onTouchEvent(MotionEvent event) {
  111. float x;
  112. float y;
  113. switch (event.getAction()) {
  114. case MotionEvent.ACTION_DOWN:
  115. isOutofRange = false;
  116. isDisappear = false;
  117. x = event.getRawX();
  118. y = event.getRawY();
  119. updateDragCenter(x, y);
  120. break;
  121. case MotionEvent.ACTION_MOVE:
  122. x = event.getRawX();
  123. y = event.getRawY();
  124. updateDragCenter(x, y);
  125. // 处理断开事件
  126. float distance = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
  127. if(distance > farestDistance){
  128. isOutofRange = true;
  129. invalidate();
  130. }
  131. break;
  132. case MotionEvent.ACTION_UP:
  133. if(isOutofRange){
  134. float d = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
  135. if(d > farestDistance){
  136. // a. 拖拽超出范围,断开, 松手, 消失
  137. isDisappear = true;
  138. invalidate();
  139. }else {
  140. //b. 拖拽超出范围,断开,放回去了,恢复
  141. updateDragCenter(mStickCenter.x, mStickCenter.y);
  142. }
  143. }else {
  144. // c. 拖拽没超出范围, 松手,弹回去
  145. final PointF tempDragCenter = new PointF(mDragCenter.x, mDragCenter.y);
  146. ValueAnimator mAnim = ValueAnimator.ofFloat(1.0f);
  147. mAnim.addUpdateListener(new AnimatorUpdateListener() {
  148. @Override
  149. public void onAnimationUpdate(ValueAnimator mAnim) {
  150. // 0.0 -> 1.0f
  151. float percent = mAnim.getAnimatedFraction();
  152. PointF p = GeometryUtil.getPointByPercent(tempDragCenter, mStickCenter, percent);
  153. updateDragCenter(p.x, p.y);
  154. }
  155. });
  156. mAnim.setInterpolator(new OvershootInterpolator(4));
  157. mAnim.setDuration(500);
  158. mAnim.start();
  159. }
  160. break;
  161. default:
  162. break;
  163. }
  164. return true;
  165. }
  166. /**
  167. * 更新拖拽圆圆心坐标,并重绘界面
  168. * @param x
  169. * @param y
  170. */
  171. private void updateDragCenter(float x, float y) {
  172. mDragCenter.set(x, y);
  173. invalidate();
  174. }
  175. @Override
  176. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  177. super.onSizeChanged(w, h, oldw, oldh);
  178. statusBarHeight = Utils.getStatusBarHeight(this);
  179. }
  180. }
GeometryUtil
  1. /**
  2. * 几何图形工具
  3. */
  4. public class GeometryUtil {
  5. /**
  6. * As meaning of method name.
  7. * 获得两点之间的距离
  8. * @param p0
  9. * @param p1
  10. * @return
  11. */
  12. public static float getDistanceBetween2Points(PointF p0, PointF p1) {
  13. float distance = (float) Math.sqrt(Math.pow(p0.y - p1.y, 2) + Math.pow(p0.x - p1.x, 2));
  14. return distance;
  15. }
  16. /**
  17. * Get middle point between p1 and p2.
  18. * 获得两点连线的中点
  19. * @param p1
  20. * @param p2
  21. * @return
  22. */
  23. public static PointF getMiddlePoint(PointF p1, PointF p2) {
  24. return new PointF((p1.x + p2.x) / 2.0f, (p1.y + p2.y) / 2.0f);
  25. }
  26. /**
  27. * Get point between p1 and p2 by percent.
  28. * 根据百分比获取两点之间的某个点坐标
  29. * @param p1
  30. * @param p2
  31. * @param percent
  32. * @return
  33. */
  34. public static PointF getPointByPercent(PointF p1, PointF p2, float percent) {
  35. return new PointF(evaluateValue(percent, p1.x , p2.x), evaluateValue(percent, p1.y , p2.y));
  36. }
  37. /**
  38. * 根据分度值,计算从start到end中,fraction位置的值。fraction范围为0 -> 1
  39. * @param fraction
  40. * @param start
  41. * @param end
  42. * @return
  43. */
  44. public static float evaluateValue(float fraction, Number start, Number end){
  45. return start.floatValue() + (end.floatValue() - start.floatValue()) * fraction;
  46. }
  47. /**
  48. * Get the point of intersection between circle and line.
  49. * 获取 通过指定圆心,斜率为lineK的直线与圆的交点。
  50. *
  51. * @param pMiddle The circle center point.
  52. * @param radius The circle radius.
  53. * @param lineK The slope of line which cross the pMiddle.
  54. * @return
  55. */
  56. public static PointF[] getIntersectionPoints(PointF pMiddle, float radius, Double lineK) {
  57. PointF[] points = new PointF[2];
  58. float radian, xOffset = 0, yOffset = 0;
  59. if(lineK != null){
  60. radian= (float) Math.atan(lineK);
  61. xOffset = (float) (Math.sin(radian) * radius);
  62. yOffset = (float) (Math.cos(radian) * radius);
  63. }else {
  64. xOffset = radius;
  65. yOffset = 0;
  66. }
  67. points[0] = new PointF(pMiddle.x + xOffset, pMiddle.y - yOffset);
  68. points[1] = new PointF(pMiddle.x - xOffset, pMiddle.y + yOffset);
  69. return points;
  70. }
  71. }





附件列表

     

    posted @ 2015-12-12 22:19  梦和远方  阅读(248)  评论(0编辑  收藏  举报