欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

安卓自定义控件(view)

程序员文章站 2022-05-04 19:45:39
...

安卓自定义view







一、自定义View


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

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

安卓自定义控件(view)
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的尺寸。效果如下图:

安卓自定义控件(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(画布与画笔)

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

安卓自定义控件(view)
       //保存画布状态
        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