Android View使用详解
来源:互联网 发布:apache fastcgi php 编辑:程序博客网 时间:2024/04/29 06:51
Android中的View就是我们眼睛看到的、屏幕上显示的东东,是Activty的具体内容的体现。每一个View都有一个Canvas(画布),我们可以对它进行扩展,使用画布绘制我们想要的图像。对View进行扩展十分简单,只需要继承View类,重载它的onDraw方法,在onDraw方法中利用画布画出各种图案,包括三角形、点、矩形、线、图片等。View必须在UI线程中刷新屏幕,因此一般用于被动更新画面的游戏,既不需要实时更新画面的游戏,需要玩家操作后才更新,如:棋牌类游戏。更新画面有两种方式:调用invalidate()或者postInvalidate()方法。对于一些对画面更新实时性更强的游戏,如:动作类和角色扮演类游戏,一般使用SurfaceView代替,下一篇再讲SurfaceView。
首先比较一下invalidate()和postInvalidate()的区别,两者都是用来实现view的更新,但是前者只能在UI线程中直接调用,后者可以在非UI线程中使用,两者没有参数时都是更新整个屏幕的,可以指定参数如:invalidate(Rect rect) 、invalidate(left, top, right, bottom)、postInvalidate(left, top, right, bottom)更新指定区域。下面通过一个简单的demo来实现在UI线程中和子线程中使用invalidate()更新画布以及使用postInvalidate函数更新画布,在子线程使用invalidate函数,需要借助于Handler来帮忙。这个demo的作用是在用户点击处绘制一个红色的实心圆。
上面的代码很简单,就是响应up事件后执行changePosition函数,改变圆圈的坐标并重绘,调用invalidate函数后会重新执行onDraw函数。需要注意的是:监听UP事件时,一定得监听DOWN事件并且DOWN事件一定返回true,否则UP事件不会被监听到。因为如果不监听和处理DOWN事件,super.onTouchEvent(event)会返回false,如果onTouchEvent返回false则表示该事件已消失且不接收下次事件,这样就无法接收到UP事件了。Android中触屏事件和按键事件的分发处理,我们以后再详细讨论。
(2)在子线程中间接调用invalidate(),有些代码跟上面一样的,就不重复贴了
其实最终还是在UI线程中执行的invalidate函数,利用了handler来处理线程间的通信,这样有一个好处:就是把一些费事的操作放到子线程中处理,处理完了就通过handler通知ui线程更新画布。
(3)在子线程中使用postInvalidate()
使用postInvalidate方式跟invalidate+handler的方式原理是一样的,内部也是使用handler机制实现的,不过它更简单使用些,代码量更少。下面简单的跟踪一下sdk的源码实现。
上面代码中出现的Bitmap.Config.ARGB_8888是一个枚举值,它还有其他几个值。可以看看它的源码定义。
ARGB 分别代表的是:透明度,红色,绿色,蓝色。
ALPHA_8:存储占一个字节内存。
RGB_565:不含alpha通道(透明度),存储占两个字节内存,其中:R占5位,G占6位,B占5位
ARGB_4444:存储占两个字节内存,现在已经过时了,ARGB分别占4位。
ARGB_8888:存储占四个字节内存,ARGB分别占8位,存储的图片质量比较高,但是比较耗内存。
首先比较一下invalidate()和postInvalidate()的区别,两者都是用来实现view的更新,但是前者只能在UI线程中直接调用,后者可以在非UI线程中使用,两者没有参数时都是更新整个屏幕的,可以指定参数如:invalidate(Rect rect) 、invalidate(left, top, right, bottom)、postInvalidate(left, top, right, bottom)更新指定区域。下面通过一个简单的demo来实现在UI线程中和子线程中使用invalidate()更新画布以及使用postInvalidate函数更新画布,在子线程使用invalidate函数,需要借助于Handler来帮忙。这个demo的作用是在用户点击处绘制一个红色的实心圆。
(1)直接在UI线程中调用invalidate()
public class GameView extends View { private int cx; private int cy; private Paint p; public GameView(Context context) { super(context); this.cx = 20; this.cy = 20; this.p = new Paint(); p.setColor(Color.RED); } @Override protected void onDraw(Canvas canvas) { canvas.drawCircle(cx, cy, 10, p); } @Override public boolean onTouchEvent(MotionEvent event) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: //返回false,则该事件消失且接收不到下次事件 return true; case MotionEvent.ACTION_UP: int x = (int)event.getX(); int y = (int)event.getY(); changePosition(x, y); return true; } return super.onTouchEvent(event); } private void changePosition(int x,int y) { this.cx = x; this.cy = y; this.invalidate(); }}
上面的代码很简单,就是响应up事件后执行changePosition函数,改变圆圈的坐标并重绘,调用invalidate函数后会重新执行onDraw函数。需要注意的是:监听UP事件时,一定得监听DOWN事件并且DOWN事件一定返回true,否则UP事件不会被监听到。因为如果不监听和处理DOWN事件,super.onTouchEvent(event)会返回false,如果onTouchEvent返回false则表示该事件已消失且不接收下次事件,这样就无法接收到UP事件了。Android中触屏事件和按键事件的分发处理,我们以后再详细讨论。
(2)在子线程中间接调用invalidate(),有些代码跟上面一样的,就不重复贴了
public class GameView extends View { //..... @Override public boolean onTouchEvent(MotionEvent event) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_UP: int x = (int)event.getX(); int y = (int)event.getY(); GameThread gameThread = new GameThread(x,y); gameThread.start(); return true; } return super.onTouchEvent(event); } private Handler mHandler = new Handler() { public void handleMessage(Message msg) { changePosition(msg.arg1, msg.arg2); } }; private class GameThread extends Thread { private int x; private int y; public GameThread(int x,int y) { this.x = x; this.y = y; } public void run() { Message msg = mHandler.obtainMessage(); msg.arg1 = x; msg.arg2 = y; msg.sendToTarget(); } } }
其实最终还是在UI线程中执行的invalidate函数,利用了handler来处理线程间的通信,这样有一个好处:就是把一些费事的操作放到子线程中处理,处理完了就通过handler通知ui线程更新画布。
(3)在子线程中使用postInvalidate()
@Overridepublic boolean onTouchEvent(MotionEvent event) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_UP: int x = (int)event.getX(); int y = (int)event.getY(); GameThread gameThread = new GameThread(x,y); gameThread.start(); return true; } return super.onTouchEvent(event);} private void changePosition(int x,int y) { this.cx = x; this.cy = y;} private class GameThread extends Thread { private int x; private int y; public GameThread(int x,int y) { this.x = x; this.y = y; } public void run() { changePosition(x, y); postInvalidate(); }}
使用postInvalidate方式跟invalidate+handler的方式原理是一样的,内部也是使用handler机制实现的,不过它更简单使用些,代码量更少。下面简单的跟踪一下sdk的源码实现。
public void postInvalidate() { postInvalidateDelayed(0); } public void postInvalidateDelayed(long delayMilliseconds) { // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } } public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); }
从上面的代码可以看到,使用了mHandler去更新UI,mHandler是ViewRootHandler的一个实例,它是在UI线程中创建的。
(4)view实现双缓冲技术
当数据量比较大,绘图时间比较长时,重复绘图会出现闪烁现象,引起闪烁现象的主要原因是视觉反差比较大。使用双缓冲技术可以解决这个问题,Surfaceview默认是使用双缓冲技术的。在Android上实现双缓冲技术的步骤是:创建一个屏幕大小(实际绘图区域)的缓冲区(Bitmap),创建一个画布(Canvas),然后设置画布的bitmap为创建好的缓冲区,把需要绘制的图像绘制到缓冲区上。最后把缓冲区中的图像绘制到屏幕上。具体实现代码如下:
public Bitmap decodeBitmapFromRes(Context context, int resourseId) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Config.ARGB_8888; opt.inPurgeable = true; opt.inInputShareable = true; InputStream is = context.getResources().openRawResource(resourseId); return BitmapFactory.decodeStream(is, null, opt);} @Overrideprotected void onDraw(Canvas canvas) { Canvas bufferCanvas = new Canvas(); Bitmap bitmap = Bitmap.createBitmap(320, 480, Config.ARGB_8888); Bitmap img = decodeBitmapFromRes(mContext, R.drawable.sprite); bufferCanvas.setBitmap(bitmap); bufferCanvas.drawBitmap(img, 0, 0, null); canvas.drawBitmap(bitmap, 0, 0, null);}
上面代码中出现的Bitmap.Config.ARGB_8888是一个枚举值,它还有其他几个值。可以看看它的源码定义。
public enum Config { ALPHA_8 (2), RGB_565 (4), @Deprecated ARGB_4444 (5), ARGB_8888 (6); final int nativeInt; @SuppressWarnings({"deprecation"}) private static Config sConfigs[] = { null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 }; Config(int ni) { this.nativeInt = ni; } static Config nativeToConfig(int ni) { return sConfigs[ni]; }}
ARGB 分别代表的是:透明度,红色,绿色,蓝色。
ALPHA_8:存储占一个字节内存。
RGB_565:不含alpha通道(透明度),存储占两个字节内存,其中:R占5位,G占6位,B占5位
ARGB_4444:存储占两个字节内存,现在已经过时了,ARGB分别占4位。
ARGB_8888:存储占四个字节内存,ARGB分别占8位,存储的图片质量比较高,但是比较耗内存。
转载请注明来自:Alex Zhou,本文链接:http://codingnow.cn/android/594.html
- Android View使用详解
- Android View使用详解
- android中view组件使用详解
- android View详解
- Android的View详解
- android View详解
- android 自定义view详解
- 【Android】 View类详解
- Android View 属性详解
- android View 详解
- Android View Focus详解
- android View详解
- android View 详解
- Android View 属性详解
- Android View MeasureSpec详解
- android View 详解
- android View 详解
- Android自定义view详解
- C语言的一个关键字——static
- 世界最凶悍犬类 十大猛犬(组图)[zhuan]
- 深度优先搜索及拓扑排序(链式前向星实现)
- 算法与数据结构--实现线性表的删除操作--算法2.4
- 支持向量机: Maximum Margin Classifier —— 支持向量机简介
- Android View使用详解
- 若爱 就如桃花般地去爱
- stringutil
- .h和.cpp的区别
- ioctl
- 【php】数组
- Fedora 17 下安装Flash player
- hdu 4297 One and One Story(维护森林中的LCA)
- centos中指定apache启动时加载实用的配置文件http.conf