先給大家展示下效果圖,如果感覺不錯(cuò),請參考實(shí)現(xiàn)思路:

我們要實(shí)現(xiàn)一個(gè)自定義的再一個(gè)圓形中繪制一個(gè)弧形的自定義View,思路是這樣的:
先要?jiǎng)?chuàng)建一個(gè)類ProgressView,繼承自View類,然后重寫其中的兩個(gè)構(gòu)造方法,一個(gè)是一個(gè)參數(shù)的,一個(gè)是兩個(gè)參數(shù)的,因?yàn)槲覀円趚ml文件中使用該自定義控件,所以必須要定義這個(gè)兩個(gè)參數(shù)的構(gòu)造函數(shù)。創(chuàng)建完了這個(gè)類后,我們先不去管它,先考慮我們實(shí)現(xiàn)的這個(gè)自定義View,我們想讓它的哪些部分可以由使用者自己指定,比如說這個(gè)Demo中我們讓他的外面圓的外邊框顏色和寬度,還有扇形部分的顏色,扇形增長的速度等等屬性,這時(shí)候我們要在項(xiàng)目工程目錄的res/values目錄下創(chuàng)建一個(gè)資源文件命名為attrs(注意,名字隨意,只是大多數(shù)情況下都這么叫而已),然后我們在這個(gè)資源文件中添加我們想要的屬性,如下:
<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="ProgressView"><!--circleColor 設(shè)置圓形邊框的顏色 sweepColor設(shè)置扇形變換的顏色startAngle 設(shè)置起始角度 sweepStep 設(shè)置變換的步長--><attr name="circleColor" format="color|reference"></attr><attr name="sweepColor" format="color|reference"></attr><attr name="startAngle" format="integer"></attr><attr name="sweepStep" format="integer"></attr><attr name="padding" format="integer"></attr></declare-styleable></resources>
可以看到,<declare-styleable>標(biāo)簽中的name屬性是為了方便我們獲取AttributeSet時(shí)候使用,而<attr>標(biāo)簽中name,則是我們希望控件可以自定義的屬性部分,類似于xml文件中的android:name=""等標(biāo)簽的使用。format屬性是設(shè)置該屬性可以接受什么類型的參數(shù)。
定義好了自定義資源類,我們開始寫ProgressView中的主要代碼:
package com.yztc.customprogressview;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.util.AttributeSet;import android.view.View;/*** 自定義的*/public class ProgressView extends View {private int sweepStep = 10;//扇形變換的步長(就是角度)private int padding = 40;//外邊框距離扇形的距離 填充private int circleColor = Color.GRAY;//邊框的顏色private int sweepColor = Color.BLUE;//扇形顏色private int startAngle = 90;//起始角度//設(shè)置外邊框圓的邊框粗細(xì)private int storke = 20;private int sweepAngle = 0;//掃過的角度private static final int DEFAULT_WIDTH = 200;private static final int DEFAULT_HEIGHT = 200;public ProgressView(Context context) {super(context);}//如果要在xml文件中使用該自定義控件,則必須重寫兩個(gè)參數(shù)的構(gòu)造函數(shù)//因?yàn)槲覀兪褂米远x的屬性的時(shí)候,會默認(rèn)傳遞過來一個(gè)AttributeSet集合public ProgressView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView);if (array != null) {//獲取我們在xml中設(shè)置的各個(gè)自定義屬性sweepStep = array.getInteger(R.styleable.ProgressView_sweepStep, sweepStep);padding = array.getInteger(R.styleable.ProgressView_padding, padding);circleColor = array.getColor(R.styleable.ProgressView_circleColor, circleColor);sweepColor = array.getColor(R.styleable.ProgressView_sweepColor, sweepColor);startAngle = array.getInteger(R.styleable.ProgressView_startAngle, startAngle);//回收TypeArray資源array.recycle();}}/*** 先繪制外邊框 --內(nèi)部扇形** @param canvas*/@Overrideprotected void onDraw(Canvas canvas) {Paint mPaint = new Paint();mPaint.setAntiAlias(true); //設(shè)置抗鋸齒//繪制外層的圓框mPaint.setColor(circleColor);mPaint.setStrokeWidth(storke);mPaint.setStyle(Paint.Style.STROKE);//設(shè)置圓形為空心的圓//這里我們得到控件的Height和Width,根據(jù)Heigh和Width來確定圓心的位置,來繪制外層圓canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - storke / 2, mPaint);// Log.d("tag", "onDraw: "+getWidth());invalidate();//請求重新繪制view//繪制內(nèi)部的扇形mPaint.setStyle(Paint.Style.FILL_AND_STROKE);mPaint.setColor(sweepColor);/*drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)RectF oval 指定扇形的矩形容器對象 指定圓弧的外輪廓的矩形float startAngle 表示圓弧的起始角度float sweepAngle 表示圓弧走過掃過的角度 順時(shí)針方向boolean useCenter 如果設(shè)置為true 在繪制圓弧時(shí)將圓心包括在內(nèi),是指以一個(gè)固定的圓心來繪制弧形(扇形),如果指定為false,則不規(guī)則繪制扇形Paint paint 畫筆 顏色 填充public RectF(float left, float top, float right, float bottom)float left 矩形的左邊點(diǎn)(左切點(diǎn))的x坐標(biāo)float top 矩形上邊點(diǎn)(上切點(diǎn))的y軸坐標(biāo)float right, 矩形的右邊點(diǎn)(右切點(diǎn))的x坐標(biāo)float bottom 矩形的下邊點(diǎn)(下切點(diǎn))的y坐標(biāo)*///RectF中的四個(gè)參數(shù),分別對應(yīng)其內(nèi)切圓的四個(gè)切點(diǎn)的坐標(biāo)RectF rectF = new RectF(padding + storke, padding + storke, getWidth() - padding - storke, getWidth() - padding - storke);canvas.drawArc(rectF, startAngle, sweepAngle, true, mPaint);sweepAngle += sweepStep;//根據(jù)步長更新掃過的角度sweepAngle = sweepAngle > 360 ? 0 : sweepAngle;invalidate();//重繪view}//因?yàn)槲覀兪抢^承的View來自定義的View,所以onMeasure()方法要重寫@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int wMode = MeasureSpec.getMode(widthMeasureSpec);int hMode = MeasureSpec.getMode(heightMeasureSpec);int wSize = MeasureSpec.getSize(widthMeasureSpec);int hSize = MeasureSpec.getSize(heightMeasureSpec);//因?yàn)槔L制的是圓,所以判斷一下高度或者寬度中的一個(gè)就可以。switch (wMode) {case MeasureSpec.AT_MOST://android:layout_width="warp_content"http://獲取屏幕像素float density = getResources().getDisplayMetrics().density;wSize = (int) (DEFAULT_WIDTH * density);hSize = (int) (DEFAULT_HEIGHT * density);break;//當(dāng)在xml中指定控件的寬高為match_parent或者指定數(shù)值的寬高時(shí),回調(diào)以下代碼case MeasureSpec.EXACTLY://android:layout_width="match_parent" android:layout_width="40dp"wSize = hSize = Math.min(wSize, hSize);break;}//只要重寫onMeasure()方法,一定要調(diào)用以下方法,不然會報(bào)錯(cuò)setMeasuredDimension(wSize, hSize);}}我們先畫一個(gè)外部的圓,也就是都用drawCircle()方法,這里我們調(diào)用getWidth(),getHeight()表示得到布局中設(shè)置的控件的尺寸,因?yàn)槭菆A,所以可以使用getWidth()/2來表示圓心位置。而畫內(nèi)部弧形的時(shí)候,確定其圓心位置,左部點(diǎn)的坐標(biāo)是外面圓的邊框加上弧形與原的間距padding來確定,右側(cè)點(diǎn)的x坐標(biāo)則是getWidth()得到總長度,減去邊框?qū)挾龋贉p去padding得到,上邊距和下邊距同樣,不明白的畫一個(gè)圖立馬明白。大部分不好理解的地方都有注釋,只要認(rèn)真看不會看不明白的~
然后就是在布局中引入我們的自定義View了:
<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.yztc.customprogressview.MainActivity"><com.yztc.customprogressview.ProgressViewandroid:id="@+id/pv"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#0000ff"app:padding="20"app:circleColor="#aa0000"app:sweepColor="#00aa00"app:sweepStep="1"app:startAngle="180"/></RelativeLayout>
需要注意的是我們需要在布局的跟標(biāo)簽下面加上這么一段代碼:xmlns:app="http://schemas.android.com/apk/res-auto"
用來指定我們可以使用自己再attrs中自定義的屬性啦~,使用的形式就是app:你定義的屬性。當(dāng)然,這個(gè)app也不是固定的寫法,只要跟上面你加的那段代碼的xmlns后面的字段一致就可以了~
還有需要注意的是:
默認(rèn)使用Canvas類的drawCircle()方法來畫圓的時(shí)候,圓的半徑是你指定的半徑,再加上一半的邊長,比如你的邊框size=10,radius=50,則實(shí)際空心部分的半徑為55.注意這點(diǎn),否則畫出來的圓的四個(gè)切點(diǎn)位置會與其他位置有些不一樣,類似出現(xiàn)鋸齒的效果。具體請看drawCircle()的Rect邊框問題。

以上所述是小編給大家介紹的Android自定義View實(shí)現(xiàn)簡單的圓形Progress效果,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時(shí)回復(fù)大家的!
新聞熱點(diǎn)
疑難解答
圖片精選