這是自定義View的第一篇文章,通過(guò)制作簡(jiǎn)單的自定義View來(lái)了解自定義View的流程。 自定義View是Android學(xué)習(xí)和開(kāi)發(fā)中必不可少的一部分。通過(guò)自定義View我們可以制作豐富絢麗的控件,自定義View主要有三種方式,具體如下:
這里,我們講第三種方法來(lái)了解自定義View的流程。 自定義View主要依賴的方法自定義VIew中,我們主要重寫(xiě)
下面我們先了解一下這兩個(gè)方法具體能做什么,然后我們通過(guò)一個(gè)實(shí)例來(lái)學(xué)習(xí)一下具體的用法。 onMeasure protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 我們從上面的方法可以看出 測(cè)量模式有一下三種:
具體的做法一般是,我們先根據(jù)參數(shù),得到具體的測(cè)量模式與測(cè)量值,在根據(jù)測(cè)試的模式不同,計(jì)算不同的寬度和高度。最后通過(guò) onDraw
上面說(shuō)了這么多,都是在YY,我們具體通過(guò)一個(gè)例子來(lái)走一遍自定義View的流程。效果如下: 首先我們新建一個(gè)CircleLoadingView.java文件。該類繼承View,生成構(gòu)造器,顯式調(diào)用父類的構(gòu)造器,并初始化我們的Paint對(duì)象,覆寫(xiě)onMeasure,onDraw方法,實(shí)現(xiàn)自定義View. 第一步——初始化在構(gòu)造方法中,我們調(diào)用自己寫(xiě)的initView方法來(lái)初始化Paint對(duì)象 private void initView{ paint = new Paint; //設(shè)置畫(huà)筆的顏色 paint.setColor(circleColor); //設(shè)置抗鋸齒,讓圖像更清晰 paint.setAntiAlias(true); //設(shè)置畫(huà)筆的風(fēng)格,有三種屬性FILL,STROLE,FILL_AND_STROKE,我們不需要填充,所以設(shè)置為STROKE paint.setStyle(Paint.Style.STROKE); //設(shè)置畫(huà)筆的粗細(xì) paint.setStrokeWidth(circleStrokewidth); } onMeasure 接下來(lái),我們需要測(cè)量我們的View的大小,重寫(xiě) @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //設(shè)置一個(gè)默認(rèn)的寬和高,AT_MOST模式需要 int result = 0; //通過(guò)MeasureSpec.getMode與getSize方法獲取寬高的測(cè)量方式與測(cè)量大小 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //保存最后的測(cè)量值,優(yōu)化代碼的話可以不用這個(gè)變量的。 int width = 0,height = 0; //對(duì)測(cè)量模式進(jìn)行判斷,如果是EXACTLY的話則最后的測(cè)量值就是系統(tǒng)幫我們測(cè)量的結(jié)果。 if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; }else{ //如果是UNSPECIFIED 則使用我們的默認(rèn)值作為最后的測(cè)量值 result = 300; //如果是AT_MOST 則就要用系統(tǒng)測(cè)量結(jié)果與我們默認(rèn)結(jié)果取最小值來(lái)決定最后的測(cè)量結(jié)果 if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(result,widthSize); } } //高度和寬度的過(guò)程是一致的。 if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; }else { result = 300; if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(result, heightSize); } } //把我們最后的寬和高設(shè)置進(jìn)去 setMeasuredDimension(width,height); } 這就是 onDraw 我們基本已經(jīng)完成了80%的工作了,接下來(lái)只需要重寫(xiě) @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //決定我們的弧形外接的矩形的寬和高 float wdistance = (float)(wlength * 0.6); float hdistance = (float)(hlength * 0.6); //定義外接矩形 RectF rectF = new RectF( wlength / 2 - wdistance / 2, hlength / 2 - hdistance / 2, wlength / 2 + wdistance / 2, hlength / 2 + hdistance / 2); //畫(huà)弧形 canvas.drawArc( rectF,//外接矩形 270,//起始角度 (float)(240),//劃過(guò)的度數(shù) false,//是否是扇形 paint//畫(huà)筆 ); } 看了上面的代碼,可能有些糊涂,因?yàn)槔锩娑嗔撕芏鄾](méi)有提到的變量。首先的變量就是 wlength = width; hlength = height; 同時(shí)我們定義了外接矩形的寬和高就是我們View的0.6倍,上,下, 左,右各留了0.2倍的內(nèi)邊距。然后通過(guò)new Rectf方法來(lái)生成矩形對(duì)象,4個(gè)參數(shù)分別為矩形的左邊距Y軸的距離(也就是說(shuō)X軸的坐標(biāo)),上邊距X軸的距離(也就是說(shuō)Y軸的坐標(biāo)),右邊距Y軸的距離(也就是說(shuō)X軸的坐標(biāo)),下邊距Y軸的距離(也就是說(shuō)Y軸的坐標(biāo))。具體見(jiàn)下圖 canvas 實(shí)際上就是這個(gè)坐標(biāo)軸。 定義好外接矩形之后,我們開(kāi)始調(diào)用drawArc方法開(kāi)始繪制弧形,這個(gè)方法接受5個(gè)參數(shù),第二個(gè)參數(shù)是弧起始的角度,這個(gè)接受一個(gè)整數(shù)代表角度,他是這樣確定起始的角度的。以我們的手表的3點(diǎn)開(kāi)始,順時(shí)針轉(zhuǎn)過(guò)我們定義的角度,這時(shí)的位置就是我們開(kāi)始的位置,比如我們現(xiàn)在定義的是270,那就是手表3點(diǎn)的位置轉(zhuǎn)過(guò)270度為我們弧形開(kāi)始的位置。就是12點(diǎn)的位置。然后第三個(gè)參數(shù),代表弧形劃過(guò)的角度。也是順時(shí)針。第四個(gè)參數(shù)則代表是否用扇形,我們這里不用,也就是說(shuō)只是一個(gè)兩個(gè)端點(diǎn)不連接圓心的弧,第五個(gè)是我們初始化過(guò)的paint。這樣我們的自定義的弧形就出來(lái)了。 我們就可以把我們的控件放到xml中,當(dāng)成普通的view去引用了。直接看代碼:
這個(gè)弧形其實(shí)并沒(méi)有卵用,不能動(dòng)。我們接下來(lái)讓他動(dòng)起來(lái),這里我們就用繪圖自帶的方法 那這次我們只需要在onDraw方法中,不斷修改弧形的第二個(gè)參數(shù),讓他每次不同就可以了。具體的我們看代碼: @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float wdistance = (float)(wlength * 0.6); float hdistance = (float)(hlength * 0.6); RectF rectF = new RectF( wlength / 2 - wdistance / 2, hlength / 2 - hdistance / 2, wlength / 2 + wdistance / 2, hlength / 2 + hdistance / 2); canvas.drawArc( rectF, //每次重繪,起始的角度就會(huì)多5度。 270 + 5 * i, (float)(240), false, paint ); i++; //調(diào)用重繪,來(lái)實(shí)現(xiàn)圖像不斷繪制 postInvalidate; } 這樣,我們?cè)俅芜\(yùn)行程序的時(shí)候,我們的弧形就動(dòng)起來(lái)。當(dāng)我們?cè)诮佑|動(dòng)畫(huà)概念的時(shí)候,又會(huì)發(fā)現(xiàn)有很多方法實(shí)現(xiàn)弧形旋轉(zhuǎn)。 第五步——完善前面4步,我們已經(jīng)實(shí)現(xiàn)了簡(jiǎn)單的自定義View,并且有一個(gè)可觀的效果。但是我們還要完善一下,比如我們可以在XML中指定弧形的顏色,弧形的粗細(xì)。這個(gè)時(shí)候,我們就需要在values文件下新建一個(gè)attrs.xml。在里面制定我們的自定義屬性,然后在我們的View文件中讀取這些屬性。 我們先看一下attrs.xml中,我們的代碼:
這里,我們就定義了2個(gè)屬性,一個(gè)是弧形的顏色,一個(gè)是弧形的粗細(xì),格式分別是color和float。然后我們?cè)趘iew中的initView代碼中,獲取那些屬性。代碼如下: TypedArray ta = getContext.obtainStyledAttributes(attrs, R.styleable.CircleLoadingView); circleStrokewidth = ta.getFloat(R.styleable.CircleLoadingView_circleStrokewidth, 0); circleColor = ta.getColor(R.styleable.CircleLoadingView_circleColor, 0); 然后我們就可以在我們的xml中引用這些屬性了。
這里的circleview是要在根布局的設(shè)置中聲明的 因?yàn)?,這個(gè)主要出現(xiàn)在加載過(guò)程中,所以他需要一個(gè)方法來(lái)讓他顯示和隱藏。我們?cè)赩iew的文件中,設(shè)置一個(gè)方法叫做 當(dāng)為true的時(shí)候,就可以顯示,當(dāng)為false就可以隱藏。這樣,這個(gè)自定義View就比較完善了。 隱藏方法代碼如下: public void setViewVisable(boolean choose) { if (choose) { this.setVisibility(View.VISIBLE); }else{ this.setVisibility(View.GONE); } } 這個(gè)小控件的源碼我放到網(wǎng)上了,大家可以對(duì)照參考下 |
|
來(lái)自: 月冷星河 > 《Android文章》