Android的渲染分为2D渲染和3D渲染两种,当中2D渲染的引擎为Skia。3D渲染的引擎是OpenGL ES。眼下。Android支持OpenGL ES1.0和OpenGL ES 2.0两种标准。
1.2D图像处理
在Android中,图像处理时开发类似图片浏览器、拍照顾用时必备的基本能力。
须要说明的是,Android支持对BMP、JPEG、PNG等常见图像格式的浏览,支持将BMP压缩为JPEG、PNG等有损压缩图像格式。在Android 4.0中,引入了对webp的支持。
BMP没有採用不论什么压缩算法。当然也没有不论什么失真。依据图像深度的不用,BMP有1bit、4bit、8bit和24bit等不同的位深。
在存储数据时,对BMP图像的扫描方式按从左到右,从下到上的顺序进行。
(1)基本接口
图像处理的基本接口主要位于android.graphics和android.graphics.drawable两个包中,当中android.graphics除了具有主要的图像处理能力外,更偏向于图形层面的处理。
1)获取图像基本信息
图像的基本信息包含宽、高、像素和格式等。眼下,Android支持的RGB配置包含ALPHA_8、RGB_565、ARGB_4444、ARGB_8888等,另外还支持将BMP格式的图像压缩成PNG、JPEG两种格式。
获取BMP配置信息的方法例如以下:
public final Config getConfig()
除了支持图像本身的宽高外,Android还支持基于Canvas、DisplayMerics、给定密度的密度伸缩后的宽高。
获得图像本身宽高的方法例如以下:
public final int getWidth()
public final int getHeight()
获得基于Canvas的宽高的方法例如以下:
public int getScaledWidth(Canvas canvas)
public int getScaledHeight(Canvas canvas)
获得基于DIsplayMetrics的宽高的方法例如以下:
public int getScaledWidth(DisplayMetrics metrics)
public int getScaledHeight(DisplayMetrics metrics)
获得给定密度的密度伸缩后的宽高的方法例如以下:
public int getScaledWidth(int targetDensity)
public int getScaledHeight(int targetDensity)
在创建BMP图像后,能够为其填充特定的颜色,方法例如以下:
b.eraseColor(Color.BLACK)
将BMP图像压缩为有损压缩格式的实现例如以下:
Bitmap photo=extras.getParcelable("data");
if(photo!=null){
ByteArrayOutputStream stream =new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.JPEG, 75, stream); //75为图像质量。一般在0~100之间
}
2)Drawable对图像的封装
在Android中,Drawable用来管理各种但是对象,如图像、图形等。Drawable能够用来设置但是对象的边界(Bounds)、填充(Padding)、状态(State,如focused、selected等)和层(0~100)等。
其与视图的差别在于,Drawable无法接收事件。
眼下。Android提供的对象Drawable,如ScaleDrawable、RotateDrawable、ShapeDrawable和TransitionDrawable等。
(1)图像Drawable
图像的Drawable主要有下面几种。
1)AnimationDrawable
AnimationDrawable通经常使用于设置帧动画的场景。
2)BitmapDrawable
BitmapDrawable能够通过资源、Bitmap对象、文件路径和输入流等来构建。
从Bitmap对象中构建BitmapDrawable的示比例如以下:
Bitmap b=BitmapFactory.decodeResource(r, R.drawable.albumart_mp_unknown_list);
mDefaultAlbumIcon=new BirmapDrawable(context.getResource(), b);
从资源中构建BitmapDrawable的示比例如以下:
BitmapDrawable sourceDrawable=(BitmapDrawable)context.getResources().getDrawable(com.android.cts.stub.R.drawable.testimage);
除了Drawable提供的基本方法外,BitmapDrawable还提供了下面比較有价值的方法:
public final Bitmap getBitmap()
public int getOpacity() //获得透明度
public void setAntiAlias(boolean aa) //是否抗锯齿
public void setDither(boolean dither) //是否抗抖动
3)PictureDrawable
PictureDrawable并不想BitmapDrawable那样经常使用。它通常和Picture结合使用,相对Bitmap对象而言,Picture对象小巧得多,它并不存储实际的像素,仅记录画布绘制的过程。
PictureDrawable对象能够从Picture对象和输入流等中获得,也能够通过实时绘制获得。
获得PictureDrawable对象的示比例如以下:
Picture mPicture=new Picture();
Canvas canvas=mPicture.beginRecording(220, 100);
Paint p=new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0x88FF0000);
canvas.drawCircle(50, 50, 40, p);
p.setColor(Color.GREEN);
p.setTextSize(30);
canvas.drawText("Pictures", 60, 60, p);
Drawable mDrawable = new PictureDrawable(mPicture);
(2)特效Drawable
特效Drawable支持图形、图像的伸缩、旋转、形态、变换等。
1)伸缩
伸缩主要依靠ScaleDrawable进行的,其支持布局、宽、高等參数的输入。一个典型的示比例如以下:
ScaleDrawable scaleDrawable=newScaleDrawable(new BitmapDrawable(), Gravity.CENTER, 100, 200);
2)旋转
旋转主要依靠RotateDrawable进行。其支持从资源文件里获取Drawable, 方法例如以下:
Resources resources=mContext.getResources();
mRotateDrawable=(RotateDrawable)resource.getDrawable(R.drawable.rotatedrawable);
mRotateDrawable.setChangingConfigurations(Configuration.KEYBOARD_12KEY);
3)形态
形态主要依靠ShapeDrawable进行。
通过ShapeDrawable,开发人员能够画出矩形、圆等形状。在进行一些特效设计时。ShapeDrawable比較实用。比如,画一个矩形的方法例如以下:
ShapeDrawable mShapeDrawable=new ShapeDrawable(new RectShape);
mShapeDrawable.getPaint().setColor(Color.RED);
Rect bounds=new Rect(50, 5, 90, 25);
mShapeDrawable.setBounds(bounds);
mShapeDrawable.draw(canvas);
4)变换
变换主要通过TransitionDrawable进行。TransitionDrawable是LayerDrawable的一个子类,主要在层1和层2之间进行变换。其本质只是是为层1设置透明度而已。假设開始进行变换,那么运行例如以下方法:
public void startTransition(int durationMillis)
假设希望仅显示层1,那么运行例如以下方法:
public void resetTransition()
和其它Drawable略有不同。TransitionDrawable的资源以transition为标签。
3)解码图像
Android支持从文件、流和资源甚至字节数组等数据源对图形进行解码,在运行解码时。能够配置一些特定的选项,这些选项位于BitmapFactory的Opotions静态子类中,基本的选项包含抖动(inDither)、缩放(inScaled)、採样(inSampleSize),RGB配置(inPreferredConfig)、色深(inDensity)、宽(outWidth)、高(outHeight)、MIME类型(outMineType)等,当中採样表示对原始图像的採样。比如,inSampleSize=4时,输出图像的像素仅为原始图像的1/16。子啊默认情况下,将解码图像的选项设置为去抖、自己主动伸缩等。
(1)基于资源的解码
基于资源的解码主要通过BitmapFactory.decodeResource()进行。
(2)基于字节数组的解码
基于字节数据的解码主要通过BitmapFactory.decodeByteArray()进行。
(3)基于文件解码
基于文件解码主要通过BitmapFactory.decodeFile()进行。
(4)基于流的解码
基于流的解码主要通过BitmapFactory.decodeStream()进行。
4)创建图像
创建图像主要是通过Bitmap实现的。Bitmap支持非常据原始图像通过某些变换、伸缩等创建新的图像,也能够凭空构建图像。创建图像的方法例如以下:
Bitmap mBitmap3=Bitmap.createBitmap(200, 200, Bitmap.Config.ALPHA_8);
对原始图像进行伸缩。进而创建出新图像的方法例如以下:
mBackgroundImageNear=Bitmap.creaeScaledBitmap(mBackgroundImageNear, mCanvasWidth*2, mCanvasHeight, true);
5)图像变换
在Android中进行图像变换须要使用Matrix类。该类中包括了一个3*3的矩阵。专门用于进行图像变换匹配。Matrix没有结构体。必须对其进行初始化。
通过其reset方法。能够得到一个单位矩阵。
另外,通过Matrix能够实现图像的旋转、伸缩、正弦余弦变换、倾斜、平移等特效。
(2)缩略图
在Android中,缩略图有着广泛的应用。能够用于图片浏览器的特效。也能够用于视频的首帧浏览。
依据场景的不同,Android提供了多种获得缩略图的方法。
针对详细的图像,开发人员能够通过BitmapFactory的Options来设置採样大小,依照比例取得缩放图像,方法例如以下:
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize=16;
Bitmap lastPictureThumb=BitmapFactory.decodeByteArray(data, 0, data.length, options);
假设希望取得固定大小的缩放图像。以上方法显然是不合适的。以下是一个取得固定大小的缩放图像的參考实现:
InputStream input=mContext.getContentResolver().openInputStream(mUri);
BitmapFactory.options opt=new BitmapFactory.options();
opt.outWidth=200;
opt.outHeight=200;
BitmapFactory.decodeStream(input, null, opt);
眼下Android提供了两种缩略图模式,一种为MINI_KIND(512像素*384像素)模式,一种为MICRO_KIND(96像素*96像素)模式,以下为提取缩略图的一个演示样例:
ImageView thumbnails=(ImageView)view.findViewById(R.id.thumbnails);
Bitmap bitmap=MediaStore.video.Thumbnails.getThumbnail(getContentResolver(), cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media, _ID)),MediaStore.Video.Thumbnails.MICRO_KIND, null);
thumbnails.setImageBitmap(bitmap);
(3)图像浏览
随着用户体验的提高和系统能力的增强。
在进行图像浏览时,原生态的浏览已经失去了竞争力,以下介绍倒影、缩放、圆角等几种比較有竞争力的特效的实现。
1)倒影效果的实现
public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap){
final int reflectionGap=4;
int width=bitmap.getWidth();
int height=bitmap.getHeight();
Matrix matrix=new Matrix();
matrix.preScale(1, -1);
Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height/2, width, height/2. matrix, false);
Bitmap bitmapWithReflection=Bitmap.createBitmap(width, (height+height/2), Config.ARGB_8888);
Canvas canvas=new Canvas(bitmapWithReflection);
canvas.drawBitmap(bitmap, 0, 0, null);
Paint deafalutPaint=new Paint();
canvas.drawBitmap(0, height, width, height+reflectionGap, deafalutPaint);
canvas.drawBitmap(reflectionImage, 0, height+reflectionGap, null);
Paint paint=new Paint();
LinearGradient shader=new LinearGradient(0, bitmap.getHeight(), 0. bitmapWithReflection.getHeight()+reflectionGap, 0x70ffffff, 0x00ffffff,TileMode.CLAMP);
paint.setShader(shader);
paint,setXfermode(new PorterDuffXfermode(Mode.DST_IN)); //为画笔设置传输模式
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()+reflectionGap, paint);
return bitmapWithReflection;
}
2)缩放效果的实现
缩放效果的实现有多种方式,除了可通过ScaleDrawable实现外。还能够通过Matrix的方式实现。
public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h){
int width=bitmap.getWidth();
int height=bitmap.getHeight();
Matrix matrix=new Matrix();
float scaleWidth=((float)w/width);
float scaleHeight=((float)h/height);
matrix.postScale(scaleWidth, scaleHeight);
Bitmap newbmp=Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
return newbmp;
}
3)圆角图像的实现
圆角图像的实现主要通过图形的处理来进行。以下是一个演示样例:
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx){
Bitmap output=Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas=new Canvas(output);
final int color=0xff424242;
final Paint paint=new Paint();
final Rect rect=new Rect(0, 0, bimap.getWidth(), bitmap.getHeight());
final RectF rectF=new RectF(rect);
paint,setAntiAlias(true);
canvas.drawARGB(0,0,0,0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
4)人脸检測
人脸检測主要在FaceDetector类中进行。
在Android 4.0之前。Android的人脸检測能力尚不强大。对人脸的检測主要是通过对眼睛的检測来实现的。
换句话说,仅仅要具有两仅仅眼睛,有没有鼻子、嘴。FaceDetector都会觉得是人脸。在Android 4.0中。人脸检測用来实现解锁功能,很有特色。
在运行详细的人脸检測时,所採用的算法对图像也有要求,其利用的源数据必须为RGB565格式的Bitmap,图像不能太小。否则会检測不到。图像也不能太大,否则易造成载入异常。检測到的人脸将放置在FaceDetector.Face中。
Android主要通过检測人双眼之间的中间点来推断人脸的区域。从而产生一个聚焦的矩形区域(HighlightView)。因为完好了检測算法,Android人脸识别的速度很高效以至于不得不人为减少对应以保证用户体验。
在Android 4.0中,为了进行人脸检測,能够通过Camera的setFaceDetectionListener()来设置Canvas.FaceDetectionListener监听器。通过这样的方法,可使人脸检測变得很easy。
2.3D图像处理
在Android 3.0中,Google引入了RenderScript,这样在进行3D处理时,开发人员有OpenGL、RenderScript两种工具可供选择。就详细场景而言,是选择OpenGL还是RenderScript作为开发工具。Google提供的建议例如以下:假设开发人员值开发简单的应用,且性能不是关键,或者希望有更好的灵活性和调试支持,则须要考虑OpenGL;假设考虑移植性且不会用到OpenGL的所有功能。则RenderScript是个不错的选择。
(1)OpenGL ES的实现
Open GL ES渲染系统分为Java框架和本地代码两部分。
本地代码主要实现OpenGL ES接口的库,在Java框架层,javax.microedition.khrons.opengles是Java标准的OpenGL ES包。android.opengl包提供了OpenGL ES系统和Android GUI系统之间的接口。
1)GLSurfaceView
GLSurfaceView继承于SuifaceView。是专门用于OpenGL ES渲染的基本视图控件。GLSurfaceView具有下面能力:
管理一个Surface。一个Surface是Android渲染系统进行渲染的基本单位。
管理一个EGL显示。
可将OpenGL ES渲染到Surface上。
通过开发人员自己定义的一个渲染器对象运行实际的渲染。
支持按需或连续的渲染模式(详细为RENDERMODE_WHEN_DIRTY、RENDERMODE_CONTINUOUSLY)。
支持OpenGL ES调试。
SurfaceView是Android渲染系统中一个主要的概念。
2)Renderer
渲染器是运行渲染的实际控件,也是实际开发中的重点。当然详细的实现还要看开发人员在OpenGL ES方面的能力。Android源码中提供了ClearRenderer、CubeRenderer、DemoRenderer、KubeRenderer、MatrixPaletteRenderer、QuakeRenderer、TriangleRenderer、AccelerometerTestRenderer等渲染器作为參考。Renderer接口实现例如以下:
public interface Renderer{
void onSurfaceCreated(GL10 gl, EGLConfig config);
void onSurfaceChanged(GL19 gl, int width, int height);
void onDrawFrame(GL10 gl);
}
(2)RenderScript的实现
RenderScript的原生代码层为开发人员提供了一组能进行高性能3D图形渲染和计算的API,是开发人员能够利用C语言以更底层、更高效的方式实现3D效果。
RenderScript的长处在于:採用了C语言进行设计。能够在多种CPU、GPU架构下执行。可移植性强;RenderScript拥有和OpenGL相近的性能,却提供了OpenGL所没有的设计计算API功能;RenderScript能够简化3D实现。
RenderScript的不足在于:提供了与OpenGL不同的新的API功能,致使学习成本添加。因为RenderScript能够执行在CPU等处理器上,调试比較困难;另外和OpenGL相比。其提供的功能较少。
RenderScript提供给开发人员3套工具:一套基于硬件加速的简单3D渲染API;一套类似于CUDA的对程序猿友好的运算API;以及一种常见的语言C99。
不同于已有的NDK。为了实现跨平台。RenderScript在编译过程中会被编译为中间字节码(LLVM),在运行时才通过JIT技术转化为机器码,这样能够避免开发阶段须要面对某种特定机器架构而带来的各种问题。
1)RenderScript的架构
RenderScript整体上採用的是主从架构。本地代码被虚拟机中的代码控制。Android虚拟机依然控制全部内存和应用的生命周期并在不须要时调用本地的RenderScript代码。从结构上讲,RenderScript自上而下能够分为原生ReaderScript层、反射层、框架层等3层。
原生RenderScript层主要用于高强度计算和图形渲染,位于rs(实现文件)和rsh(头文件)文件里。因为原生RenderScript层的设计目标是能够在不同的CPU、GPU上执行,这使得其无法接入NDK和标准的C语言。进而是原生RenderScript层相对有限。这样的问题主要集中在高性能计算和图形渲染领域。
反射层本质上是一个本地代码的包装器类,使Android架构层能够和本地RenderScript进行交互。该层内容在编译时由编译工具自己主动产生。反射层定义了接入RenderScript的函数和方法。类名为ScriptC_renderscript_file。对象会包括一个RenderScriptGL上下文对象。当中记录了RenderScript渲染状态。
框架层通过反射层实现对本地RenderScript层的控制。
Java层对RenderScript的封装位于Android框架层,其主要实现位于android.renderescript类中。
框架层主要用于处理Activity的生命周期、内存管理等,如有须要,也会将触控和输入事件中转给原生RenderScript层进行处理。框架层中最重要的类为RSSurfaceView。
2)RenderScript应用开发
开发人员能够利用RenderScript进行3D计算和3D图像渲染。进行3D计算和3D图像渲染的差别在于实现特效的算法不同。
为了开发RenderScript应用,须要先实现rs、rsh文件作为渲染的接口,然后创建接入点类和扩展RSSurfaceView视图。
原生RenderScript文件:实现一个原生RenderScript文件很easy。仅仅需实现例如以下几方面:通过预处理命令生命要反射的Java文件包名;通过预处理命令生命RenderScript版本号,眼下仅为“1”;引入头文件rs_graphics.sh;一个运行实际渲染工作的root方法;一个用于root方法运行前初始化工作的init。其它用于RenderScript的变量、指针、结构体。
实现RenderScript接入点:应用层和原生RenderScript层的通信须要借助反射层来进行,反射层在编译后会作为资源文件生成在 es aw文件夹中,文件名称为rs_filename.bc。
扩展RSSurfaceView:为了运行渲染。必须扩展RSSurfaceView视图,创建RenderScript上下文。
3.图形处理
在Android中,图形的处理虽不是普通应用层开发人员关注的重点,但其却是整个UI渲染系统的基石。而这一切都是业界著名的图形渲染引擎Skia造成的。
(1)基本接口
Android的图形处理接口多位于android.graphics包中,对于2D图形。实际的渲染工作是通过框架层的Skia引擎来进行的。
为了在画布上画出各种图形,须要考虑4个方面的要素:用来驻留像素的Bitmap对象、承接画图操作的Canvas对象、画图原语和画笔。
1)Canvas
Canvas能够支持绘制各种各样的图形,默认支持点、线、矩形、圆、文字、图片等图形。当画图操作完毕后。能够调用save方法保存。随后能够调用Canvas的平移、缩放、旋转、错切、裁剪等操作。假设希望恢复Canvas之前保存的状态,则须要调用restore方法。
2)画图原语
画图原语主要包含Rect、Path、Text、Bitmap等,本质上,这些原语均是点、线的组合。
绘制矩形:Canvas提供了drawRect方法。
绘制线:Android支持各种线的组合,通过这些组合。开发人员能够绘制出非常多图形。
绘制文字:Canvas提供了drawText方法。
绘制图像:採用方法drawBitmap。
3)画笔
Paint能够设置各种属性,如ARGB、透明度、抗锯齿、颜色、阴影、光栅化、字体等。
4)渲染特效
在图形处理上。Android支持基于Shader的多种渲染特效。如LinearGradient支持线性渐变、ComposeShader支持混合渐变、BitmapShader支持Bitmap渐变、RadialGradient支持环形渐变、SweepGradient支持梯度渐变等,实现渲染特效的步骤为:
(a)创建渲染对象Shader。
(b)通过setShader方法为Paint设置渲染对象。
(c)运行渲染
(1)线性渐变
LinearGradient对线性渐变的支持主要通过X轴、Y轴、颜色和平铺模式进行,以下是LinearGradient的构造函数:
public LinearGradien(floar x0, float y0, float x1, float y1, int color0, int color1, TileMode tile)
LinearGradient还支持更复杂的分段渐变效果。方法例如以下:
public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode tile)
眼下,Android支持的平铺模式包含CLAMP、REPEAT、MIRROR等。当中CLAMP模式表示假设在预先定义的范围外进行绘制,就反复边界的颜色;REPEAT模式表示沿着渐变方向循环反复;MIRROR模式与REPEAT模式一样都是循环反复。但MIRROR模式会对称反复。
(2)混合渐变
ComposeShader支持混合渐变,即两种渐变的组合。
以下是一个演示样例:
LinearGradient blueGradient=new LinearGradient(0, 0, SIZE, 0,Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP);
LinearGradient blueGradient=new LinearGradient(0, 0,SIZE, Color.GREEN, Color.RED, Shade.TileMode.CLAMP);
ComposeShader shader=new ComposeShader(blueGradient, redGradient, PorterDuff.Mode.SCREEN);
Bitmap bitmap=Bitmap.createBitmap(SIZE, SIZE, Config.ARGB_8888);
Canvas canvas=new Canvas(bitmap);
Paint paint=new Paint();
paint.setShader(shader);
canvas.drawPaint(paint);
(3)Bitmap渐变
BitmapShader主要用来渲染图像,示比例如以下:
Bitmap tile=Bitmap.createBitmap(TILE_WIDTH, TILE_HEIGHT, Config.ARGB_8888);
tile.eraseColor(BORDER_COLOR);
Canvas c=new Canvas(tile);
Paint p=new Paint();
p.setColor(CENTER_COLOR);
c.drawRect(BORDER_WIDTH, BORDER_WIDTH, TILE_WIDTH-BORDER_WIDTH, TILE_HEIGHT-BORDER_WIDTH, p);
BitmapShader shader=new BitmapShader(tile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
Paint paint=new Paint();
Bitmap b=Bitmap.createBitmap(NUM_TILES*TILE_WIDTH-TILE_WIDTH/2, NUM_TILES*TILE_HTIGHT-TILE_HEIGHT/2, Config.ARGB_8888);
b.eraseColor(Color.BLACK);
Canvas canvas=new Canvas(b);
canvas.drawPaint(paint);
(4)环形渐变
RadialGradient支持环形渐变。示比例如以下:
final int[] colors={Color.BLUE, Color.GREEN, Color.RED};
final float[] positions={0f, 0.3f, 1f};
int tolerance=(int)(0xFF/(0.3f*RADIUS)*2);
RadialGradient fg=new RadialGradient(CENTER, CENTER, RADIUS, colors, positions, Shader.TileMode.CLAMP);
Bitmap b=Bitmap.createBitmap(SIZE, SIZE, Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(b);
Pain p=new Paint();
p.setShader(rg);
canvas.drawRect(0, 0, SIZE, SIZE, p);
(5)梯度渐变
SweepGradient支持梯度渐变。示比例如以下:
final int[] colors=new int[] {Color.GREEN, Color.RED};
final float[] positions=new float[]{0f, 1f};
Shader shader=new SweepGradient(CENTER, CENTER, colors[0], colors[1]);
mPaint.setShader(shader);
mCanvas.drawRect(new Rect(0, 0, SIZE, SIZE), mPaint);
(2)Surface渲染系统
在Android中,更底层的渲染是通过Android渲染管理器Surface Flinger进行管理的。Surface Flinger通过管理一系列的Surface并对Surface进行组合。然后借组Skia和OpenGL ES来完毕终于的渲染操作。Surface Flinger的引入使Android的渲染机制显得十分出色。
Android的图形相同採用了C/S架构的机制。server(Surface Flinger)端代码主要由C++编写,client代码则分为两部分,一部分为Java编写的供应用调用的API。 还有一部分(SurfaceComposerClient)为由C++完毕的底层原生实现。
Surface渲染系统例如以下图所看到的:
1)Surface
Android图形系统中一个最重要的概念是Surface。每一个Surface都会创建一个画布(Canvas)对象,每一个画布对象都相应于一个位图(Bitmap)对象。该位图对象中存储着视图(View)及其子类等UI控件的Surface上的内容。
依据场景的不同,Surface能够分为多个类型,如Normal Surface、 Blur Surface、Dim Surface、 PushBuffersSurface等。框架层通过Window传递的标志位(flags)来决定创建的Surface的类型。
在通常情况下。每一个Surface相应两个缓冲:前端缓冲和后端缓冲。后端缓冲即在画布对象上渲染信息时画布对象相应的那个位图对象。
应用通过SurfaceView、ViewRoot等创建Java Surface(同一时候创建Canvas)。并将图形绘制到Surface对象上,终于将其渲染到屏幕上。
Java Surface创建ISurface(BnSurface)并发送命令,比如更新Surface内容到屏幕上。Server端接受这个命令并运行对应操作。
2)Layer
每一个Surface相应一个Layer对象,Surface Fligner负责将各个Layer对象的前端缓冲组合并渲染到屏幕上。
多个Layer构成一个层向量LayerVector。 它包括了当前全部Surface相应的Layer。Surface Flinger依据每一个Layer的Z序(Z-order)把多个Layer组合为一个终于的屏幕上显示的缓冲。
Z序实际上定义了窗体之间的层叠顺序。Z序实际上是相对屏幕坐标而言的。一般屏幕上的全部窗体均有一个坐标系,即原点在左上角。X轴水平向右。Y轴垂直向下的坐标系。Z序是相当于一个假象的Z轴而言的。这个Z轴从屏幕外指向屏幕内。窗体在这个Z轴上的值确定了其Z序。Z序值大的窗体覆盖了Z序值小的窗体。者其实就是Activity栈的实现逻辑。
在LayerVector中,每一个Layer都相应一个Z序值,同一时候通过为Layer设定优先级的方式,是某些Layer能够实现前端显示。最后通过相应的裁剪算法来计算能够被显示的区域。
Layer共同拥有4中模式:Layer、LayerDim、LayerBuffer。这4种模式分别与4种类型的Surface相应。
(1)Layer模式
Layer模式是最常见的一种Layer。从用户角度看。也是最经常使用的全屏窗体相应的模式。
(2)LayerDim模式
LayerDim模式主要用于当前窗体非全屏的情况下,可使可视背景窗体产生一个变暗的透明效果。这是全部对话框都具有的特效。
(3)LayerBlur模式
LayerBlur模式在LayerDim模式的基础上使背景窗体产生模糊的效果。
(4)PushBuffersSurface模式
PushBuffersSurface模式没有渲染缓冲。其Surface的内容必须有外部实体推送过来,通经常使用于摄像头预览、视频播放等场景中。
3)GraphicBuffer
在创建Surface时,系统会为每一个Surface分配两个缓冲;前端缓冲和后端缓冲。不论前端缓冲还是后端缓冲,本质上均为GraphicBuffer对象。依据渲染方式的不同。GraphicBuffer的使用方法能够分为多种,系统会依据缓冲的使用情况设置渲染方式为软件渲染、硬件渲染和硬件组合。
前端缓冲和后端缓冲是依据缓冲所处的位置进行差别的。当缓冲在后端时,系统在该缓冲上进行渲染并完毕后,该缓冲转变为前端缓冲和其它Surface中的Layer重载纹理并显示在屏幕上。
4.动画处理
眼下Android支持3种动画。即补间动画、帧动画、属性动画(属性动画是在Android3.0引入的)。当中补间动画主要採用用于实现Android控件本身的动画效果。帧动画用于实现类似GIF格式动画的效果。
Android暂不支持GIF格式动画,在播放GIF格式动画时仅显示首帧。假设确实须要在Android环境下支持GIF格式动画,变通的方法是通过WebView来载入,仅仅需将URI设为本地GIF资源文件就可以。可是这样要消耗大量的资源。创建一个WebView对象至少须要8MB的RAM。
属性动画在开发游戏应用时比較常见,其能力很强大,差点儿能够用于全部的对象。
(1)补间动画
补间动画(Tween Animation),即通过对场景中的对象不断进行图像变换(透明度、平移、缩放、旋转)产生的动画效果。针对不用的图像变换动画,Android提供了AlphaAnimation、ScaleAnimation、RotateAnimation、TranslateAnimation等4个类的支持。
补间动画并不针对图像,它还支持TextView等视图对象。Android的控件动画效果均是基于补间动画实现的。
最经常使用的控件动画是基于ViewAnimator类进行的,其子类包含ViewFlipper、ViewSwitcher、ImageSwitcher、TextSwitcher等。
对于控件动画,一些常见的动画效果并不须要开发人员自行实现,除了淡入淡出效果外。Android还支持下推、上推、缩水淡化、成长淡化等多种动画效果。
动画的进度是通过插补器来控制的。
眼下,Android支持7种插补器效果,即加速减速插补器、加速插补器、预期插补器、预期超调插补器、弹跳插补器、圆插补器、减速插补器、线性插补器、超调插补器。最简单的是线性插补器,动画的进度依照设定的时间均匀展开。其它的插补器都是非线性插补器。
假设这7中插补器还无法满足开发人员的需求,那么开发人员能够通过实现TimeInterpolator接口自己定义一个插补器。
Animation在运行动画时,监听器AnimationListener可供开发人员使用,可用于监视动画的開始、结束和反复播放等动作。
各种动画特效能够组合到AnimationSet中,从而实现复杂的动画效果。
(2)帧动画
帧动画即按顺序播放一组图像形成的动画。帧动画有两个比較重要的属性。一个是android:oneshot属性,用于设置播放模式(是单次播放还是循环播放);一个是android:duration属性,用于设置每帧的持续时间。单位为毫秒(ms)。帧动画的资源文件位于resanim目录下。
为了连续显示一组图像。产生动画效果,Android须要利用XML资源文件和AnimationDrawable协同工作。
AnimationDrawable的start方法不能再Activity的onCreate方法中调用。这是由于此时图像资源尚未全然载入。假设希望能在Activity启动后马上開始动画,那么能够在Activity的onWindowFocusChanged方法中运行start方法调用。
在Android中,进度条的实现利用的就是帧动画的技术。
(3)属性动画
属性动画在Android3.0中引入,为开发人员提供了更强大的自己定义动画的能力。属性动画在游戏开发中比較常见。
在Android中,眼下定义了3种类型的演讲器:ArgbEvaluator、FloatEvaluator、IntEvaluator。
演进器用于通知动画系统怎样计算给定属性的值。
假设用于动画的属性不是int、float、color类型的,那么开发人员能够扩展TypeEvaluator接口来计算目标对象的属性变化。
Animator提供了创建动画的基本结构。但在通常情况下。因为Animator仅提供最主要的功能,故须要对其进行扩展才干使用。眼下,Android属性系统提供了3种动画机制ValueAnimator、ObjectAnimator、AnimatorSet可供选择。
为了更好地支持动画播放过程中的交互,Animator通过设置监听器Animator.AnimatorListener来监听动画的開始、结束、反复播放、取消等多个状态。
假设开发人员对监听动画的全部状态不感兴趣,则须要考虑通过AnimatorListenerAdapter来处理动画监听。
1)ValueAnimator
ValueAnimator支持整形、浮点型、颜色等类型的动画。
以下是一个浮点型动画的演示样例:
ValueAnimator animation=ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
开发人员能够设置自己定义类型的动画,方法例如以下:
ValueAnimator animation =ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
通过监听器ValueAnimator.AnimatorUpdateListener,开发人员能够监听动画中每一帧的播放。
2)ObjectAnimator
ObjectAnimator是ValueAnimator的子类。其动画实现和ValueAnimator类似,但须要指定对象和对象的属性(作为字符串),方法例如以下:
ObjectAnimator anim=ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
以下是一个为ObjectAnimator设置监听器的演示样例:
ValueAnimatorAnimator fadeAnim=ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter(){
public void onAnimationEnd(Animator animation){
balls.remove(((ObjectAnimator)animation).getTarget());
}
});
3)AnimatorSet
AnimatorSet支持对动画的组合。从而使开发人员能够构建十分复杂的动画。
向AnimatorSet中加入动画的方式很灵活。能够一次加入一个动画,也能够一次加入多个动画。
在AnimatorSet中相同能够设置动画的持续时间、启动延迟、插补器等。
和其它Android控件一样,属性动画也能够通过资源文件的方式进行配置,当中ValueAnimator相应的标签为animator,ObjectAnimator相应的标签为objectAnimator。AnimatorSet相应的标签为set。当中ValueAnimator和ObjectAnimator均能够设置动画的持续时间、起始值、属性类型、延迟、反复次数、反复模式等。另外,ObjectAnmator还能够用于设置属性名。
4)Keyframe
keyframe本质上是<时间,值>对,其能够定义在动画的特定时间状态。
开发人员还能够为keyframe定义插补器,使在前一个keyframe和当前keyframe之间有更复杂的演进,方法例如以下:
public void setInterpolator(TimeInterpolator interpolator)
5)布局动画
针对单个视图对象的动画产生的效果总是有限的,在Android3.0中。还引入了布局动画。其作用域为ViewGroup及其子类。
为了使ViewGroup及其子类应用动画小姑,须要为其设置LayoutTransition属性。方法例如以下:
public void setLayoutTransition(LayoutTransition transition)
通过LayoutTransition属性,开发人员能够针对ViewGroup及其子类的多种状态,如显示(APPEARING)、变化(CHANGE_APPEARING)、变化消失(CHANGE_DISAPPEARING)、消失(DISAPPEARING)等设置动画。
设置动画的方法例如以下:
public void setAnimator(int transitionType, Animatior animator)