您的位置:首页 > 移动开发 > Android开发

android自定义控件(一) 入门

2016-06-14 16:46 465 查看
转自:鸿洋的博客,正在学习,非常感谢!

自定义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);


最终效果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: