11.粘性控件
粘性控件 (對(duì)View的自定義)
*?應(yīng)用場(chǎng)景: 未讀提醒的清除
*?功能實(shí)現(xiàn):
> 1. 畫(huà)靜態(tài)圖 OK
> 2. 把靜態(tài)的數(shù)值變成變量(計(jì)算得到真實(shí)的變量) OK?
> 3. 不斷地修改變量, 重繪界面, 動(dòng)起來(lái)了.
> 4. 功能分析:
????a. 拖拽超出范圍,斷開(kāi), 松手, 消失
????b. 拖拽超出范圍,斷開(kāi),放回去了,恢復(fù)
????c. 拖拽沒(méi)超出范圍, 松手,彈回去
沒(méi)有布局:MainActivity
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(new GooView(MainActivity.this)); } } Utils
public 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(); } /** * dip 轉(zhuǎn)換成 px * @param dip * @param context * @return */ public static float dip2Dimension(float dip, Context context) { DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); } /** * @param dip * @param context * @param complexUnit {@link TypedValue#COMPLEX_UNIT_DIP} {@link TypedValue#COMPLEX_UNIT_SP}} * @return */ public static float toDimension(float dip, Context context, int complexUnit) { DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); return TypedValue.applyDimension(complexUnit, dip, displayMetrics); } /** 獲取狀態(tài)欄高度 * @param v * @return */ public static int getStatusBarHeight(View v) { if (v == null) { return 0; } Rect frame = new Rect(); v.getWindowVisibleDisplayFrame(frame); return frame.top; } public static String getActionName(MotionEvent event) { String action = "unknow"; switch (MotionEventCompat.getActionMasked(event)) { case MotionEvent.ACTION_DOWN: action = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: action = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: action = "ACTION_UP"; break; case MotionEvent.ACTION_CANCEL: action = "ACTION_CANCEL"; break; case MotionEvent.ACTION_SCROLL: action = "ACTION_SCROLL"; break; case MotionEvent.ACTION_OUTSIDE: action = "ACTION_SCROLL"; break; default: break; } return action; } } GooView
/** * 粘性控件 * @author poplar * */ public class GooView extends View { private static final String TAG = "TAG"; private Paint mPaint; public GooView(Context context) { this(context, null); } public GooView(Context context, AttributeSet attrs) { this(context, attrs , 0); } public GooView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // 做初始化操作 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.RED); } PointF[] mStickPoints = new PointF[]{ new PointF(250f, 250f), new PointF(250f, 350f) }; PointF[] mDragPoints = new PointF[]{ new PointF(50f, 250f), new PointF(50f, 350f) }; PointF mControlPoint = new PointF(150f, 300f); PointF mDragCenter = new PointF(80f, 80f); float mDragRadius = 14f; PointF mStickCenter = new PointF(150f, 150f); float mStickRadius = 12f; private int statusBarHeight; float farestDistance = 80f; private boolean isOutofRange; private boolean isDisappear; @Override protected void onDraw(Canvas canvas) { // 計(jì)算連接點(diǎn)值, 控制點(diǎn), 固定圓半徑 // 1. 獲取固定圓半徑(根據(jù)兩圓圓心距離) float tempStickRadius = getTempStickRadius(); // 2. 獲取直線與圓的交點(diǎn) float yOffset = mStickCenter.y - mDragCenter.y; float xOffset = mStickCenter.x - mDragCenter.x; Double lineK = null; if(xOffset != 0){ lineK = (double) (yOffset / xOffset); } // 通過(guò)幾何圖形工具獲取交點(diǎn)坐標(biāo) mDragPoints = GeometryUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK); mStickPoints = GeometryUtil.getIntersectionPoints(mStickCenter, tempStickRadius, lineK); // 3. 獲取控制點(diǎn)坐標(biāo) mControlPoint = GeometryUtil.getMiddlePoint(mDragCenter, mStickCenter); // 保存畫(huà)布狀態(tài) canvas.save(); canvas.translate(0, -statusBarHeight); // 畫(huà)出最大范圍(參考用) mPaint.setStyle(Style.STROKE); canvas.drawCircle(mStickCenter.x, mStickCenter.y, farestDistance, mPaint); mPaint.setStyle(Style.FILL); if(!isDisappear){ if(!isOutofRange){ // 3. 畫(huà)連接部分 Path path = new Path(); // 跳到點(diǎn)1 path.moveTo(mStickPoints[0].x, mStickPoints[0].y); // 畫(huà)曲線1 -> 2 path.quadTo(mControlPoint.x, mControlPoint.y, mDragPoints[0].x, mDragPoints[0].y); // 畫(huà)直線2 -> 3 path.lineTo(mDragPoints[1].x, mDragPoints[1].y); // 畫(huà)曲線3 -> 4 path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints[1].x, mStickPoints[1].y); path.close(); canvas.drawPath(path, mPaint); // 畫(huà)附著點(diǎn)(參考用) mPaint.setColor(Color.BLUE); canvas.drawCircle(mDragPoints[0].x, mDragPoints[0].y, 3f, mPaint); canvas.drawCircle(mDragPoints[1].x, mDragPoints[1].y, 3f, mPaint); canvas.drawCircle(mStickPoints[0].x, mStickPoints[0].y, 3f, mPaint); canvas.drawCircle(mStickPoints[1].x, mStickPoints[1].y, 3f, mPaint); mPaint.setColor(Color.RED); // 2. 畫(huà)固定圓 canvas.drawCircle(mStickCenter.x, mStickCenter.y, tempStickRadius, mPaint); } // 1. 畫(huà)拖拽圓 canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, mPaint); } // 恢復(fù)上次的保存狀態(tài) canvas.restore(); } // 獲取固定圓半徑(根據(jù)兩圓圓心距離) private float getTempStickRadius() { float distance = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter); // if(distance> farestDistance){ // distance = farestDistance; // } distance = Math.min(distance, farestDistance); // 0.0f -> 1.0f float percent = distance / farestDistance; Log.d(TAG, "percent: " + percent); // percent , 100% -> 20% return evaluate(percent, mStickRadius, mStickRadius * 0.2f); } public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); } @Override public boolean onTouchEvent(MotionEvent event) { float x; float y; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isOutofRange = false; isDisappear = false; x = event.getRawX(); y = event.getRawY(); updateDragCenter(x, y); break; case MotionEvent.ACTION_MOVE: x = event.getRawX(); y = event.getRawY(); updateDragCenter(x, y); // 處理斷開(kāi)事件 float distance = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter); if(distance > farestDistance){ isOutofRange = true; invalidate(); } break; case MotionEvent.ACTION_UP: if(isOutofRange){ float d = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter); if(d > farestDistance){ // a. 拖拽超出范圍,斷開(kāi), 松手, 消失 isDisappear = true; invalidate(); }else { //b. 拖拽超出范圍,斷開(kāi),放回去了,恢復(fù) updateDragCenter(mStickCenter.x, mStickCenter.y); } }else { // c. 拖拽沒(méi)超出范圍, 松手,彈回去 final PointF tempDragCenter = new PointF(mDragCenter.x, mDragCenter.y); ValueAnimator mAnim = ValueAnimator.ofFloat(1.0f); mAnim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator mAnim) { // 0.0 -> 1.0f float percent = mAnim.getAnimatedFraction(); PointF p = GeometryUtil.getPointByPercent(tempDragCenter, mStickCenter, percent); updateDragCenter(p.x, p.y); } }); mAnim.setInterpolator(new OvershootInterpolator(4)); mAnim.setDuration(500); mAnim.start(); } break; default: break; } return true; } /** * 更新拖拽圓圓心坐標(biāo),并重繪界面 * @param x * @param y */ private void updateDragCenter(float x, float y) { mDragCenter.set(x, y); invalidate(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); statusBarHeight = Utils.getStatusBarHeight(this); } } GeometryUtil
/** * 幾何圖形工具 */ public class GeometryUtil { /** * As meaning of method name. * 獲得兩點(diǎn)之間的距離 * @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. * 獲得兩點(diǎn)連線的中點(diǎn) * @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. * 根據(jù)百分比獲取兩點(diǎn)之間的某個(gè)點(diǎn)坐標(biāo) * @param p1 * @param p2 * @param percent * @return */ public static PointF getPointByPercent(PointF p1, PointF p2, float percent) { return new PointF(evaluateValue(percent, p1.x , p2.x), evaluateValue(percent, p1.y , p2.y)); } /** * 根據(jù)分度值,計(jì)算從start到end中,fraction位置的值。fraction范圍為0 -> 1 * @param fraction * @param start * @param end * @return */ public static float evaluateValue(float fraction, Number start, Number end){ return start.floatValue() + (end.floatValue() - start.floatValue()) * fraction; } /** * Get the point of intersection between circle and line. * 獲取 通過(guò)指定圓心,斜率為lineK的直線與圓的交點(diǎn)。 * * @param pMiddle The circle center point. * @param radius The circle radius. * @param lineK The slope of line which cross the pMiddle. * @return */ public static PointF[] getIntersectionPoints(PointF pMiddle, float radius, Double lineK) { PointF[] points = new PointF[2]; float radian, xOffset = 0, yOffset = 0; if(lineK != null){ radian= (float) Math.atan(lineK); xOffset = (float) (Math.sin(radian) * radius); yOffset = (float) (Math.cos(radian) * radius); }else { xOffset = radius; yOffset = 0; } points[0] = new PointF(pMiddle.x + xOffset, pMiddle.y - yOffset); points[1] = new PointF(pMiddle.x - xOffset, pMiddle.y + yOffset); return points; } }
來(lái)自為知筆記(Wiz)
*?應(yīng)用場(chǎng)景: 未讀提醒的清除
*?功能實(shí)現(xiàn):
> 1. 畫(huà)靜態(tài)圖 OK
> 2. 把靜態(tài)的數(shù)值變成變量(計(jì)算得到真實(shí)的變量) OK?
> 3. 不斷地修改變量, 重繪界面, 動(dòng)起來(lái)了.
> 4. 功能分析:
????a. 拖拽超出范圍,斷開(kāi), 松手, 消失
????b. 拖拽超出范圍,斷開(kāi),放回去了,恢復(fù)
????c. 拖拽沒(méi)超出范圍, 松手,彈回去
沒(méi)有布局:MainActivity
來(lái)自為知筆記(Wiz)
附件列表
?
轉(zhuǎn)載于:https://www.cnblogs.com/sixrain/p/5041965.html
總結(jié)
- 上一篇: 菜鸟学运筹学----引
- 下一篇: 俄罗斯方块c语言教程codeblocks