View的介绍(例子:自定义简单的走马灯)
2016-08-19 09:11
501 查看
一、View结构原理(本段摘自)
View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。View定义了绘图的基本操作,基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下:
1、measure操作
measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:
(1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
2、layout操作
layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:
(1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;
(2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
3、draw操作
draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
(1)绘制背景;
(2)如果要视图显示渐变框,这里会做一些准备工作;
(3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
(4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
(5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
(6)绘制滚动条;
从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。
二、自定义View用到的类
我们在重写onDraw方法的时候需要一般会用到画笔Paint类和绘制矩形区域的Rect类。
1.paint::画笔,可设置画笔的颜色,风格,效果等,如:
2.Rect:绘制一块矩形区域,如:
注意:这块矩形区域,需要用左上右下两个坐标点表示(left,top,right,bottom),你也可以获取一个Rect实例的Width和Height。
右下角(300,500)其实是不在这个矩形里面的,但是左上角(100,50)在,也就是说,这个矩形实际表示的区域是:(100,50,299,499)。
三、View中还有三个比较重要的方法
requestLayout
View重新调用一次layout过程。
invalidate
View重新调用一次draw过程
forceLayout
标识View在下一次重绘,需要重新调用layout过程。
四、Canvas画布
设置好画笔后就是在画布上“作画”了,可以看到onDraw方法有画布的参数,画布的方法有很多,可以画文字,图片,图形等,这里不详细介绍了,最后给个例子吧,先上图片(实际效果是滚动的走马灯,这里只能截图啦!!):
我们只需要继承View来定义我们想要的视图就好啦,上代码(若有不懂看注释):
是不是觉得好像不能设置大小,和速率很不方便? 都是默认的效果,那我们来自定义一些属性吧,在attrs.xml中加入如下代码:
app:size="40"
app:rate="100"
看看改变后的效果吧:整体可变化,且速率可以自己调整,想要其他的属性效果,可以继续往里面加入,这里不再多说啦~
最后说三个问题。
1.多线程的问题:
原来是使用Thread来进行延时重画,发现处理UI相关内容时可能会发现Logcat提示Only
the original thread that created a view hierarchy can touch its views这样的错误,这主要是Android的相关View和控件不是线程安全的,我们必须做独立的处理这点比J2ME麻烦一些,这里Android给 我们提供了很多方法,有关线程的,我们需要了解下J2ME中一些传统的线程创建方法,比如Runnable或直接new Thread(),大家需要了解UI线程、worker线程以及一些概念。用Handler可以很好的处理该问题(主线程或者这里说的原始线程original
thread 一般情况下是UI线程,当然UI线程并不一定是主线程,我们不能长时间的阻塞该应用,在Android平台上可能会产生类似Force close或Wait这样的对话框这里我们成为ANR),通过一个Handler对象可以很好的传递Runnable或Message。
2.Matrix():
Matrix是一个3 x 3的矩阵,他对图片的处理分为四个基本类型:
(1)、Translate————平移变换
(2)、Scale————缩放变换
(3)、Rotate————旋转变换
(4)、Skew————错切变换
上面的例子用的是缩放变换,这里只提醒一点,设置了matrix.postScale(size/oldW,size/oldH);
之后,要再次使用前先matrix.reset(),重置好后再次设置,不这样做的话,截出来的图会是旧的大小。
3.关于Bitmap的Bitmap.createBitmap():
这个方法要注意操作的长和宽,必须要小于等于图片的长和宽,不然将会抛出异常。
View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。View定义了绘图的基本操作,基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下:
1、measure操作
measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:
(1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
2、layout操作
layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:
(1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;
(2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
3、draw操作
draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
(1)绘制背景;
(2)如果要视图显示渐变框,这里会做一些准备工作;
(3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
(4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
(5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
(6)绘制滚动条;
从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。
二、自定义View用到的类
我们在重写onDraw方法的时候需要一般会用到画笔Paint类和绘制矩形区域的Rect类。
1.paint::画笔,可设置画笔的颜色,风格,效果等,如:
mPaint = new Paint();//绘画,文字颜色和内容mPaint.setColor(mTitleTextColor);canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
2.Rect:绘制一块矩形区域,如:
mBound = new Rect(); mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound); //定下文字边界
注意:这块矩形区域,需要用左上右下两个坐标点表示(left,top,right,bottom),你也可以获取一个Rect实例的Width和Height。
三、View中还有三个比较重要的方法
requestLayout
View重新调用一次layout过程。
invalidate
View重新调用一次draw过程
forceLayout
标识View在下一次重绘,需要重新调用layout过程。
四、Canvas画布
设置好画笔后就是在画布上“作画”了,可以看到onDraw方法有画布的参数,画布的方法有很多,可以画文字,图片,图形等,这里不详细介绍了,最后给个例子吧,先上图片(实际效果是滚动的走马灯,这里只能截图啦!!):
我们只需要继承View来定义我们想要的视图就好啦,上代码(若有不懂看注释):
package com.example.lanzheng; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.os.Handler; import android.util.AttributeSet; import android.view.View; import com.example.lanzheng.wheeldemo.R; /** * Created by lan.zheng on 2016/8/19. */ public class RainbowWaitingBar extends View { private int[] mImage = {R.drawable.blue_selected,R.drawable.green_select,R.drawable.yellow_select ,R.drawable.orange_select,R.drawable.pink_select,R.drawable.purple_select}; //图片的Bitmap Bitmap bitmap; //压缩后 Bitmap lBitmap; //资源 Resources lResources; //绘制范围 private Paint mPaint; private Matrix matrix; private float ih = 50; //默认size private float iw = 300; //默认size private int count = 6; //itemnum private int rate = 50; //默认速率为50毫秒 private int size = 20; //默认每个Item大小为20 private float oldH; //原始高度 private float oldW; //原始宽度 Runnable mRunnable ; public RainbowWaitingBar(Context context) { super(context); } public RainbowWaitingBar(Context context, AttributeSet attrs) { this(context, attrs,0); } public RainbowWaitingBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获得我们所定义的自定义样式属性 TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RainbowWaitingBar); //获取自定义的属性数量,根据不的属性来赋值 int n = array.getIndexCount(); for (int i = 0; i < n; i++) { int attr = array.getIndex(i); switch (attr) { case R.styleable.RainbowWaitingBar_rate: // 获取自定义的速率 rate = array.getInteger(R.styleable.RainbowWaitingBar_rate, rate); break; case R.styleable.RainbowWaitingBar_size: // 默认自定义的item大小 size = array.getInteger(R.styleable.RainbowWaitingBar_size, size); break; default: break; } } array.recycle(); //初始化Paint,为绘画做准备 mPaint = new Paint(); matrix = new Matrix(); lResources = getResources(); bitmap = BitmapFactory.decodeResource(lResources,mImage[0]); //得到原始图片的宽高 oldH = bitmap.getHeight(); oldW = bitmap.getWidth(); ih = size; //需要的宽高 iw = size*6; matrix.postScale(size/oldW,size/oldH); //压缩比例 mRunnable = new Runnable() { @Override public void run() { invalidate(); //重画onDraw } }; } Handler mHandler = new Handler(); //handler控制速率 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLACK); //黑色的画笔,下面为循环操作,每执行一次此函数位移图片 for(int i = 0;i<mImage.length;i++){ int position = (i+count) % 6 ; bitmap = BitmapFactory.decodeResource(lResources,mImage[position]); lBitmap = Bitmap.createBitmap(bitmap,0,0,(int)oldW,(int)oldH,matrix,true); //压缩图片 canvas.drawBitmap(lBitmap,i*size,0,mPaint); } count--; //循环6次 if(count == 0){ count = 6; } sleepToInvalidate(); //延时重画 } private void sleepToInvalidate(){ mHandler.postDelayed(mRunnable,rate); //用Handler来进行延时UI的重画 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //设置控件大小 setMeasuredDimension((int)iw , (int)ih); } }使用的时候只需要在xml引用即可:
<com.example.lanzheng.RainbowWaitingBar android:layout_margin="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /><!--此处可以引入自定的属性-->
是不是觉得好像不能设置大小,和速率很不方便? 都是默认的效果,那我们来自定义一些属性吧,在attrs.xml中加入如下代码:
<declare-styleable name="RainbowWaitingBar"> <attr name="rate" format="integer"/> <attr name="size" format="integer"/> </declare-styleable>RainbowWaitingBar代码不变,因为我已经提前写好了,最后就是使用了,我们只需要引入这两句在布局里即可:
app:size="40"
app:rate="100"
看看改变后的效果吧:整体可变化,且速率可以自己调整,想要其他的属性效果,可以继续往里面加入,这里不再多说啦~
最后说三个问题。
1.多线程的问题:
原来是使用Thread来进行延时重画,发现处理UI相关内容时可能会发现Logcat提示Only
the original thread that created a view hierarchy can touch its views这样的错误,这主要是Android的相关View和控件不是线程安全的,我们必须做独立的处理这点比J2ME麻烦一些,这里Android给 我们提供了很多方法,有关线程的,我们需要了解下J2ME中一些传统的线程创建方法,比如Runnable或直接new Thread(),大家需要了解UI线程、worker线程以及一些概念。用Handler可以很好的处理该问题(主线程或者这里说的原始线程original
thread 一般情况下是UI线程,当然UI线程并不一定是主线程,我们不能长时间的阻塞该应用,在Android平台上可能会产生类似Force close或Wait这样的对话框这里我们成为ANR),通过一个Handler对象可以很好的传递Runnable或Message。
2.Matrix():
Matrix是一个3 x 3的矩阵,他对图片的处理分为四个基本类型:
(1)、Translate————平移变换
(2)、Scale————缩放变换
(3)、Rotate————旋转变换
(4)、Skew————错切变换
上面的例子用的是缩放变换,这里只提醒一点,设置了matrix.postScale(size/oldW,size/oldH);
之后,要再次使用前先matrix.reset(),重置好后再次设置,不这样做的话,截出来的图会是旧的大小。
3.关于Bitmap的Bitmap.createBitmap():
这个方法要注意操作的长和宽,必须要小于等于图片的长和宽,不然将会抛出异常。
相关文章推荐
- 自定义view简单例子
- 简单的例子了解自定义ViewGroup(一)
- 自定义View简单小例子
- Android 自定义View(一) 介绍和一个简单TextView显示
- 简单的例子了解自定义ViewGroup
- 简单的例子了解自定义ViewGroup(一)
- android 自定义View 流试布局简单例子
- EnableViewState简单介绍
- DirectX 9高层着色语言介绍1——引言、简单例子、汇编语言和编译对象
- 快照隔离(Snapshot Isolation)简单介绍和例子
- Sqlite简单介绍与一些常用的例子
- 查询其实也是个循环,简单的例子介绍
- 用户自定义ID Generator的例子(http://www.hibernate.org.cn/viewtopic.php?t=6281&highlight=IdentifierGenerator)
- 自定义Linq Provide简单例子
- Android - 控件(View)之TextView, Button, ImageButton, ImageView, CheckBox, RadioButton, AnalogClock, DigitalClock 的简单介绍
- 自定义ClassLoader 简单例子
- 一个实现了ViewPart和EditroPart的简单RCP例子
- 查询其实也是个循环,简单的例子介绍
- Jboss-jbpm介绍及简单例子