編輯:關於Android編程
今天我們來看看自定義ProgressBar,在這個過程中,我們順便來看看自定義View中兩個非常關鍵的方法,一個是View的測量,還有一個是自定義屬性。OK,廢話不多說,先來看一張效果圖:
OK,動手吧。
寫一個類繼承自View,先來聲明變量,看看我們需要哪些變量:
/** * View默認的寬 */ private static final int DEFAULTWIDTH = 100; /** * View默認的高度 */ private static final int DEFAULTHEIGHT = 100; /** * 外層圓圈的線條寬度 */ private int stoke = 7; /** * 外層圓圈的線條顏色 */ private int circleColor = Color.BLACK; /** * 內外圓圈之間的間距 */ private int padding = 20; /** * 內層實體圓的顏色 */ private int sweepColor = Color.RED; /** * 開始繪制的角度 */ private int startAngle = -90; /** * 已經繪制的角度 */ private int sweepAngle = 0; /** * 每次增長的度數 */ private int sweepStep = 1; /** * 畫筆 */ private Paint paint; /** * 繪制扇形需要的矩形 */ private RectF rectF;
public MyProgressBar(Context context) { this(context, null); } public MyProgressBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); paint = new Paint(); paint.setAntiAlias(true); }
接下來我們來看看View的繪制,大家看上面的效果圖就知道,我們這裡的繪制一共有兩部分,一部分是外部圓環的繪制,還有一部分是內部扇形的繪制,那我們一步一步來:
圓環的繪制很簡單,直接看代碼:
//設置圓環的顏色 paint.setColor(circleColor); //設置圓環的寬度 paint.setStrokeWidth(stoke); //設置繪制模式為描邊 paint.setStyle(Paint.Style.STROKE); canvas.drawCircle(getWidth() / 2, getHeight() / 2, (float) (getWidth() / 2 - Math.ceil(stoke / 2.0)), paint);
扇形的繪制需要我們先構造一個RectF類(當然這個並不是必須的操作),然後就可以開始繪制了,如下:
//設置扇形的顏色 paint.setColor(sweepColor); //設置扇形的繪制風格 paint.setStyle(Paint.Style.FILL); //構造一個RectF 出來,扇形繪制在該RectF中 rectF = new RectF(padding, padding, getWidth() - padding, getHeight() - padding); //繪制扇形 //四個參數分別是扇形所在的矩形,開始繪制的角度,需要繪制的角度,扇形是否和矩形共用一個中心點,畫筆 canvas.drawArc(rectF, startAngle, sweepAngle, true, paint); //增加要繪制的角度 sweepAngle += sweepStep; //如果要繪制的角度大於360度,就從0重新開始繪制 sweepAngle = sweepAngle > 360 ? 0 : sweepAngle; invalidate();
OK,做好上面這幾步之後,我的一個自定義ProgressBar基本上就顯示出來了。這個時候我只需要在布局文件中添加上這個自定義控件即可,如下:
當我們自定義一個View的時候,除了重寫onDraw方法之外,還有一個方法有時候也需要我們重寫,那就是onMeasure,onMeasure方法接收兩個參數,分別是widthMeasureSpec和heightMeasureSpec,這兩個參數我們稱作測量規格,它們是一個32位的整型數據,這個數據中高2位表示View的測量模式,低30位表示View的測量值,測量模式分為3種,分別是:
1.EXACTLY:精確模式,對應我們在布局文件中設置寬高時給一個具體值或者match_parent
2.AT_MOST:最大值模式:對應設置寬高時給一個wrap_content
3.UNSPECIFIED:這種測量模式多用在ScrollView中
OK,了解了這些之後,接下來我們就來看看怎麼樣從widthMeasureSpec和heightMeasureSpec中提取出來寬高對應的測量模式與測量值。在MeasureSpec類中提供了兩個靜態方法,分別是getMode和getSize,只要我們將寬高的測量規格傳遞進去就可以獲取它的測量模式和測量值。如下:
//獲取寬的測量模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); //獲取寬的測量值 int widthSize = MeasureSpec.getSize(widthMeasureSpec); //獲取高的測量模式 int heightMode = MeasureSpec.getMode(heightMeasureSpec); //獲取高的測量值 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
switch (widthMode) { case MeasureSpec.EXACTLY: break; case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: //如果寬為wrap_content,則給定一個默認值 widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULTWIDTH, getResources().getDisplayMetrics()); break; } switch (heightMode) { case MeasureSpec.EXACTLY: break; case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULTHEIGHT, getResources().getDisplayMetrics()); break; }
widthSize = heightSize = Math.min(widthSize, heightSize);OK,至此,我的View的寬高都確定下來了,最後我只需要調用setMeasuredDimension方法,告訴系統我的測量結果即可,如下:
setMeasuredDimension(widthSize, heightSize);
@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); switch (widthMode) { case MeasureSpec.EXACTLY: break; case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: //如果寬為wrap_content,則給定一個默認值 widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULTWIDTH, getResources().getDisplayMetrics()); break; } switch (heightMode) { case MeasureSpec.EXACTLY: break; case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULTHEIGHT, getResources().getDisplayMetrics()); break; } widthSize = heightSize = Math.min(widthSize, heightSize); //設置測量結果 setMeasuredDimension(widthSize, heightSize); }
做完上面這幾步,我的自定義ProgressBar已經完成的差不多了,現在我如果想要修改圓環的顏色,圓環的線條的寬度,扇形的顏色等等這些屬性的話只能在代碼中修改,可是如果我想要在布局文件中來配置這些顏色,然後在代碼中讀取這些顏色再設置給paint又該怎麼辦呢?這裡就涉及到我們的自定義屬性了。OK,那麼接下來我們就來看看自定義屬性。
自定義屬性需要我們首先在res/values文件夾中添加attrs文件(該文件名可以任意取,約定俗成取attrs),在attrs文件中來生命你要設置的屬性,如下:
1. boolean 屬性取值為boolean類型
2. string 屬性取值為文本類型
3. color 屬性取值為顏色類型
4. dimension 屬性值為尺寸
5. enum 屬性取值是枚舉類型,例如:LinearLayout中的android:orientation="horizontal"屬性
6. flag 屬性取值進行或運算,比如android:layout_gravity="left|bottom"
7. fraction 屬性取值為小數
8. float 屬性取值為浮點數
9. integer 屬性取值為整數
10. reference 屬性取值可以引用一個值
OK,這一部分的工作完成之後,接下來我們就可以在布局文件中設置屬性了,如下:
OK,但是光這樣肯定不行,我只是在布局文件中設置了,代碼裡又該怎麼樣來獲取布局文件中設置的值呢?修改構造方法如下:
public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); paint = new Paint(); paint.setAntiAlias(true); //讀取布局文件中設置的屬性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyProgressBar); //讀取布局文件中定義的顏色值,第二個參數為默認值(如果布局文件中未設置該屬性時使用) circleColor = ta.getColor(R.styleable.MyProgressBar_circleColor, this.circleColor); sweepColor = ta.getColor(R.styleable.MyProgressBar_sweepColor, this.sweepColor); startAngle = ta.getInt(R.styleable.MyProgressBar_startAngle, this.startAngle); sweepStep = ta.getInt(R.styleable.MyProgressBar_sweepStep, this.sweepStep); stroke = (int) ta.getDimension(R.styleable.MyProgressBar_stroke, stroke); padding = (int) ta.getDimension(R.styleable.MyProgressBar_mypb_padding, padding); //回收ta ta.recycle(); }當系統調用構造方法的時候,我們將布局文件中設置的屬性一個個讀取出來,如果用戶設置了該值,那麼直接讀取出來使用,如果用戶沒有設置該值,那麼我們也給了一個默認的值(默認值就是我們一開始預定義的值),OK,我們再來看看顯示效果:
OK,就是這麼簡單。
說到android studio的調試,很多人可能會說,這有什麼可講的不就是一個斷點調試麼,剛開始我也是這麼認為的,直到我了解之後,才發現,調試原來可以玩的這麼牛。下面我
最近在android上用opencv搞人臉識別的 現在簡單展示下代碼。環境搭建我前面有寫,不會的自己看可以。 這個事用java api直接調用的更簡單了就,搭建都不需要直
來自:https://developer.android.com/training/basics/intents/index.html 前言:我們都知道在APP
首先,感謝公司能給我閒暇的時間,來穩固我的技術,讓我不斷的去探索研究,在此不勝感激。 先不說實現功能,上圖看看效果 這個是續上一次水平變色進度條的有一個全新的控件,理論實