從0開始學自定義View -1
PS:好久沒有寫部落格了,之前的東西有所忘記,百度一下竟然查到了自己的寫過的部落格,訪問量還可以,一開始的寫部落格的初衷是把自己不會的記錄下來,現在沒想到也有博友會關注我,這就給了我動力,工作之餘把零零碎碎的東西總結一下,供大家參考。下面的博文是我自己的總結再加上博友的一些見解整合而成,共同進步。
初識View
在Android中所有的樣式都可以說是一個視圖,TextView,Button,ImageView…這些官方已經給出的view已經無法滿足我們的日常生活所需了,這個時候,我們就可以自定義View,隨心所欲創建屬於我們自己的View。那麼我們應該怎麼去做呢,首先要幹嘛,其次又幹嘛,最後幹嘛呢,這都是過程中的一個節點。下面呢我們就從第一步開始。
一張圖認識View

View的坐標系
getTop(); //獲取子View左上角距父View頂部的距離 getLeft(); //獲取子View左上角距父View左側的距離 getBottom(); //獲取子View右下角距父View頂部的距離 getRight(); //獲取子View右下角距父View左側的距離
MotionEvent中 get 和 getRaw 的區別
event.getX(); //觸摸點相對於其所在組件坐標系的坐標 event.getY(); event.getRawX(); //觸摸點相對於螢幕默認坐標系的坐標 event.getRawY();
這裡只是講解了一些View的獨有關於坐標的方法,因為在平常自定義的時候了解這些方法就已經可以對View進行操作了,比如說拖拽,拉伸,收縮等。
自定義View實戰
自定義View如何做,怎麼做,往往都是第一步比較難,之後對View美化就相對來說比較簡單了。下面我寫了幾個步驟
- 繼承View
- MyView extends View重寫裡面重要的3-4個構造方法
- onMeasure測量控制項
- onMeasure(int widthMeasureSpec, int heightMeasureSpec) 獲取螢幕,自定義View父組件尺寸等。
- onDraw繪製View
- onDraw(Canvas canvas)使用canvas去繪製View,並展示出來
我們就根據上面步驟一一解答
繼承View
我這裡寫了三個構造方法,也可以寫四個,但如果只寫一個會出現問題,比如說在XML文件中使用會報錯
public MyView(Context context) { super(context); init(); } public MyView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); this.context = context; init(); } public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); }
onMeasure測量控制項
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //以下大小均為獲取父布局尺寸 measureWidth = MeasureSpec.getSize(widthMeasureSpec); measureHeight = MeasureSpec.getSize(heightMeasureSpec); maxWidth = getMaxWidth(context); maxHeight = getMaxHeight(context); Log.e("measure", maxWidth + " -- " + maxHeight); measureWidthMode = MeasureSpec.getMode(widthMeasureSpec); measureHeightMode = MeasureSpec.getMode(heightMeasureSpec); Log.e("measure", "measureWidth:" + measureWidth + "measureHeight:" + measureHeight + "measureWidthMode:" + measureWidthMode + "measureHeightMode:" + measureHeightMode); } // 獲取最大寬度 public int getMaxWidth(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); return dm.widthPixels; } // 獲取最大高度 public int getMaxHeight(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); return dm.heightPixels; }
widthMeasureSpec,heightMeasureSpec 這兩個參數不是簡單的整數類型,而是2位整數(模式類型)和30位整數(實際數值) 的組合,所以我們要通過MeasureSpec 獲取模式int值 和 獲取數值int值。這個時候才是我們想要的尺寸,比如說螢幕是1080 * 1920,獲取的值也就是1080 * 1920。
onDraw繪製View
這個方法就厲害了,所有的繪製工作都是裡面的canvas去完成,canvas翻譯過來是帆布的意思,對我們來說就是畫布,畫布有了,還差畫筆,有筆有布有多彩墨水才能畫出大好河山嘛,這裡先介紹畫筆Paint
1:初始畫筆 —– Paint的使用
canvas.drawCircle(100, 100, 50, paint)
返回值
|
簡介
|
int
|
getFlags()
獲取畫筆相關的一些設置(標誌)。
|
int
|
getFlags()
獲取畫筆相關的一些設置(標誌)。
|
void
|
setFlags(int flags)
設置畫筆的標誌位。
|
void
|
set(Paint src)
複製 src 的畫筆設置。
|
void
|
reset()
將畫筆恢復為默認設置。
|
int
|
getAlpha()
只返回顏色的alpha值。
|
void
|
setAlpha(int a)
設置透明度。
|
int
|
getColor()
返回畫筆的顏色。
|
void
|
setColor(int color)
設置顏色。
|
void
|
setARGB(int a, int r, int g, int b)
設置帶透明通道的顏色。
|
float
|
getStrokeWidth()
返回描邊的寬度。
|
void
|
setStrokeWidth(float width)
設置線條寬度。
|
Paint.Style
|
getStyle()
返回paint的樣式,用於控制如何解釋幾何元素(除了drawBitmap,它總是假定為FILL_STYLE)。
|
void
|
setStyle(Paint.Style style)
設置畫筆繪製模式(填充,描邊,或兩者均有)。
|
Paint.Cap
|
getStrokeCap()
返回paint的Cap,控制如何處理描邊線和路徑的開始和結束。
|
void
|
setStrokeCap(Paint.Cap cap)
設置線帽。
|
Paint.Join
|
getStrokeJoin()
返回畫筆的筆觸連接類型。
|
void
|
setStrokeJoin(Paint.Join join)
設置連接方式。
|
float
|
getStrokeMiter()
返回畫筆的筆觸斜接值。用於在連接角度銳利時控制斜接連接的行為。
|
void
|
setStrokeMiter(float miter)
設置畫筆的筆觸斜接值。用於在連接角度銳利時控制斜接連接的行為。
|
PathEffect
|
getPathEffect()
獲取畫筆的 patheffect 對象。
|
PathEffect
|
setPathEffect(PathEffect effect)
設置 Path 效果。
|
boolean
|
getFillPath(Path src, Path dst)
將任何/所有效果(patheffect,stroking)應用於src,並將結果返回到dst。
結果是使用此畫筆繪製繪製 src 將與使用默認畫筆繪製繪製 dst 相同(至少從幾何角度來說是這樣的)。
|
Style
|
簡介
|
Paint.Style.FILL
|
填充內容,也是畫筆的默認模式。
|
Paint.Style.STROKE
|
描邊,只繪製圖形輪廓。
|
Paint.Style.FILL_AND_STROKE
|
描邊+填充,同時繪製輪廓和填充內容。
|
paint = new Paint(); paint.setColor(Color.BLACK);//畫筆顏色 paint.setStrokeWidth(5f);//邊的寬度 paint.setStyle(Paint.Style.STROKE);//描邊
上面canvas.drawCircle(100, 100, 50, paint)和paint的創建 都是寫在onDraw方法里的。上整體程式碼
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //樣式一 canvas.drawCircle(200, 200, 100, paint); //樣式二 Path arcPath = new Path(); arcPath.addArc(new RectF(100, 100, 500, 500), 120, 300); // Paint paint = new Paint(); // paint.setStyle(Paint.Style.STROKE); // canvas.drawPath(arcPath, paint); Path borderPath = new Path(); Paint paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeWidth(100); paint.getFillPath(arcPath, borderPath); // getFillPath // 測試畫筆,注意設置為 STROKE Paint testPaint = new Paint(); testPaint.setStyle(Paint.Style.STROKE); testPaint.setStrokeWidth(2); testPaint.setAntiAlias(true); // 繪製通過 getFillPath 獲取到的 Path canvas.drawPath(borderPath, testPaint); }
好了,到這裡對canvas畫簡單圖案是告一段落了,那麼我們之前獲取到的尺寸是幹嘛用的呢,下面我們對拖拽進行講解,拖拽其實就是down,move,up對著三者的一個解析,當我們手指按下的時候將會出發down,手指一動時觸發move,手指抬起時觸發up。那麼我們怎麼去監聽他的,有方法,那就是onTouchEvent,觸摸方法的事件分發機制我們下節講。這裡我們直接上程式碼
拖拽圖案
@Override public boolean onTouchEvent(MotionEvent event) { //手指按下 int action = event.getAction(); //獲取手機觸摸的坐標 int x = (int) event.getX(); int y = (int) event.getY(); switch (action){ case MotionEvent.ACTION_DOWN://按下,獲取小球初始的位置 startLeft = getLeft(); startRight = getRight(); startTop = getTop(); startBottom = getBottom(); lastX = x; lastY = y; break; case MotionEvent.ACTION_MOVE://移動,小球跟隨手指的移動 Log.e("event","getRawX():"+event.getRawX()+" getRawY():"+event.getRawY()); Log.e("event","getX():"+event.getX()+" getY():"+event.getY()); int offsetX = x - lastX; int offsetY = y - lastY; layout(getLeft()+offsetX,getTop()+offsetY, getRight()+offsetX,getBottom()+offsetY); break; case MotionEvent.ACTION_UP://當手指抬起時,回到小球初始的位置 // layout(startLeft, startTop, startRight, startBottom); break; } return true; }
好了,到這裡,就可以拖拽你畫的圖案了。下一篇講解View的事件分發機制。