您的位置:首页 > 其它

自定义view详解

2015-02-05 12:11 92 查看
建议先看官方文档,中文版点这里,说明非常全面。

自定义View一般步骤大概如下:

自定义view的属性
在View的构造方法中获得自定义的属性值,为绘图作准备
重写onMesure,获取view的大小
重写onDraw,在初始化时绘图以及view发生改变时的重绘。

1、自定义View的属性

首先在res/values/下建立一个attrs.xml,在里面定义我们的属性和声明我们的整个样式。
<resources>
<declare-styleablename="PieChart">
<attrname="showText"format="boolean"/>
<attrname="labelPosition"format="enum">
<enumname="left"value="0"/>
<enumname="right"value="1"/>
</attr>
</declare-styleable>
</resources>


在布局中声明我们的自定义View:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
<com.example.customviews.charting.PieChart
custom:showText="true"
custom:labelPosition="left"/>
</LinearLayout>


包名一定要写全。

请注意,如果你的view是一个innerclass,你必须指定这个view的outerclass。同样的,如果PieChart有一个innerclass叫做PieView。为了使用这个类中自设的属性,你应该使用com.example.customviews.charting.PieChart$PieView.

2.在View的构造方法中,获得我们的自定义的样式

必须提供一个能够获取Context和作为属性的AttributeSet对象的构造函数,获取属性。

当view从XML布局中创建了之后,XML标签中所有的属性都从资源包中读取出来并作为一个AttributeSet传递给view的构造函数。

尽管可以从AttributeSet中直接读取数值,可是这样做有些弊端:

拥有属性的资源并没有经过解析
Styles并没有运用上

Android资源编译器帮你做了许多工作来使调用obtainStyledAttributes()更简单。对res目录里的每一个资源,自动生成的R.java文件定义了存放属性ID的数组和常量,常量用来索引数组中每个属性。你可以使用这些预先定义的常量来从TypedArray中读取属性。

将AttributeSet传递给obtainStyledAttributes()方法。这个方法传回了一个TypedArray数组,包含了已经解除引用和样式化的值。
publicPieChart(Contextcontext,AttributeSetattrs){
super(context,attrs);
TypedArraya=context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0,0);
try{
mShowText=a.getBoolean(R.styleable.PieChart_showText,false);
mTextPos=a.getInteger(R.styleable.PieChart_labelPosition,0);
}finally{
a.recycle();
}
}


清注意TypedArray对象是一个共享资源,必须被在使用后进行回收

3.重写onMesure函数

这个方法的参数是View.MeasureSpec,它会告诉你的view的父控件的大小。那些值被包装成int类型,你可以使用静态方法来获取其中的信息。

MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。specMode一共有三种类型,如下所示:

EXACTLY

表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。

AT_MOST

表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。

UNSPECIFIED

表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。

@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
//Tryforawidthbasedonourminimum
intminw=getPaddingLeft()+getPaddingRight()+getSuggestedMinimumWidth();
intwidth=resolveSizeAndState(minw,widthMeasureSpec,1);
//Whateverthewidthendsupbeing,askforaheightthatwouldletthepiegetasbigasitcan
intminh=MeasureSpec.getSize(width)-(int)mTextWidth+getPaddingBottom()+getPaddingTop();
intheight=resolveSizeAndState(MeasureSpec.getSize(width)-(int)mTextWidth,heightMeasureSpec,0);
setMeasuredDimension(width,height);
}


另一个例子:
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)
{
intwidthMode=MeasureSpec.getMode(widthMeasureSpec);
intwidthSize=MeasureSpec.getSize(widthMeasureSpec);
intheightMode=MeasureSpec.getMode(heightMeasureSpec);
intheightSize=MeasureSpec.getSize(heightMeasureSpec);
intwidth;
intheight;
if(widthMode==MeasureSpec.EXACTLY)
{
width=widthSize;
}else
{
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitle,0,mTitle.length(),mBounds);
floattextWidth=mBounds.width();
intdesired=(int)(getPaddingLeft()+textWidth+getPaddingRight());
width=desired;
}
if(heightMode==MeasureSpec.EXACTLY)
{
height=heightSize;
}else
{
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitle,0,mTitle.length(),mBounds);
floattextHeight=mBounds.height();
intdesired=(int)(getPaddingTop()+textHeight+getPaddingBottom());
height=desired;
}
setMeasuredDimension(width,height);
}


需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。

4、重写onDraw函数

在你调用任何绘制方法之前,你需要创建一个Paint对象。

注意:刚开始就创建对象是一个重要的优化技巧。Views会被频繁的重新绘制,初始化许多绘制对象需要花费昂贵的代价。在onDraw方法里面创建绘制对象会严重影响到性能并使得你的UI显得卡顿。

绘制文字使用drawText()。指定字体通过调用setTypeface(),通过setColor()来设置文字颜色.
绘制基本图形使用drawRect(),drawOval(),drawArc().通过setStyle()来指定形状是否需要filled,outlined.
绘制一些复杂的图形,使用Path类.通过给Path对象添加直线与曲线,然后使用drawPath()来绘制图形.和基本图形一样,paths也可以通过setStyle来设置是outlined,filled,both.
通过创建LinearGradient对象来定义渐变。调用setShader()来使用LinearGradient。
通过使用drawBitmap来绘制图片.

protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
canvas.drawOval(
mShadowBounds,
mShadowPaint
);
canvas.drawText(mData.get(mCurrentItem).mLabel,mTextX,mTextY,mTextPaint);
for(inti=0;i<mData.size();++i){
Itemit=mData.get(i);
mPiePaint.setShader(it.mShader);
canvas.drawArc(mBounds,
360-it.mEndAngle,
it.mEndAngle-it.mStartAngle,
true,mPiePaint);
}
canvas.drawLine(mTextX,mPointerY,mPointerX,mPointerY,mTextPaint);
canvas.drawCircle(mPointerX,mPointerY,mPointerSize,mTextPaint);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: