<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] 自定義 View 系列參考學習資料:[https://github.com/GcsSloop/AndroidNote](https://github.com/GcsSloop/AndroidNote) # 基礎掌握 ## 坐標系 * 移動設備定義屏幕左上角為坐標原點,向右為x軸增大方向,向下為y軸增大方向。 * View 的坐標系是相對父控件而言,getTop()方法獲取子 View 左上角距父 View 頂部的距離,getLeft()方法獲取子 View 左上角距父 View 左側的距離。getBottom() 和 getRight()同理。 * MotionEvent 中 event 的 getX()和 get()方法獲取的是觸摸點相對于其所在組件坐標系的坐標;event.getRawX() 和 event.getRawY()獲取的是觸摸點相對于屏幕坐標系的坐標。 ![](https://box.kancloud.cn/9ccf77af2345fd7b21a5daec8ee0c9cc_316x480.png) ## 角度弧度 ![](https://box.kancloud.cn/f5db71f6ea089fd3c994d5e569955a96_662x510.jpg) 360(角度)= 2π(弧度) 180(角度)= π(弧度) ## 幾種使用顏色的方式 1、java 中定義 ```java int color = Color.GRAY; int color = Color.argb(127, 255, 0, 0); int color = 0xaaff0000; ``` 2、xml 中定義 ```xml <color name="red">#ff0000</color> ``` 3、java 中引用 xml 中定義的顏色 ```java int color = ContextCompat.getColor(mContext, Color.RED); int color = getColor(R.color.red); // API 需要大于23 ``` 4、xml 中引用顏色 ```xml android:background="@color/red" // 使用 xml 中定義的顏色 android:background="#ff0000" // 直接創建并使用顏色 ``` # 自定義 View 分類及流程 ## 分類 1、繼承View重寫onDraw方法 一般用于實現一些不規則的效果,通過重寫onDraw方法進行繪制。采用這種方式需要自己支持wrap_content和padding的處理。 2、繼承ViewGroup派生特殊的Layout 一般用于實現自定義布局,當某種效果看起來很像幾種View組合在一起的時候,可使用此種方法實現。需要合適的處理ViewGroup的測量、布局過程,并同時處理子View的測量和布局過程。 3、繼承特定的View(比如TextView) 一般擴展某種已有的View的功能,比如TextView。不需要自己支持wrap_content和padding等 4、繼承特定的ViewGroup(比如LinearLayout) 當某種效果看起來很像幾種View組合在一起的時候,采用此方法實現。不需要自己處理ViewGroup的測量和布局。 ## 流程 ![](https://box.kancloud.cn/4d7c7a3bdc5f8fff8fa9ac975676e15c_552x626.jpeg) 1、View 初始化(構造函數) 構造函數是View的入口,可以用于初始化一些的內容,和獲取自定義屬性。 View的構造函數有四種重載分別如下: ```java // 一般在直接 new 一個 View 的時候調用(如在 Activity 中) public void SloopView(Context context) {} // 一般在 layout 文件中使用的時候會調用,關于它的所有屬性(包括自定義屬性)都會包含在 attrs 中傳遞進來(如在布局文件中) public void SloopView(Context context, AttributeSet attrs) {} // 第三個參數是默認的 Style,這里的默認的 Style 是指它在當前 Application 或 Activity 所用的 Theme 中的默認 Style,且只有在明確調用的時候才會生效 public void SloopView(Context context, AttributeSet attrs, int defStyleAttr) {} // 在 API21 的時候才添加上,可以暫不考慮 public void SloopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {} ``` 2、測量 View 大小(onMeasure 方法) 因為 View 的大小不僅由自身所決定,同時也會受到父控件的影響,所以為了我們的控件能更好的適應各種情況,一般會自己進行測量。 可以從onMeasure的兩個參數中取出寬高的相關數據: ```java @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthsize = MeasureSpec.getSize(widthMeasureSpec); //取出寬度的確切數值 int widthmode = MeasureSpec.getMode(widthMeasureSpec); //取出寬度的測量模式 int heightsize = MeasureSpec.getSize(heightMeasureSpec); //取出高度的確切數值 int heightmode = MeasureSpec.getMode(heightMeasureSpec); //取出高度的測量模式 } ``` 測量模式有如下三種: | 模式 | 描述 | | --- | --- | | UNSPPECIFIED | 默認值,父控件沒有給子view任何限制,子View可以設置為任意大小。 | | EXACTLY | 表示父控件已經確切的指定了子View的大小。| | AT_MOST | 表示子View具體大小沒有尺寸限制,但是存在上限,上限一般為父View大小。 | 注意: 如果對View的寬高進行修改了,不要調用super.onMeasure(widthMeasureSpec,heightMeasureSpec);要調用setMeasuredDimension(widthsize,heightsize); 這個函數。 3、確定 View 大小(onSizeChanged 方法) 這個函數在視圖大小發生改變時調用。因為View的大小不僅由View本身控制,而且受父控件的影響,所以我們在確定View大小的時候最好使用系統提供的onSizeChanged回調函數。 ```java @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); } ``` 4、確定子 View 布局(onLayout 方法) 用于確定子 View 位置,在自定義 ViewGroup 中會用到,它調用的是子 View的 layout 函數。 在自定義 ViewGroup 中,onLayout 一般是循環取出子 View,然后經過計算得出各個子 View 位置的坐標值,然后用以下函數設置子 View 位置。 ```java child.layout(l, t, r, b); ``` 四個參數分別為: |名稱|說明|對應函數| |---|----|---| l|View 左側距父 View 左側的距離|getLeft() t|View 頂部距父 View 頂部的距離|getTop) r|View 右側距父 View 右側的距離|getRight() b|View 底部距父 View 底部的距離|getBottom() 5、繪制內容(onDraw 方法) onDraw 方法使用的是Canvas繪圖。 ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } ``` 6、對外提供操作方法和監聽回調 自定義完View之后,一般會對外暴露一些接口,用于控制View的狀態等,或者監聽View的變化。 # Canvas 基本繪制 ## 畫布 Canvas Canvas我們可以稱之為畫布,能夠在上面繪制各種東西,是安卓平臺2D圖形繪制的基礎 ## 畫筆 Paint ```java // 1.創建一個畫筆 private Paint mPaint = new Paint(); // 2.初始化畫筆 private void initPaint() { mPaint.setColor(Color.BLACK); //設置畫筆顏色 mPaint.setStyle(Paint.Style.FILL); //設置畫筆模式為填充 mPaint.setStrokeWidth(10f); //設置畫筆寬度為10px } // 3.在構造函數中初始化 public SloopView(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); } ``` 畫筆相關常用方法表: |標題|相關方法|備注| |---|---|---| |色彩|setColor setARGB setAlpha|設置顏色,透明度| |大小|setTextSize|設置文本字體大小| |字體|setTypeface|設置或清除字體樣式| |樣式|setStyle|填充(FILL),描邊(STROKE),填充加描邊(FILL_AND_STROKE)| |對齊|setTextAlign|左對齊(LEFT),居中對齊(CENTER),右對齊(RIGHT)| |測量|measureText|測量文本大小(注意,請在設置完文本各項參數后調用)| 畫筆的三種填充模式如下: ```plain STROKE //描邊 FILL //填充 FILL_AND_STROKE //描邊加填充 ``` 效果圖如下: ![](https://box.kancloud.cn/1f5c17054e3186822bc069f86ccf1978_1080x1920.jpeg) ## Canvas 的常用操作速查表 |操作類型|相關 API|備注 |---|---|---| |繪制顏色|drawColor, drawRGB, drawARGB|使用單一顏色填充整個畫布| |繪制基本形狀|drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc|依次為 點、線、矩形、圓角矩形、橢圓、圓、圓弧| |繪制圖片|drawBitmap, drawPicture|繪制位圖和圖片| |繪制文本|drawText, drawPosText, drawTextOnPath|依次為 繪制文字、繪制文字時指定每個文字位置、根據路徑繪制文字| |繪制路徑|drawPath|繪制路徑,繪制貝塞爾曲線時也需要用到該函數| |頂點操作|drawVertices, drawBitmapMesh|通過對頂點操作可以使圖像形變,drawVertices直接對畫布作用、 drawBitmapMesh只對繪制的Bitmap作用| |畫布剪裁|clipPath, clipRect|設置畫布的顯示區域| |畫布快照|save, restore, saveLayerXxx, restoreToCount, getSaveCount|依次為 保存當前狀態、 回滾到上一次保存的狀態、 保存圖層狀態、 回滾到指定狀態、 獲取保存次數| |畫布變換|translate, scale, rotate, skew|依次為 位移、縮放、 旋轉、錯切 |Matrix(矩陣)|getMatrix, setMatrix, concat|實際畫布的位移,縮放等操作的都是圖像矩陣Matrix,只不過Matrix比較難以理解和使用,故封裝了一些常用的方法。| ## Canvas 繪制部分圖形 1、繪制圓角矩形 ```java // 第一種 RectF rectF = new RectF(100,100,800,400); canvas.drawRoundRect(rectF,30,30,mPaint); // 第二種 canvas.drawRoundRect(100,100,800,400,30,30,mPaint); ``` 與繪制矩形相比,圓角矩形多出來了兩個參數rx 和 ry,這兩個參數是橢圓的兩個半徑,如圖所示: ![](https://box.kancloud.cn/03922874925c5167260a3b766ef1847c_300x500.jpeg) 2、繪制圓弧 方法如下: ```java // 第一種 public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){} // 第二種 public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint) {} ``` 多出的三個參數: ```plain startAngle // 開始角度 sweepAngle // 掃過角度 useCenter // 是否使用中心 ``` 3、繪制位圖 推薦通過 BitmapFactory 獲取 Bitmap 常用方法有: ```java // 第一種,后兩個參數(matrix, paint)是在繪制的時候對圖片進行一些改變 public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) // 第二種,在繪制時指定了圖片左上角的坐標(距離坐標原點的距離) public void drawBitmap (Bitmap bitmap, float left, float top, Paint paint) // 第三種,參數 src 指定繪制圖片的區域,參數 dst 指定圖片在屏幕上顯示(繪制)的區域 public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint) public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint) ``` 4、繪制文字 方法如下: ```java // 第一類:只能指定文本基線位置(基線x默認在字符串左側,基線y默認在字符串下方) public void drawText (String text, float x, float y, Paint paint) public void drawText (String text, int start, int end, float x, float y, Paint paint) public void drawText (CharSequence text, int start, int end, float x, float y, Paint paint) public void drawText (char[] text, int index, int count, float x, float y, Paint paint) // 第二類:可以分別指定每個文字的位置 public void drawPosText (String text, float[] pos, Paint paint) public void drawPosText (char[] text, int index, int count, float[] pos, Paint paint) // 第三類:指定一個路徑,根據路徑繪制文字 public void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) public void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint) ``` 注意:關于文字居中問題! > drawText()方法中x,y指的是基線中間的那個點,是因為setTextAlign(Paint.Align.Center) 那么要想在正中間顯示文字,x只要為矩形的中點x坐標即可 x = rect.centerX() 要計算的就是基線中間圖上紅色點的y坐標了,看圖可以發現紅色點的y為矩形中點黑色點的y坐標+圖中黑色點和紅色點之間的距離 矩形y坐標為 rect.centerY() 黑色點和紅色點之間的距離為相對于基線的(top+bottom)/2 - bottom 而 top是相對于基線的所以為負數,所以公式為 (-top+bottom)/2 - bottom簡化下為-top/2 - bottom/2 所以最后計算為rect.centerY - top/2 - bottom/2. ![](https://box.kancloud.cn/ea3e7d2beec7477600c75ffd9fba8bf2_545x452.jpg) 示例代碼如下: ```java Rect rect = new Rect(100,100,500,500);//畫一個矩形 Paint rectPaint = new Paint(); rectPaint.setColor(Color.BLUE); rectPaint.setStyle(Paint.Style.FILL); canvas.drawRect(rect, rectPaint); Paint textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setTextSize(50); textPaint.setStyle(Paint.Style.FILL); //該方法即為設置基線上那個點究竟是left,center,還是right 這里我設置為center textPaint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); float top = fontMetrics.top;//為基線到字體上邊框的距離,即上圖中的top float bottom = fontMetrics.bottom;//為基線到字體下邊框的距離,即上圖中的bottom int baseLineY = (int) (rect.centerY() - top/2 - bottom/2);//基線中間點的y軸計算公式 canvas.drawText("你好世界",rect.centerX(),baseLineY,textPaint); ``` 參考鏈接:[https://blog.csdn.net/zly921112/article/details/50401976](https://blog.csdn.net/zly921112/article/details/50401976) ## Canvas 操作 注意:所有的畫布操作都只影響后續的繪制,對之前已經繪制過的內容沒有影響。 1、位移(translate) translate 是坐標系的移動 可以為圖形繪制選擇一個合適的坐標系。 注意,位移是基于當前位置移動,而不是每次基于屏幕左上角的(0,0)點移動 2、縮放(scale) 方法如下: ```java public void scale (float sx, float sy) public final void scale (float sx, float sy, float px, float py) ``` 前兩個參數是相同的分別為x軸和y軸的縮放比例。而第二種方法比前一種多了兩個參數,用來控制縮放中心位置的。 縮放比例(sx,sy)取值范圍詳解: |取值范圍|說明| |---|---| |(-∞, -1)|先根據縮放中心放大 n 倍,再根據中心軸進行翻轉| |-1|根據縮放中心軸進行翻轉| |(-1, 0)|先根據縮放中心縮小到 n,再根據中心軸進行翻轉| |0|不會顯示,若 sx 為 0,則寬度為 0,不會顯示,sy 同理| |(0, 1)|根據縮放中心縮小到 n| |1|沒有變化| |(1, +∞)|根據縮放中心放大 n 倍| 3、旋轉(rotate) 方法如下: ```java public void rotate (float degrees) // 多出來的兩個參數依舊是控制旋轉中心點的 public final void rotate (float degrees, float px, float py) ``` 4、錯切(skew) 錯切是特殊類型的線性變換,方法如下: ```java public void skew (float sx, float sy) ``` 參數含義: float sx:將畫布在 x 方向上傾斜相應的角度,sx 傾斜角度的 tan 值 float sy:將畫布在 y 軸方向上傾斜相應的角度,sy 為傾斜角度的 tan 值 5、快照與回滾(save 與 restore) 畫布的操作是不可逆的,而且很多畫布操作會影響后續的步驟,例如第一個例子,兩個圓形都是在坐標原點繪制的,而因為坐標系的移動繪制出來的實際位置不同。所以會對畫布的一些狀態進行保存和回滾。 相關 API: |相關 API|簡介| |---|---| |save|把當前的畫布的狀態進行保存,然后放入特定的棧中| |saveLayerXxx|新建一個圖層,并放入特定的棧中| |restore|把棧中最頂層的畫布狀態取出來,并按照這個狀態恢復當前的畫布| |restoreToCount|彈出指定位置及其以上所有的狀態,并按照指定位置的狀態進行恢復| |getSaveCount|獲取棧中內容的數量(即保存次數)|
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看