- 浏览: 64202 次
-
文章分类
最新评论
Android View 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的作用是在用户点击处绘制一个红色的实心圆。
(1)直接在UI线程中调用invalidate()
- publicclassGameViewextendsView{
- privateintcx;
- privateintcy;
- privatePaintp;
- publicGameView(Contextcontext){
- super(context);
- this.cx=20;
- this.cy=20;
- this.p=newPaint();
- p.setColor(Color.RED);
- }
- @Override
- protectedvoidonDraw(Canvascanvas){
- canvas.drawCircle(cx,cy,10,p);
- }
- @Override
- publicbooleanonTouchEvent(MotionEventevent){
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:
- //返回false,则该事件消失且接收不到下次事件
- returntrue;
- caseMotionEvent.ACTION_UP:
- intx=(int)event.getX();
- inty=(int)event.getY();
- changePosition(x,y);
- returntrue;
- }
- returnsuper.onTouchEvent(event);
- }
- privatevoidchangePosition(intx,inty){
- 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(),有些代码跟上面一样的,就不重复贴了
- publicclassGameViewextendsView{
- //.....
- @Override
- publicbooleanonTouchEvent(MotionEventevent){
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:
- returntrue;
- caseMotionEvent.ACTION_UP:
- intx=(int)event.getX();
- inty=(int)event.getY();
- GameThreadgameThread=newGameThread(x,y);
- gameThread.start();
- returntrue;
- }
- returnsuper.onTouchEvent(event);
- }
- privateHandlermHandler=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- changePosition(msg.arg1,msg.arg2);
- }
- };
- privateclassGameThreadextendsThread{
- privateintx;
- privateinty;
- publicGameThread(intx,inty){
- this.x=x;
- this.y=y;
- }
- publicvoidrun(){
- Messagemsg=mHandler.obtainMessage();
- msg.arg1=x;
- msg.arg2=y;
- msg.sendToTarget();
- }
- }
- }
其实最终还是在UI线程中执行的invalidate函数,利用了handler来处理线程间的通信,这样有一个好处:就是把一些费事的操作放到子线程中处理,处理完了就通过handler通知ui线程更新画布。
(3)在子线程中使用postInvalidate()
- @Override
- publicbooleanonTouchEvent(MotionEventevent){
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:
- returntrue;
- caseMotionEvent.ACTION_UP:
- intx=(int)event.getX();
- inty=(int)event.getY();
- GameThreadgameThread=newGameThread(x,y);
- gameThread.start();
- returntrue;
- }
- returnsuper.onTouchEvent(event);
- }
- privatevoidchangePosition(intx,inty){
- this.cx=x;
- this.cy=y;
- }
- privateclassGameThreadextendsThread{
- privateintx;
- privateinty;
- publicGameThread(intx,inty){
- this.x=x;
- this.y=y;
- }
- publicvoidrun(){
- changePosition(x,y);
- postInvalidate();
- }
- }
使用postInvalidate方式跟invalidate+handler的方式原理是一样的,内部也是使用handler机制实现的,不过它更简单使用些,代码量更少。下面简单的跟踪一下sdk的源码实现。
- publicvoidpostInvalidate(){
- postInvalidateDelayed(0);
- }
- publicvoidpostInvalidateDelayed(longdelayMilliseconds){
- //WetryonlywiththeAttachInfobecausethere'snopointininvalidating
- //ifwearenotattachedtoourwindow
- finalAttachInfoattachInfo=mAttachInfo;
- if(attachInfo!=null){
- attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this,delayMilliseconds);
- }
- }
- publicvoiddispatchInvalidateDelayed(Viewview,longdelayMilliseconds){
- Messagemsg=mHandler.obtainMessage(MSG_INVALIDATE,view);
- mHandler.sendMessageDelayed(msg,delayMilliseconds);
- }
从上面的代码可以看到,使用了mHandler去更新UI,mHandler是ViewRootHandler的一个实例,它是在UI线程中创建的。
(4)view实现双缓冲技术
当数据量比较大,绘图时间比较长时,重复绘图会出现闪烁现象,引起闪烁现象的主要原因是视觉反差比较大。使用双缓冲技术可以解决这个问题,Surfaceview默认是使用双缓冲技术的。在Android上实现双缓冲技术的步骤是:创建一个屏幕大小(实际绘图区域)的缓冲区(Bitmap),创建一个画布(Canvas),然后设置画布的bitmap为创建好的缓冲区,把需要绘制的图像绘制到缓冲区上。最后把缓冲区中的图像绘制到屏幕上。具体实现代码如下:
- publicBitmapdecodeBitmapFromRes(Contextcontext,intresourseId){
- BitmapFactory.Optionsopt=newBitmapFactory.Options();
- opt.inPreferredConfig=Config.ARGB_8888;
- opt.inPurgeable=true;
- opt.inInputShareable=true;
- InputStreamis=context.getResources().openRawResource(resourseId);
- returnBitmapFactory.decodeStream(is,null,opt);
- }
- @Override
- protectedvoidonDraw(Canvascanvas){
- CanvasbufferCanvas=newCanvas();
- Bitmapbitmap=Bitmap.createBitmap(320,480,Config.ARGB_8888);
- Bitmapimg=decodeBitmapFromRes(mContext,R.drawable.sprite);
- bufferCanvas.setBitmap(bitmap);
- bufferCanvas.drawBitmap(img,0,0,null);
- canvas.drawBitmap(bitmap,0,0,null);
- }
上面代码中出现的Bitmap.Config.ARGB_8888是一个枚举值,它还有其他几个值。可以看看它的源码定义。
- publicenumConfig{
- ALPHA_8(2),
- RGB_565(4),
- @Deprecated
- ARGB_4444(5),
- ARGB_8888(6);
- finalintnativeInt;
- @SuppressWarnings({"deprecation"})
- privatestaticConfigsConfigs[]={
- null,null,ALPHA_8,null,RGB_565,ARGB_4444,ARGB_8888
- };
- Config(intni){
- this.nativeInt=ni;
- }
- staticConfignativeToConfig(intni){
- returnsConfigs[ni];
- }
- }
ARGB 分别代表的是:透明度,红色,绿色,蓝色。
ALPHA_8:存储占一个字节内存。
RGB_565:不含alpha通道(透明度),存储占两个字节内存,其中:R占5位,G占6位,B占5位
ARGB_4444:存储占两个字节内存,现在已经过时了,ARGB分别占4位。
ARGB_8888:存储占四个字节内存,ARGB分别占8位,存储的图片质量比较高,但是比较耗内存
1. SurfaceView的定义
前面已经介绍过View了,下面来简单介绍一下SurfaceView,参考SDK文档和网络资料:SurfaceView是View的子类,它内嵌了一个专门用于绘制的Surface,你可以控制这个Surface的格式和尺寸,Surfaceview控制这个Surface的绘制位置。surface是纵深排序(Z-ordered)的,说明它总在自己所在窗口的后面。SurfaceView提供了一个可见区域,只有在这个可见区域内的surface内容才可见。surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者
surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果surface上面有透明控件,那么每次surface变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。
SurfaceView默认使用双缓冲技术的,它支持在子线程中绘制图像,这样就不会阻塞主线程了,所以它更适合于游戏的开发。
2. SurfaceView的使用
首先继承SurfaceView,并实现SurfaceHolder.Callback接口,实现它的三个方法:surfaceCreated,surfaceChanged,surfaceDestroyed。
surfaceCreated(SurfaceHolder holder):surface创建的时候调用,一般在该方法中启动绘图的线程。
surfaceChanged(SurfaceHolder holder, int format, int width,int height):surface尺寸发生改变的时候调用,如横竖屏切换。
surfaceDestroyed(SurfaceHolder holder) :surface被销毁的时候调用,如退出游戏画面,一般在该方法中停止绘图线程。
还需要获得SurfaceHolder,并添加回调函数,这样这三个方法才会执行。
3. SurfaceView实战
下面通过一个小demo来学习SurfaceView的使用,绘制一个精灵,该精灵有四个方向的行走动画,让精灵沿着屏幕四周不停的行走。游戏中精灵素材和最终实现的效果图:
首先创建核心类GameView.java,源码如下:
- publicclassGameViewextendsSurfaceViewimplements
- SurfaceHolder.Callback{
- //屏幕宽高
- publicstaticintSCREEN_WIDTH;
- publicstaticintSCREEN_HEIGHT;
- privateContextmContext;
- privateSurfaceHoldermHolder;
- //最大帧数(1000/30)
- privatestaticfinalintDRAW_INTERVAL=30;
- privateDrawThreadmDrawThread;
- privateFrameAnimation[]spriteAnimations;
- privateSpritemSprite;
- privateintspriteWidth=0;
- privateintspriteHeight=0;
- privatefloatspriteSpeed=(float)((500*SCREEN_WIDTH/480)*0.001);
- privateintrow=4;
- privateintcol=4;
- publicGameSurfaceView(Contextcontext){
- super(context);
- this.mContext=context;
- mHolder=this.getHolder();
- mHolder.addCallback(this);
- initResources();
- mSprite=newSprite(spriteAnimations,0,0,spriteWidth,spriteHeight,spriteSpeed);
- }
- privatevoidinitResources(){
- Bitmap[][]spriteImgs=generateBitmapArray(mContext,R.drawable.sprite,row,col);
- spriteAnimations=newFrameAnimation[row];
- for(inti=0;i<row;i++){
- Bitmap[]spriteImg=spriteImgs[i];
- FrameAnimationspriteAnimation=newFrameAnimation(spriteImg,newint[]{150,150,150,150},true);
- spriteAnimations[i]=spriteAnimation;
- }
- }
- publicBitmapdecodeBitmapFromRes(Contextcontext,intresourseId){
- BitmapFactory.Optionsopt=newBitmapFactory.Options();
- opt.inPreferredConfig=Bitmap.Config.RGB_565;
- opt.inPurgeable=true;
- opt.inInputShareable=true;
- InputStreamis=context.getResources().openRawResource(resourseId);
- returnBitmapFactory.decodeStream(is,null,opt);
- }
- publicBitmapcreateBitmap(Contextcontext,Bitmapsource,introw,
- intcol,introwTotal,intcolTotal){
- Bitmapbitmap=Bitmap.createBitmap(source,
- (col-1)*source.getWidth()/colTotal,
- (row-1)*source.getHeight()/rowTotal,source.getWidth()
- /colTotal,source.getHeight()/rowTotal);
- returnbitmap;
- }
- publicBitmap[][]generateBitmapArray(Contextcontext,intresourseId,
- introw,intcol){
- Bitmapbitmaps[][]=newBitmap[row][col];
- Bitmapsource=decodeBitmapFromRes(context,resourseId);
- this.spriteWidth=source.getWidth()/col;
- this.spriteHeight=source.getHeight()/row;
- for(inti=1;i<=row;i++){
- for(intj=1;j<=col;j++){
- bitmaps[i-1][j-1]=createBitmap(context,source,i,j,
- row,col);
- }
- }
- if(source!=null&&!source.isRecycled()){
- source.recycle();
- source=null;
- }
- returnbitmaps;
- }
- publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intwidth,
- intheight){
- }
- publicvoidsurfaceCreated(SurfaceHolderholder){
- if(null==mDrawThread){
- mDrawThread=newDrawThread();
- mDrawThread.start();
- }
- }
- publicvoidsurfaceDestroyed(SurfaceHolderholder){
- if(null!=mDrawThread){
- mDrawThread.stopThread();
- }
- }
- privateclassDrawThreadextendsThread{
- publicbooleanisRunning=false;
- publicDrawThread(){
- isRunning=true;
- }
- publicvoidstopThread(){
- isRunning=false;
- booleanworkIsNotFinish=true;
- while(workIsNotFinish){
- try{
- this.join();//保证run方法执行完毕
- }catch(InterruptedExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- workIsNotFinish=false;
- }
- }
- publicvoidrun(){
- longdeltaTime=0;
- longtickTime=0;
- tickTime=System.currentTimeMillis();
- while(isRunning){
- Canvascanvas=null;
- try{
- synchronized(mHolder){
- canvas=mHolder.lockCanvas();
- //设置方向
- mSprite.setDirection();
- //更新精灵位置
- mSprite.updatePosition(deltaTime);
- drawSprite(canvas);
- }
- }catch(Exceptione){
- e.printStackTrace();
- }finally{
- if(null!=mHolder){
- mHolder.unlockCanvasAndPost(canvas);
- }
- }
- deltaTime=System.currentTimeMillis()-tickTime;
- if(deltaTime<DRAW_INTERVAL){
- try{
- Thread.sleep(DRAW_INTERVAL-deltaTime);
- }catch(InterruptedExceptione){
- e.printStackTrace();
- }
- }
- tickTime=System.currentTimeMillis();
- }
- }
- }
- privatevoiddrawSprite(Canvascanvas){
- //清屏操作
- canvas.drawColor(Color.BLACK);
- mSprite.draw(canvas);
- }
- }
GameView.java中包含了一个绘图线程DrawThread,在线程的run方法中锁定Canvas、绘制精灵、更新精灵位置、释放Canvas等操作。因为精灵素材是一张大图,所以这里进行了裁剪生成一个二维数组。使用这个二维数组初始化了精灵四个方向的动画,下面看Sprite.java的源码:
- publicclassSprite{
- publicstaticfinalintDOWN=0;
- publicstaticfinalintLEFT=1;
- publicstaticfinalintRIGHT=2;
- publicstaticfinalintUP=3;
- publicfloatx;
- publicfloaty;
- publicintwidth;
- publicintheight;
- //精灵行走速度
- publicdoublespeed;
- //精灵当前行走方向
- publicintdirection;
- //精灵四个方向的动画
- publicFrameAnimation[]frameAnimations;
- publicSprite(FrameAnimation[]frameAnimations,intpositionX,
- intpositionY,intwidth,intheight,floatspeed){
- this.frameAnimations=frameAnimations;
- this.x=positionX;
- this.y=positionY;
- this.width=width;
- this.height=height;
- this.speed=speed;
- }
- publicvoidupdatePosition(longdeltaTime){
- switch(direction){
- caseLEFT:
- //让物体的移动速度不受机器性能的影响,每帧精灵需要移动的距离为:移动速度*时间间隔
- this.x=this.x-(float)(this.speed*deltaTime);
- break;
- caseDOWN:
- this.y=this.y+(float)(this.speed*deltaTime);
- break;
- caseRIGHT:
- this.x=this.x+(float)(this.speed*deltaTime);
- break;
- caseUP:
- this.y=this.y-(float)(this.speed*deltaTime);
- break;
- }
- }
- /**
- *根据精灵的当前位置判断是否改变行走方向
- */
- publicvoidsetDirection(){
- if(this.x<=0
- &&(this.y+this.height)<GameSurfaceView.SCREEN_HEIGHT){
- if(this.x<0)
- this.x=0;
- this.direction=Sprite.DOWN;
- }elseif((this.y+this.height)>=GameSurfaceView.SCREEN_HEIGHT
- &&(this.x+this.width)<GameSurfaceView.SCREEN_WIDTH){
- if((this.y+this.height)>GameSurfaceView.SCREEN_HEIGHT)
- this.y=GameSurfaceView.SCREEN_HEIGHT-this.height;
- this.direction=Sprite.RIGHT;
- }elseif((this.x+this.width)>=GameSurfaceView.SCREEN_WIDTH
- &&this.y>0){
- if((this.x+this.width)>GameSurfaceView.SCREEN_WIDTH)
- this.x=GameSurfaceView.SCREEN_WIDTH-this.width;
- this.direction=Sprite.UP;
- }else{
- if(this.y<0)
- this.y=0;
- this.direction=Sprite.LEFT;
- }
- }
- publicvoiddraw(Canvascanvas){
- FrameAnimationframeAnimation=frameAnimations[this.direction];
- Bitmapbitmap=frameAnimation.nextFrame();
- if(null!=bitmap){
- canvas.drawBitmap(bitmap,x,y,null);
- }
- }
- }
精灵类主要是根据当前位置判断行走的方向,然后根据行走的方向更新精灵的位置,再绘制自身的动画。由于精灵的动画是一帧一帧的播放图片,所以这里封装了FrameAnimation.java,源码如下:
- publicclassFrameAnimation{
- /**动画显示的需要的资源*/
- privateBitmap[]bitmaps;
- /**动画每帧显示的时间*/
- privateint[]duration;
- /**动画上一帧显示的时间*/
- protectedLonglastBitmapTime;
- /**动画显示的索引值,防止数组越界*/
- protectedintstep;
- /**动画是否重复播放*/
- protectedbooleanrepeat;
- /**动画重复播放的次数*/
- protectedintrepeatCount;
- /**
- *@parambitmap:显示的图片<br/>
- *@paramduration:图片显示的时间<br/>
- *@paramrepeat:是否重复动画过程<br/>
- */
- publicFrameAnimation(Bitmap[]bitmaps,intduration[],booleanrepeat){
- this.bitmaps=bitmaps;
- this.duration=duration;
- this.repeat=repeat;
- lastBitmapTime=null;
- step=0;
- }
- publicBitmapnextFrame(){
- //判断step是否越界
- if(step>=bitmaps.length){
- //如果不无限循环
- if(!repeat){
- returnnull;
- }else{
- lastBitmapTime=null;
- }
- }
- if(null==lastBitmapTime){
- //第一次执行
- lastBitmapTime=System.currentTimeMillis();
- returnbitmaps[step=0];
- }
- //第X次执行
- longnowTime=System.currentTimeMillis();
- if(nowTime-lastBitmapTime<=duration[step]){
- //如果还在duration的时间段内,则继续返回当前Bitmap
- //如果duration的值小于0,则表明永远不失效,一般用于背景
- returnbitmaps[step];
- }
- lastBitmapTime=nowTime;
- returnbitmaps[step++];//返回下一Bitmap
- }
- }
FrameAnimation根据每一帧的显示时间返回当前的图片帧,若没有超过指定的时间则继续返回当前帧,否则返回下一帧。
接下来需要做的是让Activty显示的View为我们之前创建的GameView,然后设置全屏显示。
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
- WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- DisplayMetricsoutMetrics=newDisplayMetrics();
- this.getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
- GameSurfaceView.SCREEN_WIDTH=outMetrics.widthPixels;
- GameSurfaceView.SCREEN_HEIGHT=outMetrics.heightPixels;
- GameSurfaceViewgameView=newGameSurfaceView(this);
- setContentView(gameView);
- }
现在运行Android工程,应该就可以看到一个手持宝剑的武士在沿着屏幕不停的走了。
相关推荐
**Android SurfaceView 使用详解** `SurfaceView` 是 Android 系统提供的一种特殊的视图组件,它允许开发者在应用程序中创建高性能的图形界面,比如游戏、视频播放器等需要连续更新图像的应用。`SurfaceView` 提供...
在Android应用开发中,View类和SurfaceView类是两个至关重要的组件,它们分别负责界面的显示和复杂的多媒体处理。理解这两个类的特性和用途对于创建高效、流畅的应用至关重要。 首先,我们来看看Android的基础视图...
在Android游戏开发教程之二:View类与SurfaceView类中我们已经谈到,SurfaceView类是有很多优势的,所以在Android游戏开发中还是选择SurfaceView。 这里我们直接继承SurfaceView,实现SurfaceHolder.Callback接口...
- `setZOrderOnTop(boolean onTop)`:设置SurfaceView是否位于其他View之上,这对于需要全屏显示或遮挡其他View的情况很有用。 - `setFormat(int format)`:设置Surface的像素格式,如 PixelFormat.RGB_565 或 Pixel...
《迷你小鸟Android SurfaceView游戏开发详解》 在Android游戏编程领域,SurfaceView是开发者们常用的一个组件,尤其对于初学者来说,它是一个很好的起点。本文将深入探讨如何使用SurfaceView来构建一个简单的“迷你...
本文实例讲述了Android编程之SurfaceView用法。分享给大家供大家参考,具体如下: 关于surfaceView相关知识: View和SurfaceView主要区别: 1. View只能在UI线程中刷新,而SurfaceView可以在子线程中刷新 2. ...
SurfaceView surfaceView = findViewById(R.id.surface_view); MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource("video_path.mp4"); mediaPlayer.setDisplay(surfaceView.getHolder()); ...
**Android SurfaceView 游戏开发:Flappy Bird 实例详解** 在Android开发中,SurfaceView是一种特殊的视图组件,常用于高性能的图形渲染,如游戏和视频播放。它提供了直接与硬件图形缓冲区交互的能力,使得开发者能...
【Android SurfaceView 抽奖转盘制作详解】 在Android应用开发中,SurfaceView是一个非常重要的组件,尤其在处理高性能图形渲染或视频播放等需要频繁更新视图的场景中。SurfaceView不同于普通的View,它拥有自己的...
SurfaceView是Android系统中一种特殊的View,主要用于处理复杂的、高性能的图形绘制,特别是在需要独立于UI主线程进行绘制操作的场景,比如游戏开发、视频播放、相机预览等。SurfaceView内部包含了一个Surface,这个...
### SurfaceView概述与核心知识点详解 #### 一、SurfaceView简介 SurfaceView是Android系统中一个重要的绘图组件,主要用于实现实时视频流播放、游戏动画等高性能绘图场景。它是一个视图(View)的子类,其核心功能...
《实例3--使用SurfaceView详解》 在Android开发中,SurfaceView是一个非常重要的视图组件,它主要用于处理复杂的图形绘制和视频播放等需要高性能、低延迟的场景。本实例将深入探讨SurfaceView的工作原理和使用方法...
在 Android 底层,还提供了 GPU 加速功能,所以一般实时性很强的应用中主要使用 SurfaceView 而不是直接从 View 构建。 下面着重介绍 View 类: 自定义 View 的常用方法有: * onFinishInflate():当 View 中所有...
### Android 图片镜像倒影特效详解 #### 一、概述 在移动应用开发中,为了提升用户体验,开发者经常会加入一些视觉特效。其中,“图片镜像倒影特效”是一种非常受欢迎的效果,它可以让图片底部产生一种对称的镜像...
本文详细介绍了Android平台上图像绘制的三种不同API方法,包括`ImageView`、`SurfaceView`和自定义`View`,以及`AudioRecord` API的使用方法。这些都是进行Android音视频开发的基础知识,掌握这些内容可以帮助开发者...
《Android核心技术与实例详解》是一本深度探讨Android开发的书籍,其项目源码"KDWB_Android"提供了实际操作的示例,对于Android开发者来说,是深入理解Android系统和提升编程技能的重要资源。这个源码主要围绕...
### Android SurfaceFlinger详解 #### 一、SurfaceFlinger概览 **SurfaceFlinger**,按照直译可以理解为“表面投递者”。它在Android系统中扮演着至关重要的角色,主要职责是处理由上层应用程序所绘制的各种...
3.Android游戏开发之旅三 View类详解 4.Android游戏开发之旅四 Canvas和Paint实例 5.Android游戏开发之旅五 Path和Typeface 6.Android游戏开发之旅六 自定义View 7.Android游戏开发之旅七 自定义SurfaceView 8....