自定义控件的归纳

    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%");



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