1. 程式人生 > >Android 自定義View(基礎)

Android 自定義View(基礎)

一:引言

Android的開發中,在移動裝置上展示的所有內容,都是靠一個一個具體的檢視控制元件,按照一定的排列規則展示出來的,這些一個個控制元件,都是系統提供給我們的。但是我們看到,app商店上有些比較炫酷的頁面展示,我們會發現,系統根本沒有提供那些控制元件,那麼這是怎麼實現的呢?對就是通過我們的自定義控制元件去完成。那麼什麼是自定義控制元件呢,這裡我個人理解可以分為三類:

  1. 自定義試圖,——繼承 View,然後自繪試圖內容
  2. 自定義組合試圖,——繼承ViewGroup,然後對子類試圖進行重新佈局。
  3. 自定義已有試圖,——繼承已有的View,比如繼承ImageView

    自定義控制元件是android修行道路上的必經之路,也是得道昇仙的必備能力。所以我們的跨過去,每天練習一點點,就一點點。

二:自定義View步驟

這裡介紹下自定義試圖的主要步驟
- 自定義屬相
- 繼承View重寫構造方法
- 獲取自定義屬性
- 重寫測量控制元件的寬高
- 繪製控制元件顯示
- 提供自定義事件

三:自定義View

  • 自定義屬性
    自定義屬性一共有10中定義型別,String,boolean等,具體的型別
    和使用對應如下程式碼
    <?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="text" format="string"></attr
>
<!-- 定義:資源ID 使用:@drawable/圖片ID --> <attr name="msrc" format="reference"></attr> <!-- 定義: 顏色值 使用: android:mcolor = "#00FF00" --> <attr name="mcolor" format="color"></attr> <!-- 定義:布林型別 使用:android:misfocus = "true" -->
<attr name="misfocus" format="boolean"></attr> <!-- 定義:尺寸 使用: android:msize = "42dip" --> <attr name="msize" format="dimension"></attr> <!-- 定義:浮點值 使用: android:malpha = "0.1" --> <attr name="malpha" format="float"></attr> <!-- 定義:整形 使用:android:mcount = "12" --> <attr name="mcount" format="integer"></attr> <!-- 定義:字串 使用:android:apiKey = "2223" --> <attr name="apikey" format="string"></attr> <!-- 定義:百分數 使用:100% --> <attr name="mcurrent" format="fraction"></attr> <!-- 定義:列舉 使用:type:1 --> <attr name="type"> <enum name="cycle" value="1"></enum> <enum name="round" value="2"></enum> </attr> <declare-styleable name="customView"> <attr name="text"/> <attr name="mcolor"/> <attr name="msize"/> </declare-styleable> </resources>
  • 編寫自定義控制元件,使用自定義屬性
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    >

    <com.zhang.zs.customviewdemo.MyCuntomView
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        app:mcolor="#ff0"
        app:msize="18sp"
        app:text="你好" />
</RelativeLayout>
  • 建立自定義View繼承View(重寫構造方法)
    在建立View的時候,需要重寫構造方法,一般重寫前三個構造方法就可以了,但是如果我們的自定控制元件是通過佈局檔案的形式載入,則第二個構造必須重寫,不然會報錯。
   public MyCuntomView(Context context) {
        this(context, null);
    }

    public MyCuntomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyCuntomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //獲取自定義屬性
        initViewAtrr(context, attrs, defStyleAttr);
    }
  • 獲取自定屬性的值
    在獲取自定義屬性值的時候,我們通過迴圈的方式來獲取值,這樣獲取到屬性值,就是我們xml檔案中使用到的,沒有使用到的就獲取不到。而並獲取我們所有自定義的屬性。
  private void initViewAtrr(Context context, AttributeSet attrs, int defStyleAttr) {

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.customView, defStyleAttr, 0);

        //獲取有幾個自定義屬相
        final int count = a.getIndexCount();
        Log.e("TAG", "=====" + count);

        for (int i = 0; i < count; i++) {
            int type = a.getIndex(i);
            switch (type) {
                case R.styleable.customView_text:

                    text = a.getString(type);
                    if (TextUtils.isEmpty(text)) {
                        text = "我是文字";
                    }
                    break;

                case R.styleable.customView_mcolor:

                    corlor = a.getColor(type, Color.RED);

                    break;

                case R.styleable.customView_msize:

                    msize = a.getDimensionPixelSize(type, 15);
                    break;


            }

        }

        a.recycle();

        paint = new Paint();
        //抗鋸齒
        paint.setAntiAlias(true);


    }
  • 測量控制元件的大小(重寫onMeasure方法)
    測量之前先了解MeasureSpec的specMode,mode共有三種情況,取值分別為MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。

MeasureSpec.EXACTLY是精確尺寸,當我們將控制元件的layout_width或layout_height指定為具體數值時如andorid:layout_width=”50dip”,或者為FILL_PARENT是,都是控制元件大小已經確定的情況,都是精確尺寸。

MeasureSpec.AT_MOST是最大尺寸,當控制元件的layout_width或layout_height指定為WRAP_CONTENT時,控制元件大小一般隨著控制元件的子空間或內容進行變化,此時控制元件尺寸只要不超過父控制元件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控制元件允許的最大尺寸。

MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控制元件是AdapterView,通過measure方法傳入的模式。

知道以上概念之後我們重寫測量就容易的多了

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        bounds = new Rect();
        if (mode == MeasureSpec.EXACTLY) {
            mwidth = size;
        } else {

            paint.setTextSize(msize);

            paint.getTextBounds(text, 0, text.length(), bounds);
            mwidth = getPaddingLeft() + getPaddingRight() + bounds.width();

        }


        mode = MeasureSpec.getMode(heightMeasureSpec);
        size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {

            mheight = size;
        } else {

            paint.getTextBounds(text, 0, text.length(), bounds);
            mheight = getPaddingBottom() + getPaddingTop() + bounds.height();
        }

      r=Math.max(mwidth,mheight);
        setMeasuredDimension(r, r);

    }
  • 繪製控制元件顯示(重寫onDraw方法)
  @Override
    protected void onDraw(Canvas canvas) {
        paint.setColor(corlor);
        canvas.drawCircle(r/2,r/2,r/2,paint);
        paint.setColor(Color.BLACK);
        canvas.drawText(text,r/2-bounds.width()/2,r/2+bounds.height()/2,paint);

    }

這裡寫圖片描述

  • 定義事件
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction()==MotionEvent.ACTION_DOWN){
            if(changeColor!=null){
                changeColor.change(this,text);
            }

        }
        return super.onTouchEvent(event);
    }


    public interface  ChangeColor{

        public void change(MyCuntomView  view,String name);
    }

    public ChangeColor changeColor;

    public void setChangeColor(ChangeColor changeColor) {
        this.changeColor = changeColor;
    }

MainActivity程式碼

public class MainActivity extends AppCompatActivity {
    private MyCuntomView name;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);



        name = (MyCuntomView) findViewById(R.id.name);
        name.setChangeColor(new MyCuntomView.ChangeColor() {
            @Override
            public void change(MyCuntomView view,String text) {

                Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();

            }
        });

    }

以上就是自定義View的整個流程。