Android图像篇

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监听器。通过这种方法,可使人脸检测变得非常简单。

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文件非常简单,只需实现如下几方面:通过预处理命令生命要反射的Java文件包名;通过预处理命令生命RenderScript版本,目前仅为“1”;引入头文件rs_graphics.sh;一个执行实际渲染工作的root方法;一个用于root方法执行前初始化工作的init;其他用于RenderScript的变量、指针、结构体。

                         实现RenderScript接入点:应用层和原生RenderScript层的通信需要借助反射层来进行,反射层在编译后会作为资源文件生成在\res\raw目录中,文件名为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架构的机制,服务器(Surface Flinger)端代码主要由C++编写,客户端代码则分为两部分,一部分为Java编写的供应用调用的API, 另一部分(SurfaceComposerClient)为由C++完成的底层原生实现。Surface渲染系统如下图所示:

                 Android图像篇_第1张图片

                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)。帧动画的资源文件位于res\anim文件夹下。

                为了连续显示一组图像,产生动画效果,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)

 

 

 

你可能感兴趣的:(Android)