Android自定义view

来源:互联网 发布:lol安妮舞会公主淘宝 编辑:程序博客网 时间:2024/06/03 21:49

自定义View是每个Android开发人员的必备技能,首先我们先来看一下自定义view的几个步骤:

1、自定义属性

2、在构造函数中获取自定义属性

3、重写onMeasure()  [非必须]

4、重写onDraw()

对于自定义view并不是每个自定义View都必须重写onMeasure方法,当然绝大多数时候我们还是需要重写这个方法。

   下面我们分别来看下这几个步骤:


1、自定义属性:

      

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="titleTextColor" format="color"/>    <attr name="titleTextSize" format="dimension"/>    <attr name="titleText" format="string"/>    <declare-styleable name="CustomTextView">        <attr name="customTitleText" format="string"/>        <attr name="customTitleTextColor" format="color"/>        <attr name="custonTitleTextSize" format="dimension"/>    </declare-styleable></resources>

在这里我们现在res/values 的文件下下新建一个attrs.xml,然后我们在这个文件里面定义了文字、文字大小、文字颜色三个属性,format是指属性的类型一共有10个:string、color、integer、dimession、enum、reference(参考某一资源id)、boolean、float、fraction(百分数)、flag

  (1)、 string:字符串

         属性定义:

    <declare-styleable name="CustomTextView">        <attr name="customTitleText" format="string"/>    </declare-styleable>
         属性使用:
  <com.project.viewtestdemo1.view.MyTextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        app:customTitleText="自定义文字"        />

(2)、color:颜色
     属性定义:
<declare-styleable name="名称">      <attr format="color" name="textColor" />  </declare-styleable>  

     属性使用
<TextView      android:layout_width="42dip"      android:layout_height="42dip"      android:textColor="#00FF00" />  
(3)、integer:整型值
     属性定义:
<declare-styleable name="AnimatedRotateDrawable">      <attr format="integer" name="frameDuration" />      <attr format="integer" name="framesCount" />  </declare-styleable>  
     属性使用
<animated-rotate      android:frameDuration="100"      android:framesCount="12"       />  
(4)、dimession:尺寸值
     属性定义:
<declare-styleable name="名称">      <attr format="dimension" name="layout_width" />  </declare-styleable>  
     属性使用
<Button      android:layout_width="42dip"      android:layout_height="42dip" />  
(5)、enum:枚举
     属性定义:
<declare-styleable name="名称">      <attr name="orientation">          <enum name="horizontal" value="0" />          <enum name="vertical" value="1" />      </attr>  </declare-styleable>  
     属性使用
<LinearLayout      android:orientation="vertical" >  </LinearLayout>  
(6)、reference:参考某一资源ID
     属性定义:
<declare-styleable name="名称">      <attr format="reference" name="background" />  </declare-styleable>  
     属性使用
<ImageView      android:layout_width="42dip"      android:layout_height="42dip"      android:background="@drawable/图片ID" />  
(7)、boolean布尔值
     属性定义:
<declare-styleable name="名称">      <attr format="boolean" name="focusable" />  </declare-styleable>  
     属性使用
<Button      android:layout_width="42dip"      android:layout_height="42dip"      android:focusable="true" />  
(8)、float:浮点数
     属性定义:
<declare-styleable name="AlphaAnimation">      <attr format="float" name="fromAlpha" />      <attr format="float" name="toAlpha" />  </declare-styleable>  
     属性使用
<alpha      android:fromAlpha="1.0"      android:toAlpha="0.7" />  
(9)、fraction:百分数
     属性定义:
<declare-styleable name="RotateDrawable">      <attr format="fraction" name="pivotX" />      <attr format="fraction" name="pivotY" />  </declare-styleable>  
     属性使用
<rotate      android:pivotX="200%"      android:pivotY="300%"      />  
(10)、flag:位或运算
      属性定义:
<declare-styleable name="名称">    <attr name="windowSoftInputMode">        <flag name="stateUnspecified" value="0"/>        <flag name="stateUnchanged" value="1"/>        <flag name="stateHidden" value="2"/>        <flag name="stateAlwaysHidden" value="3"/>        <flag name="stateVisible" value="4"/>        <flag name="stateAlwaysVisible" value="5"/>        <flag name="adjustUnspecified" value="0x00"/>        <flag name="adjustResize" value="0x10"/>        <flag name="adjustPan" value="0x20"/>        <flag name="adjustNothing" value="0x30"/>    </attr></declare-styleable>
   属性使用:
<activity
android:windowSoftInputMode="stateUnspecified | stateUnchanged | stateHidden" >
</activity>
要使用自定义view中的自定义属性必须在根布局上添加xmlns:名称="http://schemas.android.com/apk/res-auto"后面的res-auto可以换成res/自定义view的全路径名

2、在自定义view的构造函数中,获取自定义属性
public class MyTextView extends View {    private String mText;    private int    mTextColor;    private float  mTextSize;    private Paint  mPaint;    private Rect   mRect;    public MyTextView(Context context) {        this(context, null);    }    public MyTextView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public MyTextView(Context context, AttributeSet attrs, int defStyleRes) {        super(context, attrs, defStyleRes);        //获取属性        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTextView, defStyleRes, 0);        int        indexCount = typedArray.getIndexCount();        for (int i = 0; i < indexCount; i++) {            int index = typedArray.getIndex(i);            switch (index) {                case R.styleable.CustomTextView_titleText:                    mText = typedArray.getString(index);                    break;                case R.styleable.CustomTextView_titleTextColor:                    mTextColor = typedArray.getColor(index, Color.BLACK);                    break;                case R.styleable.CustomTextView_titleTextSize:                    //默认设置16sp,TypeValue也可以把sp转成px                    mTextSize = typedArray.getDimensionPixelSize(index, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));                    break;                default:                    break;            }        }        //一定要记得释放        typedArray.recycle();        /**         * 获取绘制文本的宽和高         */        mPaint = new Paint();        mPaint.setTextSize(mTextSize);        mRect = new Rect();        //计算文字所在矩形,可以得到宽高        mPaint.getTextBounds(mText, 0, mText.length(), mRect);    }}
3、重写onDraw()、onMeasure()调用父类的:
    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        mPaint.setColor(Color.GREEN);        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);        mPaint.setColor(mTextColor);        //参数1:要画的文字 2、从x轴开始绘制 3、y轴开始画的位置        canvas.drawText(mText, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mPaint);    }


基本已经实现了自定义View。但是此时如果我们把布局文件的宽和高写成wrap_content,会发现效果并不是我们的预期:
可以看到系统帮我们测量的宽高都是MATCH_PARNET,当我们设置了明确的宽高的时候,系统帮我们测量的宽高就是我们设置的宽高,当我们设置WRAP_CONTENT或MATCH_PARNET的时候,系统帮我们测量的都是MATCH_PARNET
所以,当我们设置WRAP_CONTENT的时候,我们需要重写onMeasure()。
重写之前我们要了解MeasureSpec的specMode,一共三种类型:
1、EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
2、AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
3、UNSPECIFIED:表示子布局想要多大就多大,很少使用
我们重写onMeasure方法:
 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int widthMode  = MeasureSpec.getMode(widthMeasureSpec);//测量模式        int widthSize  = MeasureSpec.getSize(widthMeasureSpec);//测量宽度        int heightMode = MeasureSpec.getMode(heightMeasureSpec);//测量模式        int heightSize = MeasureSpec.getSize(heightMeasureSpec);//测量高度        int width;        int height;        if (widthMode == MeasureSpec.EXACTLY) {//有明确的值或者MATCH_PARENT            width = widthSize;        } else {            mPaint.setColor(mTextColor);            mPaint.getTextBounds(mText, 0, mText.length(), mRect);            int textWidth = mRect.width();//获取文字高度            width = getPaddingLeft() + textWidth + getPaddingRight();        }        if (heightMode == MeasureSpec.EXACTLY) {            height = heightSize;        } else {            mPaint.setColor(mTextColor);            mPaint.getTextBounds(mText, 0, mText.length(), mRect);            int textHeight = mRect.height();            height = getPaddingTop() + textHeight + getPaddingBottom();        }        setMeasuredDimension(width,height);    }

然后修改布局文件:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.project.viewtestdemo1.view.MyTextView        android:id="@+id/mtv"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        app:titleText="自定义文字"        app:titleTextColor="#000"        app:titleTextSize="20sp"        android:padding="10dp"        android:layout_centerInParent="true"        /></RelativeLayout>
运行效果:

完全复合我们的预期,现在我们可以对高度、宽度进行随便的设置了,基本可以满足我们的需求。
在构造中添加点击事件
 this.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                mText = randomText();                postInvalidate();//更新view            }        });
    private String randomText() {        Random       random = new Random();        Set<Integer> set    = new HashSet<Integer>();        while (set.size() < 4) {            int randomInt = random.nextInt(10);            set.add(randomInt);        }        StringBuffer sb = new StringBuffer();        for (Integer i : set) {            sb.append("" + i);        }        return sb.toString();    }




0 0
原创粉丝点击