安卓自定义控件(view)

安卓自定义view







一、自定义View


  • 优点:控件最自由的实现方法,能自由控制整个View的实现
  • 缺点:比较复杂,需要正确测量View的尺寸
    手动绘制各种视觉效果、工作量大

      本次学习的内容就是通过继承View来实现一个简单的ImageView,它能够根据用户设置的大小使得图片在任何尺寸下都能正常显示。
下面先上效果图片:

1.首先创建继承自View的SimpleView类
package com.example.customviewapplication;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class SimpleView extends View {

    private Paint mBitmapPaint;
    private Drawable mDrawable;
    private  int mWidth;
    private  int mHeight;

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

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


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

        initAttrs(attrs);
        mBitmapPaint = new Paint();
        mBitmapPaint.setAntiAlias(true);

    }

    private void initAttrs(AttributeSet attrs) {
        if(attrs !=null){
            //首先赋值
            TypedArray array = null;
            try{
                array =
                        getContext().obtainStyledAttributes(attrs,R.styleable.SimpleImageView);
                //根据图片id获取到drawable对象
                mDrawable = array.getDrawable(R.styleable.SimpleImageView_src);
                //测量drawable对象的宽高
                measureDrawable();

            }finally {
                if (array != null) {
                    //然后回收
                    array.recycle();
                }
            }
        }
    }

      在构造函数中获取该控件的属性,初始化要绘制的图片与画笔。



2.在Values目录下新建attr.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SimpleImageView">
        <attr name="src" format="integer" />
    </declare-styleable>
</resources>




3.在我们的activity_main.xml中引入我们的attr.xml文件,并且设置图片资源
<?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"
    tools:context=".MainActivity">

    <com.example.customviewapplication.SimpleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:src = "@drawable/pic_2"/>

</LinearLayout>




4.测量图片的大小
private void measureDrawable() {
        if(mDrawable == null){
            throw new RuntimeException("drawable不能为空");
        }
        mWidth = mDrawable.getIntrinsicWidth();
        mHeight = mDrawable.getMinimumHeight();
    }

      当我们运行程序时,首先会从布局信息中解析SimpleView,获取属性,进入SimpleView后会首先调用initAttrs函数进行初始化。
mWidth、mHeight分别表示视图的宽度、高度。我们在xml文件中对应的Drawable得到了图片的宽高,图片有多大,我们的SimpleView就多大。在SimpleView被加载的时候,会调用onMeasuer函数来测量大小,然后再绘制


5.绘制视图内容
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //设置图片的宽高
        setMeasuredDimension(mWidth,mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mDrawable == null) {
            return;
        }
        //绘制图片
        canvas.drawBitmap(ImageUtils.drawableToBitmap(mDrawable),
                getLeft(),getTop(),mBitmapPaint);
    }

到这一步就可以运行我们的程序了。



二、View尺寸的测量


      为了支持用户自定义设置宽与高 我们需要根据用户的设置测量View的尺寸。效果如下图:

1.我们继续继续使用上文代码,这里我们要对onMeasure改写

定义变量

private  int mWidth;
private  int mHeight;

改写

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //宽度模式与大小
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        //高度模式与大小
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        //设置view的宽高
        setMeasuredDimension(measureWidth(widthMode,width),measureHeight(heightMode,height));



    }

    private int measureWidth(int mode, int width) {
        switch (mode){
            case MeasureSpec.UNSPECIFIED:
                case MeasureSpec.AT_MOST:
                    break;
                    case MeasureSpec.EXACTLY:
                        mWidth = width;
                        break;
        }
        return  mWidth;
    }

    private int measureHeight(int mode, int height) {
        switch (mode){
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:
                break;
            case MeasureSpec.EXACTLY:
                mHeight = height;
                break;
        }
        return  mHeight;
    }

      在onMeasure函数中获取宽、高的模式与大小后,分别调用measureWidth、measureHeight函数根据MeasureSpec的mode与大小计算View的具体大小。在MeasureSpec.AT_MOST
与MeasureSpec.UNSPECIFIED类型中,讲View的宽度高度设置为图片的宽度高度,当用户指定时,他的模式为EXACTLY,这样就会根据View的大小重新创建一个图片。



2.修改绘制代码
private Bitmap mBitmap;
    @Override
    protected void onDraw(Canvas canvas) {
        if (mBitmap == null) {

            mBitmap = Bitmap.createScaledBitmap(ImageUtils.drawableToBitmap(mDrawable),
                    getMeasuredWidth(),getMeasuredHeight(),true);
        }
        //绘制图片
        canvas.drawBitmap(mBitmap,getLeft(),getTop(),mBitmapPaint);
    }




三、Canvas与Paint(画布与画笔)

      简单的在视图中绘制一个竖向文本,

       //保存画布状态
        canvas.save();
        //旋转90度
        canvas.rotate(90);


        //绘制文字
        mBitmapPaint.setColor(Color.YELLOW);
        mBitmapPaint.setTextSize(300);


        canvas.drawText("黑猫警长",getLeft() +50,getTop() -50,mBitmapPaint);
        canvas.restore();

       这里有一个问题,drawText函数默认是横向绘制的,为了达到我们预期的效果,需要首先将画布旋转90度,这时候原点在左下角,向右方向x递增,向下则为y递增。假设原点为0,那么文本起始坐标就为(50,-50),x越大越靠右,y越小就越向上偏移。绘制完文本之后再将画布还原,此时就能得到我们想要的效果了!

git代码地址:

https://github.com/370937607/CustomViewApplication

发布了11 篇原创文章 · 获赞 8 · 访问量 913

猜你喜欢

转载自blog.csdn.net/qq_44739668/article/details/102528519