自定義控件的歸納

    Android類庫爲我們提供了非常豐富的控件來美化我們的UI界面。也提供了非常方式的拓展方法讓我們設計自己風格的控件(自定義控件)。

     要想自定義控件首先要先熟悉幾個方法:

1,onFinishInflate(),從xml文件中加載組件後回調。

2,onSizeChange(),組件大小改變時回調。

3,onMeasure(),回調該方法會組件的大小進行測量。

4,onLayout(),回調該方法確定組件顯示的位置。

5,onDraw(),回調該方法對組件進行繪製。

6,onTouchEvent(),監聽觸摸事件回調。

(1-5的方法的回調通常順序爲:onFinishInflate ,onMeasure,onSizeChange,onLayout,onDraw)


     自定義控件方法有三種:

1,對已有控件的擴展,

2,組合已有控件形成新的控件。

3,繼承View形成全新的控件。

     接下來會對每一種方法舉例說明(第一對TextView的擴展,多層背景+文字閃爍效果;第二組合已有控件形成統一的TopBar,

第三,圖文比例圖),怕麻煩放在一個界面展示了。


1,對已有控件的擴展:我們繼承TextView,我們想要TextView的背景更加的豐富,給其多繪製幾層背景,並且文字帶閃動效果。

package com.example.songbinwang.littledemo.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

/**
 *  on 2016/5/4.
 */
public class MyTextView extends TextView{

    public static final String Tag = "wangMyText";
    private Paint mPaint1,mPaint2;
    private LinearGradient mLinearGradient;
    private Matrix mLinearMatrix;
    private int mMeasuredWidth;
    private int mMeasuredHeight;

    private Paint mPaint;
    private int mTranslate;

    public MyTextView(Context context){
        this(context, null);
    }

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

    public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mPaint1 = new Paint();
        mPaint1.setColor(Color.LTGRAY);
        mPaint1.setStyle(Paint.Style.FILL);

        mPaint2 = new Paint();
        mPaint2.setColor(Color.YELLOW);
        mPaint2.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        Log.i(Tag,"onFinishInflate");
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w,h,oldw,oldh);
        Log.i(Tag, "onSizeChanged");
        if(mLinearGradient == null){
            mMeasuredWidth = getMeasuredWidth();
            mPaint = getPaint();
            if(mMeasuredWidth > 0){
                mLinearGradient = new LinearGradient(
                        0,0,mMeasuredWidth, 0,new int[]{Color.RED,Color.WHITE,Color.RED},null, Shader.TileMode.CLAMP
                );
                mPaint.setShader(mLinearGradient);
                mLinearMatrix = new Matrix();
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.i(Tag, "onMeasure");
        mMeasuredWidth = getMeasuredWidth();
        mMeasuredHeight = getMeasuredHeight();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.i(Tag, "onLayout");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i(Tag, "onDraw");
        canvas.drawRect(0, 0, mMeasuredWidth, mMeasuredHeight, mPaint1);
        canvas.drawRect(getPaddingLeft(), getPaddingTop(), getMeasuredWidth() - getPaddingRight(), getMeasuredHeight() - getPaddingBottom(), mPaint2);
        super.onDraw(canvas);
        if(mLinearGradient != null) {
            mTranslate += mMeasuredWidth / 5;
            if (mTranslate > 2 * mMeasuredWidth) {
                mTranslate = -mMeasuredWidth;
            }
            mLinearMatrix.setTranslate(mTranslate, 0);
            mLinearGradient.setLocalMatrix(mLinearMatrix);
            postInvalidateDelayed(100);
        }
    }
}

2,組合控件形成統一樣式的標題欄TopBar;

2,1屬性定義:value/attr.xml

    <declare-styleable name="TopBar">
        <attr name="title_text" format="string"></attr>
    </declare-styleable>

2,2代碼實現:

package com.example.songbinwang.littledemo.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.example.songbinwang.littledemo.R;

/**
 * Created by songbinwang on 2016/5/4.
 */
public class TopBar extends RelativeLayout{

    private Button mRightBtn;
    private ImageView mLeftImg, mRightImg;
    private TextView mTitleTv;

    private String mTitle_Text;

    public static final String Tag = "wangTopbar";

    public TopBar(Context context){
        this(context, null);
    }

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

    public TopBar(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public TopBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initFromAttribute(context, attrs, defStyleAttr, defStyleRes);
        addMyChildView(context);
        registerListener();
    }

    private void initFromAttribute(Context context,AttributeSet attrs,int defStyleAttr, int defStyleRes){
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.TopBar, defStyleAttr,defStyleRes);
        mTitle_Text = mTypedArray.getString(R.styleable.TopBar_title_text);//獲取屬性值
        mTypedArray.recycle();
    };

    private void addMyChildView(Context context){
        mTitleTv = new TextView(context);
        mTitleTv.setTextColor(Color.WHITE);
        mTitleTv.setText(mTitle_Text);
        mTitleTv.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 5, context.getResources().getDisplayMetrics()));
        LayoutParams mTitleLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        mTitleLp.addRule(RelativeLayout.CENTER_IN_PARENT);
        addView(mTitleTv, mTitleLp);

        mLeftImg = new ImageView(context);
        mLeftImg.setImageResource(R.drawable.action_bar_back_icon);
        LayoutParams mLeftImgLp = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.MATCH_PARENT);
        mLeftImgLp.leftMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics());
        mLeftImgLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT | RelativeLayout.CENTER_VERTICAL);
        addView(mLeftImg, mLeftImgLp);

        mRightBtn = new Button(context);
        mRightBtn.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 5, context.getResources().getDisplayMetrics()));
        mRightBtn.setText("分享");
        mRightBtn.setTextColor(Color.WHITE);
        mRightBtn.setBackgroundColor(Color.TRANSPARENT);
        LayoutParams mRightLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
        mRightLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        mRightLp.addRule(RelativeLayout.CENTER_VERTICAL);
        addView(mRightBtn, mRightLp);
        mRightBtn.setVisibility(View.GONE);

        mRightImg = new ImageView(context);
        mRightImg.setImageResource(R.drawable.actionbar_more_img);
        LayoutParams mRightImgLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
        mRightImgLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        mRightImgLp.addRule(RelativeLayout.CENTER_VERTICAL);
        mRightImgLp.rightMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics());
        addView(mRightImg, mRightImgLp);
        mRightImg.setVisibility(View.GONE);
    }

    private void registerListener(){
        mLeftImg.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                if(mOnTopbarClickListener != null){
                    mOnTopbarClickListener.onLeftClick(v);
                }
            }
        });

        mRightBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                if(mOnTopbarClickListener != null){
                    mOnTopbarClickListener.onRightClick(v);
                }
            }
        });

        mRightImg.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                if(mOnTopbarClickListener != null){
                    mOnTopbarClickListener.onRightClick(v);
                }
            }
        });
    }

    public void setRightBtnVisibility(int visibility){
        mRightBtn.setVisibility(visibility);
    }

    public void setRightImgVisibility(int visibility){
        mRightImg.setVisibility(visibility);
    }

    public Button getRightBtn(){
        return mRightBtn;
    }

    public ImageView getmRightImg(){
        return mRightImg;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        this.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light));
    }

    OnTopbarClickListener mOnTopbarClickListener;

    public void setOnTopbarClickListener(OnTopbarClickListener mOnTopbarClickListener){
        this.mOnTopbarClickListener = mOnTopbarClickListener;
    }

    public interface OnTopbarClickListener{

        public void onLeftClick(View view);

        public void onRightClick(View view);
    }
}

3,3 實際應用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.example.songbinwang.littledemo.view.TopBar
        android:id="@+id/topbar"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        app:title_text="自定義title" />

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton android:id="@+id/fab"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</LinearLayout>

        mTopbar = (TopBar)findViewById(R.id.topbar);
        mTopbar.setRightBtnVisibility(View.VISIBLE);
        mTopbar.setOnTopbarClickListener(new TopBar.OnTopbarClickListener() {

            @Override
            public void onLeftClick(View view) {
                Toast.makeText(MainActivity.this,"left", Toast.LENGTH_LONG).show();
            }

            @Override
            public void onRightClick(View view) {
                Toast.makeText(MainActivity.this,"right", Toast.LENGTH_LONG).show();
            }
        });

3,繼承View形成的新的控件。圓+弧形比例圖。

3,1屬性定義:values/attr.xml

    <declare-styleable name="CircleRatioView">
        <attr name="circle_ratio_title" format="string"></attr>
        <attr name="ratio" format="float"></attr>
    </declare-styleable>

3,2代碼實現

package com.example.songbinwang.littledemo.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import com.example.songbinwang.littledemo.R;

/**
 * Created by songbinwang on 2016/5/16.
 */
public class CircleRatioView extends View {

    private String title = "title";
    private float ratio = 0.5f;
    private Paint mPaint;
    private Rect mTextBound;
    private int mArcWidth;
    private int mCirclePadding;
    private int distanceBetweenCircleAndArc;

    public CircleRatioView(Context context){
        this(context, null);
    }

    public CircleRatioView(Context context, @Nullable AttributeSet attrs){
        this(context, attrs, 0);
    }

    public CircleRatioView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public CircleRatioView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
        super(context, attrs, defStyleAttr, defStyleRes);

        TypedArray mTypeArray = context.obtainStyledAttributes(attrs, R.styleable.CircleRatioView,defStyleAttr,defStyleRes);
        int length = mTypeArray.length();
        for(int i=0;i<length;i++){
            int attr = mTypeArray.getIndex(i);
            switch(attr){
                case R.styleable.CircleRatioView_ratio:
                    ratio = mTypeArray.getFloat(R.styleable.CircleRatioView_ratio,0.5f);
                    break;
                case R.styleable.CircleRatioView_circle_ratio_title:
                    title = mTypeArray.getString(R.styleable.CircleRatioView_circle_ratio_title);
                    break;
            }
        }
        mTypeArray.recycle();

        mArcWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20.0f, context.getResources().getDisplayMetrics());
        mCirclePadding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20.0f, context.getResources().getDisplayMetrics());
        distanceBetweenCircleAndArc = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20.0f, context.getResources().getDisplayMetrics());
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLUE);
        mPaint.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics()));

        mTextBound = new Rect();
        mPaint.getTextBounds(title, 0, title.length(), mTextBound);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    private int measureWidth(int widthMeasureSpec){
        int width = 0;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        if(widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        }else if(widthMode == MeasureSpec.AT_MOST){
            width = mTextBound.width() + (mArcWidth + distanceBetweenCircleAndArc + mCirclePadding)* 2;
        }
        return width;
    };

    private int measureHeight(int heightMeasureSpec){
        int height = 0;
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if(heightMode == MeasureSpec.EXACTLY){
            height = heightSize;
        }else if(heightMode == MeasureSpec.AT_MOST){
            height = mTextBound.height() + (mArcWidth + distanceBetweenCircleAndArc + mCirclePadding) * 2;
        }
        return height;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        mPaint.reset();
        mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
        mPaint.setStyle(Paint.Style.FILL);
        int radius = width/2 - mArcWidth - distanceBetweenCircleAndArc;
        canvas.drawCircle(width / 2, height / 2, radius, mPaint);

        RectF rectArc = new RectF(0.1f*width ,0.1f*height, 0.9f*width,0.9f*height);
        mPaint.reset();
        mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mArcWidth);
        canvas.drawArc(rectArc, -90, (int) 360 * ratio, false, mPaint);


        mPaint.reset();
        mPaint.setColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
        int distanceX = width/2 - mTextBound.width()/2;
        int distanceY = height/2 - mTextBound.height()/2 + mTextBound.height()/2;
        canvas.save();
        canvas.translate(distanceX,distanceY);
        canvas.drawText(title, 0, title.length(),0,0,mPaint);
        canvas.restore();
    }

    public void setRatio(float ratio, String title){
        this.ratio = ratio;
        this.title = title;
        mPaint.reset();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.BLUE);
        mPaint.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
        mTextBound = new Rect();
        mPaint.getTextBounds(title, 0, title.length(), mTextBound);
        invalidate();
    }
}

3,3具體應用:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main"
    tools:context=".MainActivity">

    <com.example.songbinwang.littledemo.view.MyTextView
        android:id="@+id/tv_wang"
        android:layout_marginTop="20dp"
        android:padding="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"/>

    <com.example.songbinwang.littledemo.view.CircleRatioView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="20dp"
        android:layout_below="@+id/tv_wang"
        app:ratio="0.6"
        app:circle_ratio_title="hello world"/>

    <Button
        android:id="@+id/btn_pm"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:text="packageManager"/>
</RelativeLayout>

        ratioView = (CircleRatioView) findViewById(R.id.ratioview);
        ratioView.setRatio(0.8f,"同比增長80%");



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章