无码人妻精一区二区三区,eeuss影院www在线观看,无码精品久久久久久人妻中字,日韩av高清在线看片

推薦新聞
定義 View高級(jí)篇,一看就會(huì)
發(fā)布者:深藍(lán)互聯(lián)
發(fā)布時(shí)間:2019-11-20
點(diǎn)擊:次

自定義View

在實(shí)際使用的過程中,我們經(jīng)常會(huì)接到這樣一些需求,比如環(huán)形計(jì)步器,柱狀圖表,圓形頭像等等,這時(shí)我們通常的思路是去Google 一下,看看 github 上是否有我們需要的這些控件,但是如果網(wǎng)上收不到這樣的控件呢?這時(shí)我們經(jīng)常需要自定義 View 來滿足需求。

 

第一次看我文章的小伙伴可以關(guān)注一下我,順便關(guān)注一下我的專欄:Android高級(jí)開發(fā)架構(gòu),每天更新各種技術(shù)干貨,分享更多最熱程序員圈內(nèi)事。
Android高級(jí)開發(fā)架構(gòu)?zhuanlan.zhihu.com圖標(biāo)

自定義控件之路

關(guān)于自定義控件,一般輝遵循以下幾個(gè)套路

  • 首先重寫 onMeasure() 方法
  • 其次重寫 onDraw() 方法
  • 總所周知 onMeasure()

方法是用來重新測(cè)量,并設(shè)定控件的大小,我們知道控件的大小是用 width 和 height 兩個(gè)標(biāo)簽來設(shè)定的。通常有三種賦值情況 :

  • 首先直接賦值,比如直接給定 15dp 這樣確切的大小
  • 其次 match_parent
  • 當(dāng)然還有 wrap_parent

這時(shí)也許你就會(huì)有疑問,既然都已經(jīng)有了這些屬性,那還重寫 onMeasure 干嘛,直接調(diào)用 View 的方法不就行了嗎?但是你想想,比如你設(shè)計(jì)了一個(gè)圓形控件,用戶在 width 和 height 都設(shè)置了 wrap_parent 屬性,同時(shí)又給你傳了一張長方形的圖片,那結(jié)果會(huì)怎么樣?必然得讓你“方”啊。。所以這時(shí)就需要重寫 onMeasure 方法,設(shè)定其寬高相等。


如何重寫OnMeasure()
首先把 onMeasure() 打出來

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

這時(shí)大家不眠會(huì)好奇,明明是重繪大小,那么給我提供寬高就行了呀?這個(gè) int widthMeasureSpec, int heightMeasureSpec ,是個(gè)什么鬼?其實(shí)很好理解,大家都知道計(jì)算機(jī)中數(shù)據(jù)是已二進(jìn)制存儲(chǔ)的。同時(shí),就像我之前講的 View 的大小賦值形式有三種,那么在計(jì)算機(jī)中,要存儲(chǔ)二進(jìn)制數(shù),需要幾位二進(jìn)制呢,答案很明了 -> 兩位。同時(shí)大家也發(fā)現(xiàn),這兩個(gè)參數(shù)都是 int 型的。int 型數(shù)據(jù)在計(jì)算機(jī)中用 32 位存儲(chǔ)。所以聰明的 Google 就把這 30 位劃分為兩部分。第一部分兩位拿來存類型,后面 28 位拿來存數(shù)據(jù)大小。


開始重寫OnMeasure()方法
首先,無論是 width 還是 height ,我們都得先判斷類型,再去計(jì)算大小,so~ 咱先寫個(gè)方法專門用于計(jì)算并返回大小。

 

 

private int getMySize(int defaultSize, int measureSpec) {
        // 設(shè)定一個(gè)默認(rèn)大小 defaultSize
        int mySize = defaultSize;
        // 獲得類型
        int mode = MeasureSpec.getMode(measureSpec);
        // 獲得大小
        int size = MeasureSpec.getSize(measureSpec);
        
        switch (mode) {
            case MeasureSpec.UNSPECIFIED: {//如果沒有指定大小,就設(shè)置為默認(rèn)大小
                mySize = defaultSize;
                break;
            }
            case MeasureSpec.AT_MOST: {//如果測(cè)量模式是最大取值為size
                //我們將大小取最大值,你也可以取其他值
                mySize = size;
                break;
            }
            case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改變它
                mySize = size;
                break;
            }
        }
        return mySize;
    }

然后,我們?cè)購?onMeasure() 中調(diào)用它

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 分別獲得長寬大小
        int width = getMySize(100, widthMeasureSpec);
        int height = getMySize(100, heightMeasureSpec);
 
        // 這里我已圓形控件舉例
        // 所以設(shè)定長寬相等
        if (width < height) {
            height = width;
        } else {
            width = height;
        }
        // 設(shè)置大小
        setMeasuredDimension(width, height);
    }

在 xml 中應(yīng)用試試效果

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    tools:context=".activities.MainActivity">
 
    <com.entry.android_view_user_defined_first.views.MyView
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:default_size="@drawable/ic_launcher_background"/>
 
</LinearLayout>

到這里圖就已經(jīng)重繪出來了,讓我們運(yùn)行一下下

 

 

我們驚呆了,說好的控件呢??!別急,咱還沒給他上色呢,所以它自然是透明的。所以現(xiàn)在重寫 onDraw() 方法,在 onDraw() 方法中 (這里我為了寫的方便,在 onDraw 方法中直接 new 了對(duì)象 { 嗷我沒有對(duì)象} 但這是一種很容易導(dǎo)致內(nèi)存泄露的行為)

我們通過 canvas (安卓的一個(gè)繪圖類對(duì)象進(jìn)行圖形的繪制)

@Override
    protected void onDraw(Canvas canvas) {
        // 調(diào)用父View的onDraw函數(shù),因?yàn)閂iew這個(gè)類幫我們實(shí)現(xiàn)了一些
        // 基本的而繪制功能,比如繪制背景顏色、背景圖片等
        super.onDraw(canvas);
        int r = getMeasuredWidth() / 2;//也可以是getMeasuredHeight()/2,本例中我們已經(jīng)將寬高設(shè)置相等了
        Log.d(TAG, r + "");
        // 圓心的橫坐標(biāo)為當(dāng)前的View的左邊起始位置+半徑
        int centerX = r;
        // 圓心的縱坐標(biāo)為當(dāng)前的View的頂部起始位置+半徑
        int centerY = r;
        // 定義灰色畫筆,繪制圓形
        Paint bacPaint = new Paint();
        bacPaint.setColor(Color.GRAY);
        canvas.drawCircle(centerX, centerY, r, bacPaint);
        // 定義藍(lán)色畫筆,繪制文字
        Paint paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setTextSize(60);
        canvas.drawText("大傻瓜", 0, r+paint.getTextSize()/2, paint);
    }

運(yùn)行一下

 

 

 

大功告成!但是善于思考的可能會(huì)發(fā)現(xiàn):使用這種方式,我們只能使用父類控件的屬性,但是我們有時(shí)需要更多的功能,比如:圖片控件需要改變透明度,卡片控件需要設(shè)定陰影值等等,那么父類控件的屬性顯然不夠用了,這時(shí)我們就要開始實(shí)現(xiàn)自定義布局。

自定義布局屬性xml屬性

由于自定義布局屬性一般只需要對(duì) onDraw() 進(jìn)行操作。所以 onMeasure() 等方法的重寫我就不再啰嗦了,這里我打算繼承字 view 實(shí)現(xiàn)一個(gè)類似 TextView 的控件。

首先,讓我們現(xiàn)在 res/values/styles 文件中增加一個(gè)自定義布局屬性。

<resources>
 
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
 
    <!--定義屬性集合名-->
    <declare-styleable name="MyView">
        <!--我們定義為 default_size 屬性為 屈指類型 像素 dp 等-->
        <attr name="text_size" format="dimension"/>
        <attr name="text_color" format="color"/>
        <attr name="text_text" format="string"/>
    </declare-styleable>
 
</resources>

這些標(biāo)簽都是什么意思呢?

首先:

MyView 是自定義布局屬性的名字,也就是標(biāo)簽也就是入口,在 onDraw 中,用 context.obtainStyledAttributes(attrs, R.styleable.MyView); 獲得自定義布局屬性的全部子項(xiàng)。

其次:

attr 中的 name 便是你屬性的名字,比如說這個(gè) text_size 、text_color 、text_text 這三個(gè)屬性,在 布局文件中就是:

<com.entry.android_view_user_defined_first.views.MyView
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:text_text="hello world"
        app:text_size="20sp"
        app:text_color="@color/colorAccent"/>

最后:

format 標(biāo)簽,format 標(biāo)簽指定的是數(shù)據(jù)類型,具體可以看這篇,我在這里就不重復(fù)了 ->


解析和引用

上面我們先定義了屬性,又在布局中對(duì)其賦值,那么實(shí)際中,我們?nèi)绾卧谧远x控件里,獲得它的實(shí)際值呢?讓我們先寫下構(gòu)造方法,在構(gòu)造方法中獲得這些值的大?。?/p>

private int textSize;
    private String textText;
    private int textColor;
 
    public MyView(Context context) {
        super(context);
    }
 
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
 
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);
 
        textSize = array.getDimensionPixelSize(R.styleable.MyView_text_size, 15);
        textText = array.getString(R.styleable.MyView_text_text);
        textColor = array.getColor(R.styleable.MyView_text_color,Color.BLACK);
 
        array.recycle();
    }
  • 建立一個(gè) TypeArray 對(duì)象,用于存儲(chǔ)自定義屬性所傳入的的值。obtainStyledAttributes 方法又兩個(gè)參數(shù),第二個(gè)參數(shù)就是我們?cè)趕tyles.xml文件中的 標(biāo)簽,即屬性集合的標(biāo)簽,在R文件中名稱為R.styleable+name
  • 然后根據(jù) array 對(duì)象,獲取傳入的值。一般來說,它的方法有兩個(gè)屬性,第一個(gè)參數(shù)為屬性集合里面的屬性,R文件名稱:R.styleable+屬性集合名稱+下劃線+屬性名稱,第二個(gè)參數(shù)為,如果沒有設(shè)置這個(gè)屬性,則設(shè)置的默認(rèn)的值
  • 最后記得將TypedArray對(duì)象回收

重寫onDraw()

由于在構(gòu)造方法中,我們已經(jīng)獲得基本的值,所以在 onDraw() 中,將這些東西繪制出來就行了,這里直接上代碼:

@Override
    protected void onDraw(Canvas canvas) {
        // 調(diào)用父View的onDraw函數(shù),因?yàn)閂iew這個(gè)類幫我們實(shí)現(xiàn)了一些
        // 基本的而繪制功能,比如繪制背景顏色、背景圖片等
        super.onDraw(canvas);
        int r = getMeasuredWidth() / 2;//也可以是getMeasuredHeight()/2,本例中我們已經(jīng)將寬高設(shè)置相等了
        // 圓心的橫坐標(biāo)為當(dāng)前的View的左邊起始位置+半徑
        int centerX = r;
        // 圓心的縱坐標(biāo)為當(dāng)前的View的頂部起始位置+半徑
        int centerY = r;
        // 定義灰色畫筆,繪制圓形
        Paint bacPaint = new Paint();
        bacPaint.setColor(Color.GRAY);
        canvas.drawCircle(centerX, centerY, r, bacPaint);
        // 定義藍(lán)色畫筆,繪制文字
        Paint paint = new Paint();
        paint.setColor(textColor);
        paint.setTextSize(textSize);
        canvas.drawText(textText, 0, r+paint.getTextSize()/2, paint);
    }

運(yùn)行:

 

 

關(guān)注深藍(lán)互聯(lián)公眾號(hào)
Copyright ? 2013-2025 深藍(lán)互聯(lián) 版權(quán)所有
友情鏈接: