android自定义控件(一) 入门

转自:鸿洋的博客,正在学习,非常感谢!
自定义View的步骤:

1、自定义View的属性

2、在View的构造方法中获得我们自定义的属性

[ 3、重写onMesure ]

4、重写onDraw

我把3用[]标出了,所以说3不一定是必须的,当然了大部分情况下还是需要重写的。
1.自定义View的属性,首先在res/values/ 下建立一个styleable.xml , 在里面定义我们的属性和声明我们的整个样式。

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!--定义一些基本的属性-->
    <attr name="textTitle" format="string"/>
    <attr name="textColor" format="color"/>
    <attr name="textSize" format="dimension"/>

    <declare-styleable name="CustomeView">
        <attr name="textTitle"/>
        <attr name="textColor"/>
        <attr name="textSize"/>
    </declare-styleable>
</resources>

我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:

一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;

2.然后在布局中声明我们的自定义View:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.down.customeviewdemo_1.MainActivity">

    <com.example.down.customeviewdemo_1.CustomeView
        android:id="@+id/img"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:textTitle="哈哈哈哈哈哈哈哈"
        app:textColor="#009933"
        app:textSize="10sp"
        />

</RelativeLayout>

**一定要引入 xmlns:custom=”http://schemas.android.com/apk/res/res-auto”我们的命名空间,后面的包路径指的是项目的package或者res-auto
**
3.自定义view代码:

  //画笔
    Paint mPaint =null;

    //可视区域
    Rect mRect =null;

    //自己定义的基本属性
    private  int textSize;
    private String textTitle;
    private  int textColor;


    public void setTextColor(int textColor) {
        this.textColor = textColor;
    }

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

    public void setTextTitle(String textTitle) {
        this.textTitle = textTitle;
    }

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

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

    public CustomeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取styleable.xml中定义的基本属性
        TypedArray a=context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomeView,defStyleAttr,0);
        int  n=a.getIndexCount();
        for (int i=0;i<n;i++){
            int attr=a.getIndex(i);
            switch (attr){
                case R.styleable.CustomeView_textTitle:
                    textTitle=a.getString(attr);
                    break;
                case R.styleable.CustomeView_textColor:
                    textColor=a.getColor(attr, Color.BLACK);//默认黑色字体
                    break;
                case R.styleable.CustomeView_textSize://这里用的是px
                    textSize=a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));//默认16sp
                    break;
            }
        }
        a.recycle();//释放回收
        //基本初始化

        mPaint =new Paint();
        mRect =new Rect();
        mPaint.setTextSize(textSize);
        mPaint.getTextBounds(textTitle, 0, textTitle.length(), mRect);
    }



    //计算控件的绘制位置
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }


    //绘制控件
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setColor(textColor);
        //绘制一个rectangle显示text
        canvas.drawRect(0, 0, getMeasuredWidth(),getMeasuredHeight(), mPaint);

        //在onMeasure没有做处理的时候,+getWidth()+"-"+getMeasuredWidth()是相等的
//        Log.i("yqy", "" + getWidth() + "-" + getMeasuredWidth() + "---" + getHeight() + "," + getMeasuredHeight());

        //绘制text
        mPaint.setColor(Color.WHITE);
        Log.i("yqy","要绘制的文本=="+textTitle+","+textSize+","+textColor+","+getWidth()+","+getHeight());
        canvas.drawText(textTitle, getWidth() / 2-mRect.width()/2 , getHeight() / 2 + mRect.height() / 2, mPaint);
    }

现在的效果是:
这里写图片描述
很明显文字不是在中间位置,原因是:

         mPaint.setTextSize(textSize);这句话的位置,在ondraw里面就是这个样子,放在构造函数中就是显示中间位置


1.构造函数中:
        mPaint.setTextSize(textSize);
        mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);
         Log.i("yqy",mRect.width()+"----"+getWidth());//315----768

2.构造函数中:
  mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);
        mPaint.setTextSize(textSize);
        Log.i("yqy",mRect.width()+"----"+getWidth());//95----768


3.onDraw()中是一样的结果
       Log.i("yqy",mRect.width()+"----"+getWidth());//95----768

 总结:大小一定要在ondraw方法之前就计算好

当我们在xml中将width或者height设置成warp_content时会全屏铺展,感觉是match_parent的效果
这时的处理是在onMeasure中重新计算要绘制的宽高
首先要了解的是,我们怎么知道用户设置的width是什么样子的?
这个可以通过一个类MeasureSpec的specMode来判断
MeasureSpec的specMode有三种类型:
EXACTLY:一般是设置了明确的值或者Match_parent
AT_MOST:表示子布局限制在一个最大值内,一般是wrap_content布局
UNSPECIFIED:表示子布局想要多大就有多大,很少使用

所以当用户设置match_parent的时候就让其自动显示,除此之外我们来设置它要绘制的范围;
所以重写onMeasure:

 int widthSise=MeasureSpec.getSize(widthMeasureSpec);
        int heightSize=MeasureSpec.getSize(heightMeasureSpec);

        int widthMode=MeasureSpec.getMode(widthMeasureSpec);
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);

        //最终要显示的宽和高
        int width;
        int height;

        if(widthMode==MeasureSpec.EXACTLY){
            width=widthSise;
        }else{//自定义
            mPaint.setTextSize(textSize);
            mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);
            width=getPaddingLeft()+mRect.width()+getPaddingRight();
        }

        if(heightMode==MeasureSpec.EXACTLY){
            height=heightSize;

        }else{
            mPaint.setTextSize(textSize);
            mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);
            height=getPaddingTop()+mRect.height()+getPaddingBottom();

        }

        //这句话一定要写
        setMeasuredDimension(width,height);

最终效果如下:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值