Android自定義控件之自定義屬性(二)
前言:
上篇介紹了自定義控件的基本要求以及繪制的基本原理,本篇文章主要介紹如何給自定義控件自定義一些屬性。本篇文章將繼續(xù)以上篇文章自定義圓形百分比為例進(jìn)行講解。有關(guān)原理知識請參考Android自定義控件基本原理詳解(一)這篇文章。
需求產(chǎn)生背景:
為何要引入自定義屬性?當(dāng)Android提供的原生屬性不能滿足實際的需求的時候,比如我們需要自定義圓形百分比半徑大小、圓形背景、圓形顯示的位置、圓形進(jìn)度的背景等等。這個時候就需要我們自定義屬性了。
自定義屬性步驟:
1.)在res/values文件下添加一個attrs.xml文件,如果項目比較大的話,會導(dǎo)致attrs.xml代碼相當(dāng)龐大,這時可以根據(jù)相應(yīng)的功能模塊起名字,方便查找,例如:登錄模塊相關(guān)attrs_login.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="RoundImageView"> <attr name="borderRadius" /> <attr name="type" /> </declare-styleable> </resources>
2.)如何聲明一組屬性
使用<declare-styleable name="PercentView"></declare-styleable>來定義一個屬性集合,name就是屬性集合的名字,這個名字一定要起的見名知意。
<declare-styleable name="PercentView"> <!--添加屬性--> </declare-styleable>
然后就是定義屬性值了,通過<attr name="textColor" format="color" /> 方式定義屬性值,屬性名字同樣也要起的見名知意,format表示這個屬性的值的類型,類型有以下幾種:
•reference:引用資源
•string:字符串
•Color:顏色
•boolean:布爾值
•dimension:尺寸值
•float:浮點型
•integer:整型
•fraction:百分?jǐn)?shù)
•enum:枚舉類型
•flag:位或運算
基于上面的要求,我們可以定義一下百分比控件屬性
<declare-styleable name="PercentView"> <attr name="percent_circle_gravity"><!--圓形繪制的位置--> <flag name="left" value="0" /> <flag name="top" value="1" /> <flag name="center" value="2" /> <flag name="right" value="3" /> <flag name="bottom" value="4" /> </attr> <attr name="percent_circle_radius" format="dimension" /><!--圓形半徑--> <attr name="percent_circle_progress" format="integer" /><!--當(dāng)前進(jìn)度值--> <attr name="percent_progress_color" format="color" /><!--進(jìn)度顯示顏色--> <attr name="percent_background_color" format="color" /><!--圓形背景色--> </declare-styleable>
3.)布局中如何使用
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:lee="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.whoislcj.views.PercentView android:layout_width="200dp" android:layout_height="200dp" android:layout_margin="10dp" android:background="@color/red" android:padding="10dp" lee:percent_background_color="@color/gray" lee:percent_circle_gravity="left" lee:percent_circle_progress="30" lee:percent_circle_radius="50dp" lee:percent_progress_color="@color/blue" /> </LinearLayout>
為屬性集設(shè)置一個屬性集名稱,我這里用的lee,我這是因為實在想不起使用什么屬性集名稱了,建議在真正的項目中使用項目的縮寫,比如微信可能就是使用wx。
4.)自定義控件中如何獲取自定義屬性
每一個屬性集合編譯之后都會對應(yīng)一個styleable對象,通過styleable對象獲取TypedArray typedArray,然后通過鍵值對獲取屬性值,這點有點類似SharedPreference的取法。
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PercentView); if (typedArray != null) { backgroundColor = typedArray.getColor(R.styleable.PercentView_percent_background_color, Color.GRAY); progressColor = typedArray.getColor(R.styleable.PercentView_percent_progress_color, Color.BLUE); radius = typedArray.getDimension(R.styleable.PercentView_percent_circle_radius, 0); progress = typedArray.getInt(R.styleable.PercentView_percent_circle_progress, 0); gravity = typedArray.getInt(R.styleable.PercentView_percent_circle_gravity, CENTER); typedArray.recycle(); }
5.)完整示例
public class PercentView extends View { private final static String TAG = PercentView.class.getSimpleName(); private Paint mPaint; private int backgroundColor = Color.GRAY; private int progressColor = Color.BLUE; private float radius; private int progress; private float centerX = 0; private float centerY = 0; public static final int LEFT = 0; public static final int TOP = 1; public static final int CENTER = 2; public static final int RIGHT = 3; public static final int BOTTOM = 4; private int gravity = CENTER; private RectF rectF; //用于定義的圓弧的形狀和大小的界限 public PercentView(Context context) { super(context); init(); } public PercentView(Context context, AttributeSet attrs) { super(context, attrs); initParams(context, attrs); init(); } public PercentView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initParams(context, attrs); init(); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); rectF = new RectF(); } private void initParams(Context context, AttributeSet attrs) { mPaint = new Paint(); mPaint.setAntiAlias(true); rectF = new RectF(); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PercentView); if (typedArray != null) { backgroundColor = typedArray.getColor(R.styleable.PercentView_percent_background_color, Color.GRAY); progressColor = typedArray.getColor(R.styleable.PercentView_percent_progress_color, Color.BLUE); radius = typedArray.getDimension(R.styleable.PercentView_percent_circle_radius, 0); progress = typedArray.getInt(R.styleable.PercentView_percent_circle_progress, 0); gravity = typedArray.getInt(R.styleable.PercentView_percent_circle_gravity, CENTER); typedArray.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); Log.e(TAG, "onMeasure--widthMode-->" + widthMode); switch (widthMode) { case MeasureSpec.EXACTLY:// break; case MeasureSpec.AT_MOST: break; case MeasureSpec.UNSPECIFIED: break; } Log.e(TAG, "onMeasure--widthSize-->" + widthSize); Log.e(TAG, "onMeasure--heightMode-->" + heightMode); Log.e(TAG, "onMeasure--heightSize-->" + heightSize); int with = getWidth(); int height = getHeight(); Log.e(TAG, "onDraw---->" + with + "*" + height); centerX = with / 2; centerY = with / 2; switch (gravity) { case LEFT: centerX = radius + getPaddingLeft(); break; case TOP: centerY = radius + getPaddingTop(); break; case CENTER: break; case RIGHT: centerX = with - radius - getPaddingRight(); break; case BOTTOM: centerY = height - radius - getPaddingBottom(); break; } float left = centerX - radius; float top = centerY - radius; float right = centerX + radius; float bottom = centerY + radius; rectF.set(left, top, right, bottom); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Log.e(TAG, "onLayout"); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(backgroundColor); // FILL填充, STROKE描邊,FILL_AND_STROKE填充和描邊 mPaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawCircle(centerX, centerY, radius, mPaint); mPaint.setColor(progressColor); double percent = progress * 1.0 / 100; int angle = (int) (percent * 360); canvas.drawArc(rectF, 270, angle, true, mPaint); //根據(jù)進(jìn)度畫圓弧 } }
運行結(jié)果:
根據(jù)不同的配置顯示的兩種效果
小結(jié):
通過自定義屬性可以達(dá)到自定義的控件也能像原生的控件一樣實現(xiàn)可配置。但是在實際的項目開發(fā)中,像本文介紹的這種自定義控件使用頻率并不是最高的,使用頻率較高的是通過自定義一個組合控件的方式,來達(dá)到布局文件的復(fù)用,以減少項目維護(hù)成本以及開發(fā)成本,下篇文章將重點介紹如何自定義控件組合,點擊查看。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于Android多渠道打包的進(jìn)階知識
前一篇文章主要介紹了關(guān)于Android程序的多渠道打包方法,這一篇文章介紹了多渠道打包的進(jìn)階知識,還不會的同學(xué)快進(jìn)來學(xué)習(xí)下吧,建議收藏以防迷路2021-08-08Android 8.0系統(tǒng)中應(yīng)用圖標(biāo)的適配技巧
今天小編就為大家分享一篇關(guān)于Android 8.0系統(tǒng)中應(yīng)用圖標(biāo)的適配技巧,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10詳解Android ViewPager2中的緩存和復(fù)用機制
最近接觸到豎向整頁滑動的需求,發(fā)現(xiàn)了viewpager2,viewpager2支持fragment,保留了viewpager的特性,下面這篇文章主要給大家介紹了關(guān)于ViewPager2中的緩存和復(fù)用機制的相關(guān)資料,需要的朋友可以參考下2021-11-11Android中RecyclerView實現(xiàn)多級折疊列表效果(二)
這篇文章主要給大家介紹了Android中RecyclerView實現(xiàn)多級折疊列表的相關(guān)資料,文中介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-05-05Kotlin圖文并茂講解續(xù)體與續(xù)體攔截器和調(diào)度器
這篇文章主要介紹了Kotlin開發(fā)中續(xù)體與續(xù)體攔截器和調(diào)度器的相關(guān)使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08