日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android自绘制控件

發布時間:2024/10/12 Android 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android自绘制控件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

開發過程中,我們免不了需要用到一些自定義的 View,自定義 View 一般可分為三類:

  ① 繼承類 View —— 一般繼承系統以后的基本 View,新增/重置一些自定義屬性 ,例如兩端對齊的TextView;

  ②?組合類 View —— 將系統某幾個基本View組合在一起形成一個新的View,例如末尾帶 ”ד(清空) 的EditText,就是將EditText和ImageView組合在一起來實現;?

  ③?自繪制 View —— 某些特殊的設計控件,無法通過上兩種方式實現時,我們就需要考慮通過自繪制來進行處理,本篇我們將著重介紹此類 View 的實現過程。

?

下面我們通過自定義一個圓形的Button(DCircleButton)來進行說明:

自定義View的步驟:

?① 自定義 View 的屬性;

?② 在自定義 View 的構造方法中獲取 View 的屬性值;

?③ 重寫測量尺寸的方法 onMeasure(int, int); (是否需要重寫根據具體根據需求);

?④ 重寫繪制方法 onDraw(Canvas c);?

?⑤ 在布局XML文件中,使用自定義 View 的屬性。

?

1. 自定義 View 的屬性:

在目錄 res/values 下新建 attrs.xml 屬性文件。

<?xml version="1.0" encoding="utf-8"?> <resources><!--name 是自定義控件的類名--><declare-styleable name="DCircleButton" parent="android.widget.Button"><attr name="txtSize" format="dimension"/><attr name="text" format="string"/><attr name="txtColor" format="color"/><attr name="txtBackgroundColor" format="color"/></declare-styleable> </resources>

?

自定義屬性分兩步: ① 定義控件的主題樣式;
  ②?定義屬性名稱及類型。
如上面的 xml 文件是自定義控件DCirclebutton的主題樣式,主題樣式里為屬性定義,有些人可能會糾結format字段后面都有哪些屬性單位?如果你是使用AS開發的話IDE會自動有提示,基本包括如下:
dimension(字體大小)string(字符串)color(顏色)boolean(布爾類型)float(浮點型)integer(整型)enmu(枚舉)fraction(百分比)等。

2. ?在構造方法中獲取屬性值,并繪制

第一步:繼承View,實現(AS會提示)以下四種,

public DCircleButton(Context context) {super(context); } public DCircleButton(Context context, @Nullable AttributeSet attrs) {super(context, attrs); } public DCircleButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public DCircleButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes); }

第二步,改寫這四種構造,讓其逐級遞進:

public DCircleButton(Context context) {super(context, null); } public DCircleButton(Context context, AttributeSet attrs) {super(context, attrs, 0); } public DCircleButton(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr, 0); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public DCircleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes); }

第三步,我們在最后一個方法獲取屬性值:

private void initAttrs(Context context, AttributeSet attrs) {TypedArray tArr = context.obtainStyledAttributes(attrs, R.styleable.DCircleButton);if (null != tArr) {txtColor = tArr.getColor(R.styleable.DCircleButton_txtColor, Color.BLACK); // 獲取文字顏色txtSize = tArr.getDimensionPixelSize(R.styleable.DCircleButton_txtSize, 18); // 獲取文字大小txt = tArr.getString(R.styleable.DCircleButton_text); // 獲取文字內容backgroundColor = tArr.getColor(R.styleable.DCircleButton_txtBackgroundColor, Color.GRAY); // 獲取文字背景顏色 tArr.recycle();} }

第四步,繪制

/** 字體顏色 **/ private int txtColor; /** 字體背景顏色 **/ private int backgroundColor; /** 字體大小 **/ private int txtSize; /** 按鈕文字內容 **/ private String txt; /** 圓半徑 **/ private float mDrawableRadius;/** 字體背景畫筆 **/ private Paint mBackgroundPaint; /** 字體畫筆 **/ private Paint mTxtPaint;public DCircleButton(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initAttrs(context, attrs);init(); } /** 初始化 **/ private void init() {mBackgroundPaint = new Paint();mBackgroundPaint.setColor(backgroundColor);mTxtPaint = new Paint();mTxtPaint.setTextAlign(Paint.Align.CENTER);mTxtPaint.setColor(txtColor);mTxtPaint.setTextSize(txtSize); }@Override protected void onDraw(Canvas canvas) {super.onDraw(canvas);mDrawableRadius = Math.min(getWidth() >> 1, getHeight() >> 1);canvas.drawCircle(getWidth() >> 1, getHeight() >> 1, mDrawableRadius, mBackgroundPaint);if (null != txt)canvas.drawText(txt, getWidth() >> 1, getHeight() >> 1, mTxtPaint); }

?3. 布局中應用

<dinn.circle.button.DCircleButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"app:text="我是按鈕"app:txtBackgroundColor="@android:color/holo_orange_dark"app:txtColor="@android:color/white"app:txtSize="20sp" />

?4. 運行結果

?

這個時候回發現按鈕是充滿屏幕的,但是布局中我們設置的尺寸屬性為“wrap_content”。其實是由于我們在自定義View的流程中還有一個onMeasure方法沒有重寫。

5. 重寫onMeasure控制View的大小

當你沒有重寫onMeasure方法時候,系統調用默認的onMeasure方法。

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); }

這個方法的作用是:測量控件的大小。其實Android系統在加載布局的時候是由系統測量各子View的大小來告訴父View我需要占多大空間,然后父View會根據自己的大小來決定分配多大空間給子View。

那么從上面的效果來看,當你在布局中設置View的大小為”wrap_content”時,其實系統測量出來的大小是“match_parent”。為什么會是這樣子呢?

那得從MeasureSpec的specMode模式說起了。一共有三種模式:

MeasureSpec.EXACTLY:父視圖希望子視圖的大小是specSize中指定的大小;一般是設置了明確的值或者是MATCH_PARENT。

MeasureSpec.AT_MOST:子視圖的大小最多是specSize中的大小;表示子布局限制在一個最大值內,一般為WARP_CONTENT。

MeasureSpec.UNSPECIFIED:父視圖不對子視圖施加任何限制,子視圖可以得到任意想要的大小;表示子布局想要多大就多大,很少使用。

?

我們看看系統源碼 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 是如何實現的:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } public static int getDefaultSize(int size, int measureSpec) {int result = size;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED:result = size;break;case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specSize;break;}return result; }

從上面的代碼 getDefaultSize() 方法中看出,原來 MeasureSpec.AT_MOST 和 MeasureSpec.EXACTLY 走的是同一個分支,也就是父視圖希望子視圖的大小是specSize中指定的大小。

得出來的默認值就是填充整個父布局。因此,不管你布局大小是 ”wrap_content” 還是 “match_parent” 效果都是充滿整個父布局。那我想要 ”wrap_content” 的效果怎么辦?那么只有重寫onMeasure方法了。

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 測量模式int widthMode = MeasureSpec.getMode(widthMeasureSpec);// 父布局希望子布局的大小,如果布局里面設置的是固定值,這里取布局里面的固定值和父布局大小值中的最小值.// 如果設置的是match_parent,則取父布局的大小int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int width;int height;Rect mBounds = new Rect();if (widthMode == MeasureSpec.EXACTLY) {width = widthSize;} else {mTxtPaint.setTextSize(txtSize);mTxtPaint.getTextBounds(txt, 0, txt.length(), mBounds);float textWidth = mBounds.width();int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());width = desired;}if (heightMode == MeasureSpec.EXACTLY) {height = heightSize;} else {height = width;}// 最后調用父類方法,把View的大小告訴父布局。 setMeasuredDimension(width, height); }

這樣實現的最終效果如下:

?

轉載于:https://www.cnblogs.com/steffen/p/9941947.html

總結

以上是生活随笔為你收集整理的Android自绘制控件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。