自定义视图的简单应用

1、在res/values目录下新建attrs.xml文件:


<resources>
    <declare-styleable name="myStyleable">
        <attr name="max" format="integer"/>
        <attr name="progress" format="integer"/>
        <attr name="textSize" format="dimension"/>
        <attr name="smallBgColor" format="color"/>
        <attr name="progressColor" format="color"/>
    </declare-styleable>

</resources>


format(自定义属性的格式)可选内容有integer、dimension(尺寸)、color、boolean、enum(枚举值)、flag(位或运算)、float、fraction(百分数)、reference(参考某一ID、默认)、string.


<declare-styleable name = "名称">
      <attr name = "background" format = "reference" />
</declare-styleable>

eg:

<ImageView
     android:layout_width = "42dip"
     android:layout_height = "42dip"
     android:background = "@drawable/图片ID"
                     />

2、自定义视图:

public class XykjProgressBar extends View {
    //进度条的半径
    private int radius;
    //圆心
    private int cx, cy;
    //画笔
    private Paint paint;
    //进度的最大值
    private int max = 100;

    //进度值
    private int progress;
    //文本大小
    private int textSize;
    //内部小圆的背景色
    private int smallBgColor = 0xff3F51B5;
    //进度条的颜色
    private int progressColor = 0xffFF4081;

    public XykjProgressBar(Context context) {
        super(context);
        init();
    }

    public XykjProgressBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //解析自定义属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.myStyleable);
        textSize = ta.getDimensionPixelSize(R.styleable.myStyleable_textSize, 0);  //sp dp dip
        max = ta.getInteger(R.styleable.myStyleable_max,100);
        progress = ta.getInteger(R.styleable.myStyleable_progress,0);
        smallBgColor = ta.getColor(R.styleable.myStyleable_smallBgColor,0xff3F51B5);
        progressColor = ta.getColor(R.styleable.myStyleable_progressColor,0xffFF4081);
        //释放资源
        ta.recycle();
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(smallBgColor);
        //画中间的小圆
        canvas.drawCircle(cx, cy, radius - 10, paint);
        //文本
        int num = 100 * progress / max;  //50 100 -->50%
        String str = num + "%";
        //获取文本的边框
        Rect bound = new Rect();
        paint.setTextSize(textSize);
        paint.getTextBounds(str, 0, str.length(), bound);
        int x = cx - bound.centerX();
        int y = cy - bound.centerY();
        paint.setColor(Color.WHITE);
        //画进度文本
        canvas.drawText(str, x, y, paint);
        //进度的弧线
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        paint.setColor(progressColor);
        //计算所跨越的弧度
        int degree = 360 * progress / max;
        RectF rect = new RectF(cx - radius, cy - radius, cx + radius, cy + radius);
        //画弧线
        canvas.drawArc(rect, -90, degree, false, paint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getSize(widthMeasureSpec);
        int height = getSize(heightMeasureSpec);
        //初始化圆心
        cx = width / 2;
        cy = height / 2;
        //取最短的边的一半作为进度半径
        radius = width < height ? cx : cy;
        radius -= 10;
        //如果没有默认值则,初始化文本大小
        if (textSize == 0) {
            textSize = radius / 2;
        }
        setMeasuredDimension(width, height);
    }

    //获取尺寸方法(自定义的根据模式获取,wrap_content时设置默认值为80    private int getSize(int measureSpec) {
        //获取测量模式
        int mode = MeasureSpec.getMode(measureSpec);
        int size = 80;
        if (mode == MeasureSpec.EXACTLY) {
            //精确的,固定的值,match_parent或者100  200
            size = MeasureSpec.getSize(measureSpec);
        }/*else if(mode == MeasureSpec.AT_MOST){
            //不确定,如wrap_content
            size = 80;
        }*/
        return size;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public int getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        if (this.progress != progress && progress <= max) {
            this.progress = progress;
            invalidate();
        }
    }

    public int getTextSize() {
        return textSize;
    }

    public void setTextSize(int textSize) {
        this.textSize = textSize;
    }

    public int getSmallBgColor() {
        return smallBgColor;
    }

    public void setSmallBgColor(int smallBgColor) {
        this.smallBgColor = smallBgColor;
    }

    public int getProgressColor() {
        return progressColor;
    }

    public void setProgressColor(int progressColor) {
        this.progressColor = progressColor;
    }
}


3、在主界面中引用:

<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"
    tools:context="com.xykj.myprogressbar.MainActivity">

    <com.xykj.myprogressbar.XykjProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:textSize="30sp"
        app:smallBgColor="#ff000000"/>

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click" />

</LinearLayout>

java代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    XykjProgressBar pBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pBar = (XykjProgressBar) findViewById(R.id.progress_bar);
        pBar.setMax(100);
        pBar.setProgress(10);
        pBar.setTextSize(50);
        findViewById(R.id.btn).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int p = pBar.getProgress();
        p+=5;
        pBar.setProgress(p);
    }
}

结果如下图:

自定义视图的简单应用


面试题:介绍下实现一个自定义View的基本流程:

1、自定义view属性,编写attrs.xml文件

2、在layout布局中引用,同时引用命名空间

3、在view的构造方法中获得我们自定义的属性,在自定义的控件中进行读取

4、重写onMeasure()方法

5、重写onDraw()方法