自定义view详解
2015-02-05 12:11
92 查看
建议先看官方文档,中文版点这里,说明非常全面。
自定义View一般步骤大概如下:
自定义view的属性
在View的构造方法中获得自定义的属性值,为绘图作准备
重写onMesure,获取view的大小
重写onDraw,在初始化时绘图以及view发生改变时的重绘。
在布局中声明我们的自定义View:
包名一定要写全。
请注意,如果你的view是一个innerclass,你必须指定这个view的outerclass。同样的,如果PieChart有一个innerclass叫做PieView。为了使用这个类中自设的属性,你应该使用com.example.customviews.charting.PieChart$PieView.
当view从XML布局中创建了之后,XML标签中所有的属性都从资源包中读取出来并作为一个AttributeSet传递给view的构造函数。
尽管可以从AttributeSet中直接读取数值,可是这样做有些弊端:
拥有属性的资源并没有经过解析
Styles并没有运用上
Android资源编译器帮你做了许多工作来使调用obtainStyledAttributes()更简单。对res目录里的每一个资源,自动生成的R.java文件定义了存放属性ID的数组和常量,常量用来索引数组中每个属性。你可以使用这些预先定义的常量来从TypedArray中读取属性。
将AttributeSet传递给obtainStyledAttributes()方法。这个方法传回了一个TypedArray数组,包含了已经解除引用和样式化的值。
清注意TypedArray对象是一个共享资源,必须被在使用后进行回收。
MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。specMode一共有三种类型,如下所示:
EXACTLY
表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。
AT_MOST
表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。
UNSPECIFIED
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。
另一个例子:
需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。
注意:刚开始就创建对象是一个重要的优化技巧。Views会被频繁的重新绘制,初始化许多绘制对象需要花费昂贵的代价。在onDraw方法里面创建绘制对象会严重影响到性能并使得你的UI显得卡顿。
绘制文字使用drawText()。指定字体通过调用setTypeface(),通过setColor()来设置文字颜色.
绘制基本图形使用drawRect(),drawOval(),drawArc().通过setStyle()来指定形状是否需要filled,outlined.
绘制一些复杂的图形,使用Path类.通过给Path对象添加直线与曲线,然后使用drawPath()来绘制图形.和基本图形一样,paths也可以通过setStyle来设置是outlined,filled,both.
通过创建LinearGradient对象来定义渐变。调用setShader()来使用LinearGradient。
通过使用drawBitmap来绘制图片.
自定义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);
}
相关文章推荐
- Three20软件引擎之自定义TableView列表详解(二)
- 自定义组件之【柱状图】详解 已封装成View
- Android 自定义View修炼-Android开发之自定义View开发及实例详解
- Android 自定义VIEW属性用法详解(attrs、TypedArray)
- 笔记83--自定义view--onMesure、MeasureSpec详解
- 详解Android中自定义View的invalidate,Handler和postInvalidate
- 【Android实战】记录自学自定义GifView过程,详解属性那些事!【学习篇】
- Android中自定义View的MeasureSpec介绍及使用详解
- 【android自定义控件】TextView详解及自定义<一>
- 底部导航栏(自定义View+ViewPager实现) android项目详解
- 详解iOS开发之自定义View
- Android自定义组件之一:View详解
- 基于Android中Webview使用自定义的javascript进行回调的问题详解
- android自定义View构造方法以及获取自定义属性详解
- 自定义View详解
- Three20软件引擎之自定义TableView列表详解(二)
- Three20软件引擎之自定义TableView列表详解
- ****Android自定义组件之一:View详解
- 深入了解View实现原理以及自定义View详解
- Android自定义ViewGroup详解