Android自定义View初步

  经过上一篇的介绍,大家对于自定义View一定有了一定的认识,接下来我们就以实现一个图片下显示文字的自定义View来练习一下。废话不多说,下面进入我们的正题,首先看一下我们的思路,1、我们需要通过在values文件夹下添加一个attrs的文件,里面设置我们的自定义属性;2、通过重写View类,来获得我们设置的自定义属性的参数,并进行绘制;3、在我们的视图文件中进行引用。好了到这里我们的基本思路就已经形成,下面我们开始进行我们的实战编码操作。

  第一步:在res目录下,values文件夹下,新建一个attrs.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <attr name="TitleText" format="string" />
    <attr name="TitleColor" format="color" />
    <attr name="TitleSize" format="dimension" />
    
    <attr name="image" format="reference" />
    <attr name="imageScaleType">
        <enum name="fillXY" value="0" />
        <enum name="center" value="1" />
    </attr>
    
    <declare-styleable name="CustomImageView">
        <attr name="TitleText" />  
        <attr name="TitleColor" />  
        <attr name="TitleSize" />
        <attr name="image" />
        <attr name="imageScaleType" />
    </declare-styleable>
    
</resources>

  提示一下:format对应的是该参数的值类型

  第二步:重写我们的View类:

public class MySelfImageView extends View {
    
    /*
     * 图片区域
     */
    Rect imageRect;
    /*
     * 文字区域
     */
    Rect titleRect;
    /*
     * 画笔对象
     */
    Paint mPaint;
    /*
     * 图片标题文字
     */
    String titleText;
    /*
     * 图片标题文字颜色
     */
    int titleColor;
    /*
     * 图片标题文字大小
     */
    int titleSize;
    /*
     * 图片资源
     */
    Bitmap image;
    /*
     * 图片资源显示样式
     */
    int imageFillXY;
    
    int mWidth = 0;
    int mHeight = 0;
    
    public MySelfImageView(Context context) {
        this(context, null);
    }

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

    public MySelfImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取自定义设置的属性
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyleAttr, 0);
        int n = typedArray.getIndexCount();
        for(int i=0; i<n; i++){
            int att = typedArray.getIndex(i);
            //分别取出自定义属性设置的值
            switch (att) {
                case R.styleable.CustomImageView_TitleText:
                    titleText = typedArray.getString(att);
                    break;
                case R.styleable.CustomImageView_TitleColor:
                    titleColor = typedArray.getColor(att, Color.RED);        
                    break;
                case R.styleable.CustomImageView_TitleSize:
                    titleSize = typedArray.getDimensionPixelSize(att, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,  
                            16, getResources().getDisplayMetrics())); 
                    break;
                case R.styleable.CustomImageView_image:
                    image = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(att, 0));
                    break;
                case R.styleable.CustomImageView_imageScaleType:
                    imageFillXY = typedArray.getInt(att, 0);
                    break;
            }
        }
        typedArray.recycle();
        
        imageRect = new Rect();
        mPaint = new Paint();
        titleRect = new Rect();
        mPaint.setTextSize(titleSize);
        // 计算描绘字体需要的范围
        mPaint.getTextBounds(titleText, 0, titleText.length(), titleRect);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /** 
         * 设置宽度 
         */
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);
        
        if(specMode == MeasureSpec.EXACTLY){
            mWidth = specSize;
        }else{
            // 由图片决定的宽  
            int desireByImg = getPaddingLeft() + getPaddingRight() + image.getWidth();  
            // 由字体决定的宽  
            int desireByTitle = getPaddingLeft() + getPaddingRight() + titleRect.width();  
      
            if (specMode == MeasureSpec.AT_MOST){// wrap_content  
                int desire = Math.max(desireByImg, desireByTitle);
                mWidth = Math.min(desire, specSize);
            }  
        }
        
        /** 
         * 设置高度 
         */
        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);
        
        if(specMode == MeasureSpec.EXACTLY){
            mHeight = specSize;
        }else{
            int desire = getPaddingTop() + getPaddingBottom() + image.getHeight() + titleRect.height();  
            if (specMode == MeasureSpec.AT_MOST){// wrap_content  
                mHeight = Math.min(desire, specSize);
            }  
        }
        setMeasuredDimension(mWidth, mHeight);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        /** 
         * 边框 
         */  
        mPaint.setStrokeWidth(4);//设置空心线宽  
        mPaint.setStyle(Paint.Style.STROKE);//设置画笔为空心 
        mPaint.setColor(Color.CYAN);//设置画笔颜色  
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);  
  
        imageRect.left = getPaddingLeft();  
        imageRect.right = mWidth - getPaddingRight();  
        imageRect.top = getPaddingTop();  
        imageRect.bottom = mHeight - getPaddingBottom();  
  
        mPaint.setColor(titleColor);  
        mPaint.setStyle(Style.FILL);  
        /** 
         * 当前设置的宽度小于字体需要的宽度,将字体改为xxx... 
         */  
        if (titleRect.width() > mWidth) {  
            TextPaint paint = new TextPaint(mPaint);  
            String msg = TextUtils.ellipsize(titleText, paint, (float) mWidth - getPaddingLeft() - getPaddingRight(),  
                    TextUtils.TruncateAt.END).toString();  
            canvas.drawText(msg, getPaddingLeft(), mHeight - getPaddingBottom(), mPaint);
        } else {  
            //正常情况,将字体居中  
            canvas.drawText(titleText, mWidth / 2 - titleRect.width() * 1.0f / 2, mHeight - getPaddingBottom(), mPaint);
        }  
  
        //取消使用掉的块 
        imageRect.bottom -= titleRect.height();  
  
        if (imageFillXY == 0) {
            canvas.drawBitmap(image, null, imageRect, mPaint);  
        } else {  
            //计算居中的矩形范围  
            imageRect.left = mWidth / 2 - image.getWidth() / 2;  
            imageRect.right = mWidth / 2 + image.getWidth() / 2;  
            imageRect.top = (mHeight - titleRect.height()) / 2 - image.getHeight() / 2;  
            imageRect.bottom = (mHeight - titleRect.height()) / 2 + image.getHeight() / 2;  
  
            canvas.drawBitmap(image, null, imageRect, mPaint);  
        } 
    }

}

  第三步:布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:zhy="http://schemas.android.com/apk/res/com.example.myselfview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <com.example.myselfview.view.MySelfImageView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_margin="10dp"  
        android:padding="10dp"  
        zhy:image="@drawable/ic_launcher"  
        zhy:imageScaleType="center"  
        zhy:TitleText="hello andorid ! "  
        zhy:TitleColor="#ff0000"  
        zhy:TitleSize="30sp" />  
  
    <com.example.myselfview.view.MySelfImageView 
        android:layout_width="100dp"  
        android:layout_height="wrap_content"  
        android:layout_margin="10dp"  
        android:padding="10dp"  
        zhy:image="@drawable/ic_launcher"  
        zhy:imageScaleType="center"  
        zhy:TitleText="helloworldwelcome"  
        zhy:TitleColor="#00ff00"  
        zhy:TitleSize="20sp" />  
  
    <com.example.myselfview.view.MySelfImageView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_margin="10dp"  
        android:padding="10dp"  
        zhy:image="@drawable/im"  
        zhy:imageScaleType="center"  
        zhy:TitleText="山水美景"  
        zhy:TitleColor="#ff0000"  
        zhy:TitleSize="12sp" />

</LinearLayout>

  最后效果图:

  

  相对第一、三步,第二步相对更复杂一些,下面我就对第二步里面的具体内容进行一下解析:

//获取自定义设置的属性
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyleAttr, 0);
        int n = typedArray.getIndexCount();
        for(int i=0; i<n; i++){
            int att = typedArray.getIndex(i);
            //分别取出自定义属性设置的值
            switch (att) {
                case R.styleable.CustomImageView_TitleText:
                    titleText = typedArray.getString(att);
                    break;
                case R.styleable.CustomImageView_TitleColor:
                    titleColor = typedArray.getColor(att, Color.RED);        
                    break;
                case R.styleable.CustomImageView_TitleSize:
                    titleSize = typedArray.getDimensionPixelSize(att, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,  
                            16, getResources().getDisplayMetrics())); 
                    break;
                case R.styleable.CustomImageView_image:
                    image = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(att, 0));
                    break;
                case R.styleable.CustomImageView_imageScaleType:
                    imageFillXY = typedArray.getInt(att, 0);
                    break;
            }
        }
        typedArray.recycle();

  作用是获取我们在attrs中设置的自定义参数,在布局文件中进行的赋值。

  onMeasure()方法用于设置控件的宽度,控件的长宽值如何取得呢?下面我们就来进行一下解析:

  

  最后是我们绘图方法onDraw(Canvas canvas):

@Override
    protected void onDraw(Canvas canvas) {
        /** 
         * 空心矩形绘制 
         */  
        mPaint.setStrokeWidth(4);//设置空心线宽  
        mPaint.setStyle(Paint.Style.STROKE);//设置画笔为空心 
        mPaint.setColor(Color.CYAN);//设置画笔颜色  
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);  
  
        imageRect.left = getPaddingLeft();//获取图片左上角坐标  
        imageRect.right = mWidth - getPaddingRight();//获取图片右上角坐标  
        imageRect.top = getPaddingTop();//获取图片距控件顶部距离  
        imageRect.bottom = mHeight - getPaddingBottom();//确定图片底部坐标  
  
        mPaint.setColor(titleColor);//设置文本的颜色  
        mPaint.setStyle(Style.FILL);//设置文本内容的填充方式  
        /** 
         * 当前设置的宽度小于字体需要的宽度,将字体改为xxx... 
         */  
        if (titleRect.width() > mWidth) {  
            TextPaint paint = new TextPaint(mPaint);  
            String msg = TextUtils.ellipsize(titleText, paint, (float) mWidth - getPaddingLeft() - getPaddingRight(),  
                    TextUtils.TruncateAt.END).toString();  
            canvas.drawText(msg, getPaddingLeft(), mHeight - getPaddingBottom(), mPaint);
        } else {  
            //正常情况,将字体居中  
            canvas.drawText(titleText, mWidth / 2 - titleRect.width() * 1.0f / 2, mHeight - getPaddingBottom(), mPaint);
        }  
  
        //取消使用掉的块 
        imageRect.bottom -= titleRect.height();//因为文字内容占用一定的高度,所以图片的底部坐标需要上移。  
  
        if (imageFillXY == 0) {
            canvas.drawBitmap(image, null, imageRect, mPaint);  
        } else {  
            //计算居中的矩形范围  
            imageRect.left = mWidth / 2 - image.getWidth() / 2;  
            imageRect.right = mWidth / 2 + image.getWidth() / 2;  
            imageRect.top = (mHeight - titleRect.height()) / 2 - image.getHeight() / 2;  
            imageRect.bottom = (mHeight - titleRect.height()) / 2 + image.getHeight() / 2;  
  
            canvas.drawBitmap(image, null, imageRect, mPaint);  
        } 
    }

  好了,到这里关于自定义View的初步学习就介绍完毕,如果你有更好的关于自定义View的文章,欢迎留言交流。

原文地址:https://www.cnblogs.com/AndroidJotting/p/5460646.html