自定義控件:粘性控件_第1頁
自定義控件:粘性控件_第2頁
自定義控件:粘性控件_第3頁
自定義控件:粘性控件_第4頁
自定義控件:粘性控件_第5頁
已閱讀5頁,還剩14頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、自定義控件:粘性控件GooView粘性控件了解幾何圖形工具的用法掌握畫不規(guī)則圖形的方法應(yīng)用場景:未讀提醒,效果圖:繪制一幀的效果畫一幀粘性控件的步驟分析 1畫一個固定圓 2畫一個拖拽圓 3畫中間連接部分 將中間連接部分水平放置,四個角的坐標(biāo)定為固定值,分別標(biāo)記上點的編號,矩形中心的點為控件點,畫曲線時用自定義一個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)方法可以畫當(dāng)前所在點到x2,y2 間的一條曲線,x1,y1 是當(dāng)前點與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、圓心及半徑 將替換后的變量轉(zhuǎn)換成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 的正弦值為兩圓心縱坐標(biāo)之差比上橫坐標(biāo)之差,則角3 的角度可知,則角1 可知,AB,AC 的長度即可計算出來,mDragPoints0的坐標(biāo)可以計算出來,同理其它三個附著點坐標(biāo)也可知。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. * 根據(jù)百分比獲取兩點之間的某個點坐標(biāo) * 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); /* * 根據(jù)分度值,計算從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; 利用幾何圖形工具類計算四個附著點坐標(biāo)及控件點坐標(biāo)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 行計算四個附著點及控制點坐標(biāo)1.4 計算固定圓半徑GooView 重寫onSizeChanged()方法,計算狀態(tài)欄高度Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) super.onSizeChanged(w, h, oldw, oldh); /獲取狀態(tài)欄的高度,傳入一個顯示在屏幕

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、* * 獲取狀態(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; 修改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 行把畫布向上移動狀態(tài)欄的高度,移動前需要保存一下當(dāng)前狀態(tài),做完操作后需要恢復(fù)一下狀態(tài),由于在onTouchEvent()中用的是getRawX(),getRawY()獲取的是相對屏幕的坐標(biāo),所以GooView畫圖操作時需要向上移到一個狀態(tài)欄的高度才能剛好和手指重合拖拽圓跟隨手指移動時,隨著拖拽與固定圓的距離的變大,固定圓的半徑越來越小/允許的最大距

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、超出最大范圍:又放回去,恢復(fù) 3沒有超出最大范圍:松手,回彈動畫,恢復(fù)事件處理的實現(xiàn)修改onTouchEvent()方法/是否已經(jīng)消失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 / 超出范圍,斷開,又放回去了,恢復(fù) updateDragCenter(mSti

39、ckCenter.x, mStickCenter.y); else / 沒有超出范圍,松手,回彈,恢復(fù) 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 行創(chuàng)建兩個布爾變量記錄是否已經(jīng)消失及是否超出范圍 第11-12 行手指重新按下時,重置變量 第21-27 行拖拽過程中記錄是否超出范圍 第32-38 行超出范圍,松手,消失,標(biāo)記當(dāng)前為消失狀態(tài) 第39-41 行超出范圍,又放回去了,需要恢復(fù),直接更新拖拽圓圓心為固定圓心即可 第45-62 行沒有超出范圍,松手,需要回彈動畫,恢復(fù) 修改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) /沒有消失時,才繪制內(nèi)容 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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論