<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                #### **五、自定義View** 在自定義View 時,我們通常會去重寫onDraw()方法來繪制View的顯示內容。如果該View還需要使用wrap_content屬性,那么還必須重寫onMeasure()方法。另外,通過自定義attrs屬性,還可以設置新的屬性配置值。 自定義View時有一些比較重要的回調方法如下: ~~~ onFinishInflate();//從xml加載組件后回調 onSizeChanged();//組件大小改變時回調 onMeasure();//回調該方法進行測量 onLayout();//回調該方法來確定顯示的位置 onTouchEvent();//監聽到觸摸事件回調 ~~~ >[info] 注意:當然,創建自定義View的時候,并不需要重寫所有的方氈,只需要重寫特定條件的回調方法即可。這也是Android控件架構靈活性的體現。 **自定義View通常有三種情況**: **1、對現有控件進行拓展**: 這是一個非常重要的自定義View方法,它可以在原生控件的基礎上進行拓展,增加新的功能、修改顯示的UI等。一般來說,會在**onDraw**()方法中對原生控件行為進行拓展 ~~~ @Override protected void onDraw(Canvas canvas) { //在回調父類方法前,實現自己的邏輯,對TextView來說即是在繪制文本內容前 super.onDraw(canvas); //在回調父類方法后,實現自己的邏輯,對TextView來說即是在繪制文本內容后 } ~~~ **程序調用super.onDraw(canvas)方法來實現原生控件的功能,但是在調用super.onDraw(canvas)方法之前和之后,我們都可以實現自己的邏輯,分別在系統繪制文字前后,完成自己的操作。** **示例1:自定義修改TextView** :-: ![](https://box.kancloud.cn/6dfe2f9adc65d2402f20bafc5efb6a17_360x605.jpg) 圖7 自定義修改TextView 代碼如下所示 **[MyTextView](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/SystemWidget/app/src/main/java/com/imooc/systemwidget/MyTextView.java)** ~~~ public class MyTextView extends TextView { private Paint mPaint1, mPaint2; public MyTextView(Context context) { super(context); initView(); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } private void initView() { mPaint1 = new Paint(); mPaint1.setColor(getResources().getColor( android.R.color.holo_blue_light)); mPaint1.setStyle(Paint.Style.FILL); mPaint2 = new Paint(); mPaint2.setColor(Color.YELLOW); mPaint2.setStyle(Paint.Style.FILL); } @Override protected void onDraw(Canvas canvas) { // 繪制外層矩形 canvas.drawRect( 0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint1); // 繪制內層矩形 canvas.drawRect( 10, 10, getMeasuredWidth() - 10, getMeasuredHeight() - 10, mPaint2); canvas.save(); // 繪制文字前平移10像素 canvas.translate(10, 0); // 父類完成的方法,即繪制文本 super.onDraw(canvas); canvas.restore(); } } ~~~ 而代碼中最重要的部分則是.在onDraw()方法中,為了改變原生的繪制行為,在系統調用super.onDraw(canvas)方法前,也就是在繪制文字之下,繪制兩個不同大小的矩形,形成一個重疊效果,再讓系統調用super.onDraw(canvas)方法,執行繪制文字的工作。這樣,我們就通過改變控件繪制行為,創建了一個新的控件。 **示例2:閃動的文字效果** :-: ![](https://box.kancloud.cn/18561af176d6aae8eba8515a1996dc8d_360x233.gif) 圖8:閃動的文字 要想實現這樣一個效果,可以充分利用Android中Paint 對象的Shader渲染器。通過設置一個不斷變化的LinearGradient(Shader的子類),并使用帶有該屬性的Paint對象來繪制耍顯示的文字。 代碼如下所示 **[ShineTextView](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/SystemWidget/app/src/main/java/com/imooc/systemwidget/ShineTextView.java)** ~~~ public class ShineTextView extends TextView { private LinearGradient mLinearGradient; private Matrix mGradientMatrix; private Paint mPaint; private int mViewWidth = 0; private int mTranslate = 0; public ShineTextView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mViewWidth == 0) { mViewWidth = getMeasuredWidth(); if (mViewWidth > 0) { mPaint = getPaint();//TextView自身的方法,返回TextPaint,text文本的畫筆 mLinearGradient = new LinearGradient( 0, 0, mViewWidth, 0, new int[]{ Color.BLUE, 0xffffffff, Color.BLUE}, null, Shader.TileMode.CLAMP); mPaint.setShader(mLinearGradient); mGradientMatrix = new Matrix(); } } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mGradientMatrix != null) { mTranslate += mViewWidth / 5; if (mTranslate > 2 * mViewWidth) { mTranslate = -mViewWidth; } mGradientMatrix.setTranslate(mTranslate, 0); mLinearGradient.setLocalMatrix(mGradientMatrix); postInvalidateDelayed(100); } } } ~~~ 首先,在onSizeChanged()方法中進行一些對象的初始化工作,并根據View的寬帶設置一個LinearGradient漸變渲染器,其中最關鍵的就是使用getPaint()方法獲取當前繪制TextView的Paint對象,并給這個Paint對象設置原生TextView沒有的LinearGradient屬性。最后,在onDraw()方法中,通過矩陣的方式來不斷平移漸變效果,從而在繪制文字時,產生動態的閃動效果。 **2、通過組合來實現新的控件:** 這種方式通常需要繼承一個合適的ViewGroup,再給它添加指定功能的控件,從而組合成新的復合控件。通過這種方式創建的控件,我們一般會給它指定一些可配置的屬性,讓它具有更強的拓展性。下面就以一個TopBar為示例,講解如何創建復合控件。 效果如圖所示 :-: ![](https://box.kancloud.cn/248fc47c8752d2146db69abe2e17e8d2_374x554.jpg) 圖9 Topbar 代碼如下所示 **[TopBar](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/SystemWidget/app/src/main/java/com/imooc/systemwidget/TopBar.java)** ~~~ public class TopBar extends RelativeLayout { // 包含topbar上的元素:左按鈕、右按鈕、標題 private Button mLeftButton, mRightButton; private TextView mTitleView; // 布局屬性,用來控制組件元素在ViewGroup中的位置 private LayoutParams mLeftParams, mTitlepParams, mRightParams; // 左按鈕的屬性值,即我們在atts.xml文件中定義的屬性 private int mLeftTextColor; private Drawable mLeftBackground; private String mLeftText; // 右按鈕的屬性值,即我們在atts.xml文件中定義的屬性 private int mRightTextColor; private Drawable mRightBackground; private String mRightText; // 標題的屬性值,即我們在atts.xml文件中定義的屬性 private float mTitleTextSize; private int mTitleTextColor; private String mTitle; // 映射傳入的接口對象 private topbarClickListener mListener; public TopBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public TopBar(Context context) { super(context); } public TopBar(Context context, AttributeSet attrs) { super(context, attrs); // 設置topbar的背景 setBackgroundColor(0xFFF59563); // 通過這個方法,將你在atts.xml中定義的declare-styleable // 的所有屬性的值存儲到TypedArray中 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar); // 從TypedArray中取出對應的值來為要設置的屬性賦值 mLeftTextColor = ta.getColor( R.styleable.TopBar_leftTextColor, 0); mLeftBackground = ta.getDrawable( R.styleable.TopBar_leftBackground); mLeftText = ta.getString(R.styleable.TopBar_leftText); mRightTextColor = ta.getColor( R.styleable.TopBar_rightTextColor, 0); mRightBackground = ta.getDrawable( R.styleable.TopBar_rightBackground); mRightText = ta.getString(R.styleable.TopBar_rightText); mTitleTextSize = ta.getDimension( R.styleable.TopBar_titleTextSize, 10); mTitleTextColor = ta.getColor( R.styleable.TopBar_titleTextColor, 0); mTitle = ta.getString(R.styleable.TopBar_title); // 獲取完TypedArray的值后,一般要調用 // recyle方法來避免重新創建的時候的錯誤 ta.recycle(); mLeftButton = new Button(context); mRightButton = new Button(context); mTitleView = new TextView(context); // 為創建的組件元素賦值 // 值就來源于我們在引用的xml文件中給對應屬性的賦值 mLeftButton.setTextColor(mLeftTextColor); mLeftButton.setBackground(mLeftBackground); mLeftButton.setText(mLeftText); mRightButton.setTextColor(mRightTextColor); mRightButton.setBackground(mRightBackground); mRightButton.setText(mRightText); mTitleView.setText(mTitle); mTitleView.setTextColor(mTitleTextColor); mTitleView.setTextSize(mTitleTextSize); mTitleView.setGravity(Gravity.CENTER); // 為組件元素設置相應的布局元素 mLeftParams = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE); // 添加到ViewGroup addView(mLeftButton, mLeftParams); mRightParams = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE); addView(mRightButton, mRightParams); mTitlepParams = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); mTitlepParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE); addView(mTitleView, mTitlepParams); // 按鈕的點擊事件,不需要具體的實現, // 只需調用接口的方法,回調的時候,會有具體的實現 mRightButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mListener.rightClick(); } }); mLeftButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mListener.leftClick(); } }); } // 暴露一個方法給調用者來注冊接口回調 // 通過接口來獲得回調者對接口方法的實現 public void setOnTopbarClickListener(topbarClickListener mListener) { this.mListener = mListener; } /** * 設置按鈕的顯示與否 通過id區分按鈕,flag區分是否顯示 * * @param id id * @param flag 是否顯示 */ public void setButtonVisable(int id, boolean flag) { if (flag) { if (id == 0) { mLeftButton.setVisibility(View.VISIBLE); } else { mRightButton.setVisibility(View.VISIBLE); } } else { if (id == 0) { mLeftButton.setVisibility(View.GONE); } else { mRightButton.setVisibility(View.GONE); } } } // 接口對象,實現回調機制,在回調方法中 // 通過映射的接口對象調用接口中的方法 // 而不用去考慮如何實現,具體的實現由調用者去創建 public interface topbarClickListener { // 左按鈕點擊事件 void leftClick(); // 右按鈕點擊事件 void rightClick(); } } ~~~ 通常情況下,需要添加標題欄的界面都會抽象出來一個這樣的TopBar,同時給TopBar增加相應的接口,可以更加靈活地控制TopBar,所以需要創建這樣一個UI模板,首先,摸板應該具有通用性與可定制性。也就是說,我們需要給調用者以豐富的接口,讓他們可以更改模板中的文字、顏色、行為等信息,而不是所有的模板都一樣,那樣就失去模扳的意義。 **(1)、定義屬性** 為一個View提供可自定義的屬性非常簡單,只需要在res資源目錄的values日錄下創建一個attrs.xml的屬性定義文件,并在該文件中通過如下代碼定義相應的屬性即可。 ~~~ <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="TopBar"> <attr name="title" format="string" /> <attr name="titleTextSize" format="dimension" /> <attr name="titleTextColor" format="color" /> <attr name="leftTextColor" format="color" /> <attr name="leftBackground" format="reference|color" /> <attr name="leftText" format="string" /> <attr name="rightTextColor" format="color" /> <attr name="rightBackground" format="reference|color" /> <attr name="rightText" format="string" /> </declare-styleable> </resources> ~~~ 通過<declare-styleable>標簽聲明了使用自定義屬性,并通過name屬性來確定引用的名稱,最后通過`<attr>`來聲明具體的自定義屬性。比如此處定義了標題文字的字體、大小、顏色,左邊按鈕的文字顏色、背景、字體,右邊按鈕的文字顏色、背景、字體等屬性,并通過format屬性來指定屬性的類型。要注意的是,有些屬性可以是顏色屬性,也可以是引用屬性。比如按鈕的背景,可以把它指定為具體的顏色,也可以把它指定為一張圖片,所以使用“|”來分隔不同的屬——“reference|color”。 在構造方位中,通過如下代碼來獲取在XML布局文件中自定義的那些屬性,即與我們使用系統提供的那些屬性一樣。 ~~~ TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar); ~~~ TypedArray數據結構相當于Map,是鍵值對的映射,通過TypedArray的getColor()、getString()等方法來獲取這些定義的屬性值。最后記得調用ta.recycle();方法來進行資源的回收,否則會對下次的使用造成影響。 **(2)、組合控件** UI模板TopBar實際上由三個控件組成,即左邊的點擊按鈕mLeftButton,右邊的點擊按鈕mRightButton和中間的標題欄mTitleView。通過動態添加控件的方式,使用addView()方法將這3個控件加入到定義的TopBar模板中,并給它們設置前面所獲取到的具體的屬性值,比如標題的文字顏色、大小等。如上面的構造方法中的代碼所述。 那么如何來給這兩個左、右按鈕設計點擊事件呢?既然是UI模板,那么每個調用者所需要這些按鈕能夠實現的功能都是不一樣的。因此,不能直接在UI模板中添加具體的實現邏輯,只能通過接口回調的思想,將具體的實現邏輯交給調用者,實現過程如下所示。 **①、定義接口** 在UI模版類中定義一個左右按鈕點擊的接口,并創建兩個方法,分別用于左邊按鈕的點擊和右邊按鈕的點擊,代碼如下所示。 ~~~ // 接口對象,實現回調機制,在回調方法中 // 通過映射的接口對象調用接口中的方法 // 而不用去考慮如何實現,具體的實現由調用者去創建 public interface topbarClickListener { // 左按鈕點擊事件 void leftClick(); // 右按鈕點擊事件 void rightClick(); } ~~~ **②、暴露接口給調用者** 在模板方法中,為左、右按鈕增加點擊事件,但不去實現具體的邏輯,而是調用接口中相應的點擊方法,代碼如下所示。 ~~~ // 按鈕的點擊事件,不需要具體的實現, // 只需調用接口的方法,回調的時候,會有具體的實現 mRightButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mListener.rightClick(); } }); mLeftButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mListener.leftClick(); } }); } // 暴露一個方法給調用者來注冊接口回調 // 通過接口來獲得回調者對接口方法的實現 public void setOnTopbarClickListener(topbarClickListener mListener) { this.mListener = mListener; } ~~~ **③、實現接口回惆** 在調用者的代碼巾,調用者需要實現這樣一個接口, 并完成接口中的方法,確定具體的實現邏輯,井使用第二步中暴露的方法,將接口的對象傳遞進去,從而完成回調。通常悄況下,可以使用匿名內部類的形式來實現接口中的方法,代碼如下所示。 **[TopBarTest](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/SystemWidget/app/src/main/java/com/imooc/systemwidget/TopBarTest.java)** ~~~ public class TopBarTest extends Activity { private TopBar mTopbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.topbar_test); // 獲得我們創建的topbar mTopbar = (TopBar) findViewById(R.id.topBar); // 為topbar注冊監聽事件,傳入定義的接口 // 并以匿名類的方式實現接口內的方法 mTopbar.setOnTopbarClickListener( new TopBar.topbarClickListener() { @Override public void rightClick() { Toast.makeText(TopBarTest.this, "right", Toast.LENGTH_SHORT) .show(); } @Override public void leftClick() { Toast.makeText(TopBarTest.this, "left", Toast.LENGTH_SHORT) .show(); } }); // 控制topbar上組件的狀態 mTopbar.setButtonVisable(0, true); mTopbar.setButtonVisable(1, false); } } ~~~ 除了通過接口回調的方式來實現動態的控制UI模板,同樣可以使用公共方法來動態地修改UI模板中的UI,這樣就進一步提高模板的可定制性,代碼如下所示。 ~~~ /** * 設置按鈕的顯示與否 通過id區分按鈕,flag區分是否顯示 * * @param id id * @param flag 是否顯示 */ public void setButtonVisable(int id, boolean flag) { if (flag) { if (id == 0) { mLeftButton.setVisibility(View.VISIBLE); } else { mRightButton.setVisibility(View.VISIBLE); } } else { if (id == 0) { mLeftButton.setVisibility(View.GONE); } else { mRightButton.setVisibility(View.GONE); } } } ~~~ 通過如上所示代間,當調用者通過TopBar 對象調用這個方法后,根據參數,調用者就可以動態地控制按鈕的顯示。 **(3)、引用UI模板** ~~~ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="5dp" tools:context=".MainActivity"> <!-- <include layout="@layout/topbar" /> --> <com.imooc.systemwidget.TopBar android:id="@+id/topBar" android:layout_width="match_parent" android:layout_height="40dp" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" custom:leftBackground="@drawable/blue_button" custom:leftText="Back" custom:leftTextColor="#FFFFFF" custom:rightBackground="@drawable/blue_button" custom:rightText="More" custom:rightTextColor="#FFFFFF" custom:title="自定義標題" custom:titleTextColor="#123412" custom:titleTextSize="10sp" /> </RelativeLayout> ~~~ 當然也可以將這個UI模板寫到一個布局文件中,如 **topbar.xml** ~~~ <com.xys.mytopbar.Topbar xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res-auto" android:id="@+id/topBar" android:layout_width="match_parent" android:layout_height="40dp" custom:leftBackground="@drawable/blue_button" custom:leftText="Back" custom:leftTextColor="#FFFFFF" custom:rightBackground="@drawable/blue_button" custom:rightText="More" custom:rightTextColor="#FFFFFF" custom:title="自定義標題" custom:titleTextColor="#123412" custom:titleTextSize="15sp"> </com.xys.mytopbar.Topbar> ~~~ 在其他布局文件中直接通過include標簽來引用這個UI模板,如下所示 ~~~ <include layout="@layout/topbar" /> ~~~ **3、重寫View來實現全新的控件:** 當Android系統原生的控件無法滿足我們的需求時,就需要創建一個全新的自定義View了。通常需要繼承View類,并重寫它的onDraw()、onMeasure()等方法實現繪制邏輯,同時通過重寫onTouchEvent()等觸控事件來實現交互邏輯,還可以引入自定義屬性,豐富自定義View的可定制性。 **(1)弧線展示圖** :-: ![](https://box.kancloud.cn/45f3c853f60470b3b7775aec020def97_365x603.jpg) 圖10 弧線展示圖 很明顯,這個自定義view分為3個部分,分別是中間的圓形,中間顯示的文字和外圈的弧線,既然有了思路,只要在onDraw()方法中一個個去繪制就可以了。 >[info] 注意:這里簡單處理,把view的繪制長度直接設置為屏幕的寬度 代碼如下所示 **[CircleProgressView](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/SystemWidget/app/src/main/java/com/imooc/systemwidget/CircleProgressView.java)** ~~~ public class CircleProgressView extends View { private int mMeasureHeigth; private int mMeasureWidth; private Paint mCirclePaint; private float mCircleXY;//圓心坐標 private float mRadius;//半徑 private Paint mArcPaint; private RectF mArcRectF;//橢圓形的邊界 private float mSweepAngle;// private float mSweepValue = 66; private Paint mTextPaint; private String mShowText; private float mShowTextSize; public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public CircleProgressView(Context context, AttributeSet attrs) { super(context, attrs); } public CircleProgressView(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mMeasureWidth = MeasureSpec.getSize(widthMeasureSpec); mMeasureHeigth = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(mMeasureWidth, mMeasureHeigth); initView(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 繪制圓 canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint); // 繪制弧線 canvas.drawArc(mArcRectF,270, mSweepAngle, false, mArcPaint); // 繪制文字 canvas.drawText(mShowText, 0, mShowText.length(), mCircleXY, mCircleXY + (mShowTextSize / 4), mTextPaint); } private void initView() { float length = 0; if (mMeasureHeigth >= mMeasureWidth) { length = mMeasureWidth; } else { length = mMeasureHeigth; } //初始化圓形的參數 mCircleXY = length / 2; mRadius = (float) (length * 0.5 / 2); mCirclePaint = new Paint(); mCirclePaint.setAntiAlias(true);//抗鋸齒 mCirclePaint.setColor(getResources().getColor(android.R.color.holo_blue_bright)); //用來定義弧線的形狀和大小 mArcRectF = new RectF( (float) (length * 0.1), (float) (length * 0.1), (float) (length * 0.9), (float) (length * 0.9)); mSweepAngle = (mSweepValue / 100f) * 360f; mArcPaint = new Paint(); mArcPaint.setAntiAlias(true); mArcPaint.setColor(getResources().getColor(android.R.color.holo_blue_bright)); mArcPaint.setStrokeWidth((float) (length * 0.1)); mArcPaint.setStyle(Style.STROKE);//空心,不設置style。默認是實心 mShowText = setShowText(); mShowTextSize = setShowTextSize(); mTextPaint = new Paint(); mTextPaint.setTextSize(mShowTextSize); mTextPaint.setTextAlign(Paint.Align.CENTER); } private float setShowTextSize() { this.invalidate(); return 50; } private String setShowText() { this.invalidate(); return "Android Skill"; } public void forceInvalidate() { this.invalidate(); } public void setSweepValue(float sweepValue) { if (sweepValue != 0) { mSweepValue = sweepValue; } else { mSweepValue = 25; } this.invalidate(); } } ~~~ 首先,在初始化時,設置好繪制3個圖形的參數,圓的代碼如下 ~~~ mCircleXY = length / 2; mRadius = (float) (length * 0.5 / 2); ~~~ 繪制弧線,需要指定其橢圓的外接矩形,參數如下所示 ~~~ //用來定義弧線的形狀和大小 mArcRectF = new RectF( (float) (length * 0.1), (float) (length * 0.1), (float) (length * 0.9), (float) (length * 0.9)); ~~~ 繪制文字,只需要設置好文字的起始繪制位置即可 接下來,在onDraw()方法中進行繪制就行了 ~~~ // 繪制圓 canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint); // 繪制弧線 canvas.drawArc(mArcRectF,270, mSweepAngle, false, mArcPaint); // 繪制文字 canvas.drawText(mShowText, 0, mShowText.length(), mCircleXY, mCircleXY + (mShowTextSize / 4), mTextPaint); ~~~ >[info] 總結:相信這些圖形如果單獨讓你去繪制,是非常容易的事情,只是這里進行一下組合,就創建了一個新的View。其實,不論是多么復雜的圖形、控件,它都是由這些最基本的圖形繪制出來的,關鍵就在于如何去分解、設計這些圖形,當你的腦海中有了一幅設計圖之后,剩下的事情就只是對坐標的計算。 **(2)音頻條形圖** 效果如下所示 :-: ![](https://box.kancloud.cn/96f453d572b64d6b426c2c5dbd42ccc2_361x597.gif) 圖11 音頻條形圖 源碼如下所示 [VolumeView](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/SystemWidget/app/src/main/java/com/imooc/systemwidget/VolumeView.java) ~~~ /** * 音頻條形圖 */ public class VolumeView extends View { private int mWidth; private int mRectWidth; private int mRectHeight; private Paint mPaint; private int mRectCount; private int offset = 5; private double mRandom; private LinearGradient mLinearGradient;//線性著色器 渲染器 public VolumeView(Context context) { super(context); initView(); } public VolumeView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public VolumeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } private void initView() { mPaint = new Paint(); mPaint.setColor(Color.BLUE); mPaint.setStyle(Paint.Style.FILL);//實心 mRectCount = 12; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = getWidth(); mRectHeight = getHeight(); mRectWidth = (int) (mWidth * 0.6 / mRectCount); mLinearGradient = new LinearGradient( 0, 0, mRectWidth, mRectHeight, Color.YELLOW, Color.BLUE, Shader.TileMode.CLAMP); mPaint.setShader(mLinearGradient);//shader著色器,渲染器,實現一系列的漸變渲染效果 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = 0; i < mRectCount; i++) { mRandom = Math.random(); float currentHeight = (float) (mRectHeight * mRandom); canvas.drawRect( (float) (mWidth * 0.4 / 2 + mRectWidth * i + offset), currentHeight, (float) (mWidth * 0.4 / 2 + mRectWidth * (i + 1)), mRectHeight, mPaint); } postInvalidateDelayed(300); } } ~~~ **分析:** 如果,我們取某一幀的靜態圖來繪制,其實就是繪制一個個的矩形,每一個矩形之間稍微偏移一點舉例即可,代碼如下所示 ~~~ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = 0; i < mRectCount; i++) { mRandom = Math.random(); float currentHeight = (float) (mRectHeight * mRandom); canvas.drawRect( (float) (mWidth * 0.4 / 2 + mRectWidth * i + offset), currentHeight,//每一個小矩形的高 (float) (mWidth * 0.4 / 2 + mRectWidth * (i + 1)), mRectHeight, mPaint); } postInvalidateDelayed(300); } ~~~ 通過循環創建這些小矩形,通過橫坐標的不斷偏移,就可繪制出這些小矩形。下面讓這些小矩形高度隨機變化, ~~~ mRandom = Math.random(); float currentHeight = (float) (mRectHeight * mRandom); ~~~ 然后每隔一段時間, `postInvalidateDelayed(300);`進行View重繪,給人造成一種視覺錯覺,感覺是音頻條在移動(其實只是它的高度變化了,再加上刷新頻率),而且在繪制小矩形的時候,給繪制的Paint對象增加一個LinearGradient漸變效果,這樣不同高度的矩形就會有不同的漸變效果,更加逼真。 漸變效果的源碼如下所示 ~~~ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = getWidth(); mRectHeight = getHeight(); mRectWidth = (int) (mWidth * 0.6 / mRectCount); mLinearGradient = new LinearGradient( 0, 0, mRectWidth, mRectHeight, Color.YELLOW, Color.BLUE, Shader.TileMode.CLAMP); mPaint.setShader(mLinearGradient);//shader著色器,渲染器,實現一系列的漸變渲染效果 } ~~~ **總之,不管多么復雜的自定義View都是慢慢迭代起來的功能**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看