国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Andriod 自定義控件之音頻條

2019-12-12 04:29:48
字體:
來源:轉載
供稿:網友

今天我們實現一個直接繼承于View的全新控件。大家都知道音樂播放器吧,在點擊一首歌進行播放時,通常會有一塊區域用于顯示音頻條,我們今天就來學習下,播放器音頻條的實現。

首先我們還是先定義一個類,直接繼承于View,并重寫它的構造方法,并初始化一個畫筆,這和上一節是同樣的道理。直接貼出代碼:

public class AudioBar extends View{private Paint mTextPaint;public AudioBar(Context context) {this(context,null);}public AudioBar(Context context, AttributeSet attrs) {this(context, attrs,0);}public AudioBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mTextPaint = new TextPaint();mTextPaint.setColor(Color.RED);}}

然后同樣的道理,想要定義我們自己的View控件,我們需要重寫View的onDraw()方法。

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);}

有聽過或是播放音樂的伙伴大都知道音頻條是什么樣子的,無非就是來回跳動的不同豎形圖,在這里我們稍微轉換下思想就知道,在我們android中可以以豎形矩形來實現,各個矩形之間以固定的間距分割開來就能模仿實現我們的目標控件-音頻條。先貼出代碼,稍候看代碼解釋:

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);width = getMeasuredWidth() ;height = getMeasuredHeight();int mRectCount = 0;for(int count = 5 ; count < width ;count += mRectWidth){mRectCount ++ ;}for(int i = 0 ; i < mRectCount ; i ++){double mRandom = Math.random();mRectHeight = (float) (height * mRandom);canvas.drawRect(offset + mRectWidth * i,mRectHeight,mRectWidth * (i+1),height,mTextPaint);}}

好,來看下這段代碼,首先是我們先獲取手機頻幕的尺寸大小,然后我會根據手機頻幕尺寸和預先定義出的矩形寬度(這里使用mRectWidth變量)來計算出當前手機頻幕可以容納多少個矩形(使用mRectCount 來計數)。然后通過循環創建矩形的方式,讓系統給我們畫出我們所定義的視圖。當然這里我還隨機產生了一個隨機數,用于控制矩形的高度。

ok,把它加入到我們的布局文件中,并在Activity中顯示出來看看是什么效果吧:

activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.sanhuimusic.mycustomview.MainActivity"><com.sanhuimusic.mycustomview.view.AudioBarandroid:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout>

然后MainActivity類

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}}

現在運行程序看看效果吧。

是不是很酷呢?但是有伙伴該有疑問了,音頻條不都是動態的嗎?現在我們實現的只是靜態的矩形條呀,別急,我們現在讓它動起來,但是該怎么實現呢?

有經驗的伙伴都知道,我們所使用或定義的UI視圖都是在onDraw()繪制完成之后在Activity中顯示出來的,那么我們要實現動態的視圖是不是可以不停的調用該方法呢?又有什么方法可以不停的調用它使它不停的繪制呢?答案顯而易見,使用invalidate();方法,它可以不停的重新繪制View。因為使用invalidate();間隔太短,速度太快,所以根據我們的需求,我們可以使用延遲的方法重繪View,在這里我們使用postInvalidateDelayed(500);讓它500毫秒重畫一次,這樣就可以體現了動態的音頻條。大家可以試下,動態圖不太會搞,我就不貼圖了,你可以跑下程序了。

ok,現在已基本符合我們的要求了,是不是送了一口氣呢,還沒有,你有沒有試試在layout文件中為我們自定義的控件添加padding屬性呢,試試吧。哈哈,是不是也木有任何改變呢?

那是因為我們在onDraw()方法中沒有考慮到這一情況的發生。在自定義控件中,直接繼承View時,必須要考慮到padding屬性對控件的影響,所以接下來,讓我們的控件貼近原生控件吧。

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int leftPadding = getPaddingLeft();int topPadding = getPaddingTop();int rightPadding = getPaddingRight();int bottomPadding = getPaddingBottom();width = getMeasuredWidth() - leftPadding - rightPadding;height = getMeasuredHeight()- topPadding - bottomPadding;int mRectCount = 0;for(int count = 5 ; count < width ;count += mRectWidth){mRectCount ++ ;}for(int i = 0 ; i < mRectCount ; i ++){double mRandom = Math.random();mRectHeight = (float) (height * mRandom);canvas.drawRect(offset + mRectWidth * i,mRectHeight,mRectWidth * (i+1),height,mTextPaint);}postInvalidateDelayed(500);}

也相當的好理解,根據當前情景對padding屬性進行控制一下就ok了,小伙伴們現在趕緊在運行試試吧。

到這里整個自定義控件已差不多完成,但是細心的伙伴可能會發現:我們制作的音頻條不可能占據整個頻幕呀,嘿嘿,這個比較簡單,我們通常的做法是修改一下布局文件不就行嘍,好,修改如下:

activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.sanhuimusic.mycustomview.MainActivity"><com.sanhuimusic.mycustomview.view.AudioBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>

ok,大功告成,在運行下,試試。

我倒,怎么完全沒有木有變化啊,檢查檢查,還是木有問題,到底是哪個出問題了呢,我想你該蒙了。

這時候你該通過搜索或是書籍查詢了,(10秒鐘以后,哈哈)通過了解你大概明白了問題所在,View的工作流程是在onDraw繪制之前,是需要先測量布局的,這里引入了兩個名詞,測量,和布局。后面我想針對View的工作流程專門做一節學習,所以,我們現在只需要先了解下View測量的工作是在哪進行的。

好,經過查詢資料,我們了解到,View的測量工作是在onMeasure()方法中進行的。接下來讓我們看看它到底是怎么測量的,而我們在當前場景下使用wrap_content為什么沒有效果?帶著問題,我們先重寫View的onMeasure()方法,如下:

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

然后跟到super.onMeasure(widthMeasureSpec, heightMeasureSpec);源碼中,我們所看到的源碼很簡單,如下,

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}

在方法體中只是調用了setMeasuredDimension();方法來決定View尺寸的,再看它里面的參數是通過getDefaultSize()方法獲取大小,再次跟進getDefaultSize()方法中。

public static int getDefaultSize(int size, int measureSpec) {int result = size;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED:result = size;break;case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specSize;break;}return result;}

ok,很簡單的源碼,主要通過MeasureSpec類(后面會詳細講解)獲取測量的模式和測量的大小,然后通過測量的模式來決定測量的大小,但是有一點是不是很奇怪呢,當測量模式為AT_MOST(最大值模式,對應的是layout寬高屬性是wrap_content)時它的測量大小和模式為EXACTLY(精確值模式,對應的是layout寬高屬性是match_parent)的測量大小一樣呢,因為我們恍然大悟,系統默認的測量大小不管是layout寬高屬性是wrap_content還是match_parent它的取值都是match_parent是的默認值。

由此可以明白,我們在修改了layout寬高屬性值時,并沒有達到我們預期的希望。那該怎么解決呢?其實也很簡單,因為,View測量大小的取值取決于setMeasuredDimension()這個方法,因此只要我們重寫了setMeasuredDimension()方法,就可以完成我們的需求。因此,我們可以進行如下操作:

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){setMeasuredDimension(getMeasuredWidth()/2 , getMeasuredHeight()/2);} else if(widthSpecMode == MeasureSpec.AT_MOST){setMeasuredDimension(getMeasuredWidth()/2 ,heightSpecSize);} else if(heightSpecMode == MeasureSpec.AT_MOST){setMeasuredDimension(widthSpecSize ,getMeasuredHeight()/2);}}

代碼解釋:首先我們分別先得到控件測量的模式和大小,然后根據情況分別識別當前View屬性屬于哪種情景,再根據具體的情景進行重寫了setMeasuredDimension()方法。這里我是讓它各顯示屏幕的一半。好,來看看現在是否符合了我們的需求。

好了,完全符合需求,可以開心下了。

總結下:當我們直接繼承View實現自定義控件時,主要困難點就在于坐標系的計算,計算出正確的坐標,自定義的控件也就完成大半了,另外還有需要針對padding屬性和layout_width 和 layout_height屬性值為wrap_content的情況進行必要的考慮。好了,今天就說到這里吧。

以上所述是小編給大家介紹的Andriod 自定義控件之音頻條,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 海丰县| 噶尔县| 昭觉县| 汕头市| 隆回县| 通化县| 江门市| 改则县| 波密县| 木兰县| 金堂县| 海南省| 白玉县| 长兴县| 苏州市| 左云县| 奇台县| 淳化县| 新丰县| 军事| 于都县| 黎城县| 新晃| 高邮市| 修文县| 武威市| 梁山县| 三穗县| 泽普县| 泽州县| 香港| 都昌县| 灵宝市| 八宿县| 子长县| 临汾市| 南丰县| 江门市| 平昌县| 息烽县| 德庆县|