自定义View其实很简单

一:为什么要自定义控件?

1.实现自己的style   

2.处理特有的用户交互

3.优化布局

4.封装

 

二:自定义控件有哪些步骤?

1.自定义属性的声明与获取

2.测量onMeasure

3.绘制onDraw

4.状态的存储与恢复

 

 

 

1.自定义属性的声明与获取:

  1.1 分析需要的自定义属性(以一个圆形的下载进度条为例)

  color:进度条颜色   format:color

  Line_width:进度条宽度  format:dimension

  radius:进度条半径  format:dimension

  progress:进度值(这个可以使用系统提供的类型)

  textSize:字体大小

 

 

 

 

  1.2 在res/values/attrs.xml中定义

 



    
    
        
        
        
        
        
    

  1.3 在layout.xml文件中进行使用

  

    
 

  1.4 在view的构造方法中进行属性获取

  

    public RoundProgressBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.RoundProgressBar);

        //在构造方法中进行属性获取
        mRadius = (int) ta.getDimension(R.styleable.RoundProgressBar_radius,dpToPx(30));
        mColor = ta.getColor(R.styleable.RoundProgressBar_color,0xffff0000);
        mLineWidth= (int) ta.getDimension(R.styleable.RoundProgressBar_Line_width,dpToPx(3));
        mTextSize= (int) ta.getDimension(R.styleable.RoundProgressBar_android_textSize,72);
        mProgress=ta.getInt(R.styleable.RoundProgressBar_android_progress,30);

        //资源回收
        ta.recycle();

        //初始化画笔要在获取属性之后
        initPaint();
    }

 

2.测量OnMeasure

 

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //测量宽度
        int widthMode= MeasureSpec.getMode(widthMeasureSpec);
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);

        int width=0;
        if (widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        }else {
            int needWidth = measureWidth() + getPaddingLeft() + getPaddingRight();
            if (widthMode == MeasureSpec.AT_MOST){
                width = Math.min(widthSize,needWidth);
            }else {
                width = needWidth;
            }
        }

        //测量高度
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);
        int heightSize=MeasureSpec.getSize(heightMeasureSpec);
        int height=0;

        //若已经指定视图宽度和高度,直接赋值即可
        if(heightMode==MeasureSpec.EXACTLY){
            height=heightSize;
        }else {
            int needHeight=measureHeigth()+getPaddingBottom()+getPaddingTop();
            //若要求至多,则取最小值
            if (heightMode==MeasureSpec.AT_MOST){
                height=Math.min(needHeight,heightSize);
            }else {
                //使用自己测量值
                height=needHeight;
            }
        }
        //设置获取到的宽度和高度
        setMeasuredDimension(width,height);
    }
    //根据View的具体样式使用不同的测量方法
    private int measureHeigth() {
        return mRadius*2;
    }

    private int measureWidth() {
        return mRadius*2;
    }

 

3.OnDraw

//绘制图形,draw里面最好不要new对象,造成资源浪费
    @Override
    protected void onDraw(Canvas canvas) {
        //绘制细圆,空心圆要设置为STROKE
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mLineWidth*1.0f/4);

        int width=getWidth();
        int height=getHeight();

        canvas.drawCircle(width/2,height/2,width/2-getPaddingLeft()-mPaint.getStrokeWidth()/2,mPaint);
        mPaint.setStrokeWidth(mLineWidth);
        canvas.save();
        //重新设置canvas画布的中心
        canvas.translate(getPaddingLeft(),getPaddingTop());
        //将进度转换成角度
        float angle=mProgress * 1.0f /100 * 360;
        //以矩形作为外框,画一个扇形
        canvas.drawArc(mProgressRectF,0,angle,false,mPaint);
        canvas.restore();

        //绘制文本
        String text= mProgress + "%";
        //绘制文本不需要设置宽度
        mPaint.setStrokeWidth(0);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTextSize(mTextSize);
        int y=getHeight()/2;
        //获取字体高度
        int textHeight = getTextHeight(text);
        canvas.drawText(text,0,text.length(),getWidth()/2,y+textHeight/2,mPaint);
    }

    //提取出方法
    private int getTextHeight(String text) {
        //获取字体高度
        mPaint.getTextBounds(text,0,text.length(),bound);
        return bound.height();
    }

 

4.状态的存储与恢复

   private static final String INSTANCE="instence";
    private static final String KEY_PROGRESS="key_progress";

    //存储状态
    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle=new Bundle();
        bundle.putInt(KEY_PROGRESS,mProgress);
        bundle.putParcelable(INSTANCE,super.onSaveInstanceState());
        return bundle;
    }

    //恢复状态
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle){
            Bundle bundle= (Bundle) state;
            mProgress = bundle.getInt(KEY_PROGRESS);
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE));
            return;
        }
        super.onRestoreInstanceState(state);
    }

 

效果图:

自定义View其实很简单_第1张图片                                   自定义View其实很简单_第2张图片 

 

完整代码:

package com.example.jiaho.customview;

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

public class RoundProgressBar extends View{

    private int mRadius;
    private int mColor;
    private int mLineWidth;
    private int mTextSize;
    private int mProgress;

    private RectF mProgressRectF;

    private Paint mPaint;

    private Rect bound= new Rect();

    public RoundProgressBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.RoundProgressBar);

        //在构造方法中进行属性获取
        mRadius = (int) ta.getDimension(R.styleable.RoundProgressBar_radius,dpToPx(30));
        mColor = ta.getColor(R.styleable.RoundProgressBar_color,0xffff0000);
        mLineWidth= (int) ta.getDimension(R.styleable.RoundProgressBar_Line_width,dpToPx(3));
        mTextSize= (int) ta.getDimension(R.styleable.RoundProgressBar_android_textSize,72);
        mProgress=ta.getInt(R.styleable.RoundProgressBar_android_progress,30);

        //资源回收
        ta.recycle();

        //初始化画笔要在获取属性之后
        initPaint();
    }

    //将单位dp转化成px
    private float dpToPx(int dpVal) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal,getResources().getDisplayMetrics());
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mColor);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //测量宽度
        int widthMode= MeasureSpec.getMode(widthMeasureSpec);
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);

        int width=0;
        if (widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        }else {
            int needWidth = measureWidth() + getPaddingLeft() + getPaddingRight();
            if (widthMode == MeasureSpec.AT_MOST){
                width = Math.min(widthSize,needWidth);
            }else {
                width = needWidth;
            }
        }

        //测量高度
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);
        int heightSize=MeasureSpec.getSize(heightMeasureSpec);
        int height=0;

        //若已经指定视图宽度和高度,直接赋值即可
        if(heightMode==MeasureSpec.EXACTLY){
            height=heightSize;
        }else {
            int needHeight=measureHeigth()+getPaddingBottom()+getPaddingTop();
            //若要求至多,则取最小值
            if (heightMode==MeasureSpec.AT_MOST){
                height=Math.min(needHeight,heightSize);
            }else {
                //使用自己测量值
                height=needHeight;
            }
        }
        //设置获取到的宽度和高度
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mProgressRectF=new RectF(0,0,w - getPaddingLeft() * 2,h - getPaddingTop() * 2);
    }

    //根据View的具体样式使用不同的测量方法
    private int measureHeigth() {
        return mRadius*2;
    }

    private int measureWidth() {
        return mRadius*2;
    }

    //绘制图形,draw里面最好不要new对象,造成资源浪费
    @Override
    protected void onDraw(Canvas canvas) {
        //绘制细圆,空心圆要设置为STROKE
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mLineWidth*1.0f/4);

        int width=getWidth();
        int height=getHeight();

        canvas.drawCircle(width/2,height/2,width/2-getPaddingLeft()-mPaint.getStrokeWidth()/2,mPaint);
        mPaint.setStrokeWidth(mLineWidth);
        canvas.save();
        //重新设置canvas画布的中心
        canvas.translate(getPaddingLeft(),getPaddingTop());
        //将进度转换成角度
        float angle=mProgress * 1.0f /100 * 360;
        //以矩形作为外框,画一个扇形
        canvas.drawArc(mProgressRectF,0,angle,false,mPaint);
        canvas.restore();

        //绘制文本
        String text= mProgress + "%";
        //绘制文本不需要设置宽度
        mPaint.setStrokeWidth(0);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTextSize(mTextSize);
        int y=getHeight()/2;
        //获取字体高度
        int textHeight = getTextHeight(text);
        canvas.drawText(text,0,text.length(),getWidth()/2,y+textHeight/2,mPaint);
    }

    //提取出方法
    private int getTextHeight(String text) {
        //获取字体高度
        mPaint.getTextBounds(text,0,text.length(),bound);
        return bound.height();
    }

    public void setProgress(int progress){
        mProgress=progress;
        //每次设置progress后都要重新绘制
        invalidate();
    }

    public int getProgress(){
        return mProgress;
    }

    private static final String INSTANCE="instence";
    private static final String KEY_PROGRESS="key_progress";

    //存储状态
    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle=new Bundle();
        bundle.putInt(KEY_PROGRESS,mProgress);
        bundle.putParcelable(INSTANCE,super.onSaveInstanceState());
        return bundle;
    }

    //恢复状态
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle){
            Bundle bundle= (Bundle) state;
            mProgress = bundle.getInt(KEY_PROGRESS);
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE));
            return;
        }
        super.onRestoreInstanceState(state);
    }
}

你可能感兴趣的:(android,progressBar,自定义view)