Android Graphics专题(1)--- Canvas基础

简介:

作为Android Graphics专题的开篇。毫无疑问,我们将讨论Android UI技术的核心概念——Canvas

CanvasAndroid UI框架的基础,在Android的控件体系中。全部容器类、控件类在实现上都依赖于Canvas。界面的绘制实质上都是Canvas绘制的。本文将讨论Canvs的由来。并通过实例展示Canvas的基础使用方法。

对于应用开发而言,我们能够不去深究CanvasAndroid 控件体系的实现细节,但明确Canvas与控件的关联有助于我们更好的使用CanvasAndroid控件体系不是了解的朋友能够參见博文《Android原理揭秘系列之ViewViewGroup》。

先看下Android全部控件的基类——View.java的代码片段:

…
protected void onDraw(Canvas canvas) {
 }
….

熟悉Andriod应用开发的人对onDraw方法一定不会陌生。View基类里onDraw方法里是空的。但请注意,方法传入了形參——Canvas对象,也就是说。Canvas对象是UI体系流程中已经创建好的。我们直接拿来用就可以,一般不须要自己构造。Canvas的典型使用场景是,在自己定义控件重载基类的onDraw方法,并在onDraw方法中通过Canvas绘制我们想要的图形、图片等效果。

我们再看看容器类的基类——ViewGroup.javadispatchDraw方法的代码片段:

protected void dispatchDraw(Canvas canvas) {
         …
            for (int i = 0; i < count; i++) {
             …
                    more |= drawChild(canvas, child, drawingTime);
        } 
          …
}

dispatchDraw方法是ViewGroup分发绘制子View的核心函数,其通过drawChild方法详细绘制各个子View。这里我们仅仅须要注意Canvas对象的出现位置,相同。Canvas作为形參从dispatchDraw方法传入。并传给drawChild方法用以绘制子view

通过ViewViewGroup两个核心函数的代码片段分析。我们可以很清晰的明白Canvas在控件体系中的作用。以及我们接下来将讨论的Canvas使用方法的canvas对象来自何处。

Canvas在概念上能够理解为其他编程语言中的画布,在画布中。我们能够绘制各种图形。也能够绘制图片,更深层次的。如上ViewGroupdispatchDraw方法所描写叙述的。我们能够通过变换Canvas,进而在容器内中自己定义的绘制子控件。

本文仅仅讨论Canvas的基础使用方法,即在自己定义控件重载的onDraw方法中,使用Canvas来绘制主要的图形、图像等基础使用方法。

Android的官方SDK中罗列了Canvas的全部API可点击具体查看

这里罗列下在实际应用开发中用得很普遍的几个API:

1)  绘制BitmapdrawBitmapdrawPicture

2)  绘制颜色:drawColordrawARGB

3)  绘制基本形状:drawPointdrawLinedrawCircledrawArcdrawRectdrawRoundRect

4)  绘制剪切区:drawPathclipPathclipRectclipRegion

5)  变换Canvassaverestoretranslatescalerotateconcat(Matrix matrix)setMatrix(Matrix matrix)

6)  绘制顶点数据:drawVerticesdrawBitmapMesh 

上面的六项基本概况了Canvas的使用得最普遍的API。各API的详细含义和使用方法參见SDK。熟练掌握这些API的功能和使用方法基本能够满足开发须要。

以下通过两个代码演示样例来演示Canvas的基本使用方法。

演示样例1:绘制Bitmap

private static class SampleView extends View {
        private Bitmap mBitmap;
        private Bitmap mBitmap2;
        private Bitmap mBitmap3;
        private Bitmap mBitmap4;
        public SampleView(Context context) {
            super(context);
            setFocusable(true);

            java.io.InputStream is;
            is = context.getResources().openRawResource(R.drawable.beach);
            BitmapFactory.Options opts = new BitmapFactory.Options();
            Bitmap bm;
            opts.inJustDecodeBounds = true;
            bm = BitmapFactory.decodeStream(is, null, opts);
            opts.inJustDecodeBounds = false;    
            opts.inSampleSize = 4;             
            bm = BitmapFactory.decodeStream(is, null, opts);
            mBitmap = bm;  //通过配置參数解码生成Bitmap

           is = context.getResources().openRawResource(R.drawable.frog);
            mBitmap2 = BitmapFactory.decodeStream(is); //通过打开资源ID直接解码图片

            int w = mBitmap2.getWidth();
            int h = mBitmap2.getHeight();
            int[] pixels = new int[w*h];
            mBitmap2.getPixels(pixels, 0, w, 0, 0, w, h);
            mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h,
                                           Bitmap.Config.ARGB_8888);
            //通过缓冲区数据构造Bitmap
            mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h,                                           Bitmap.Config.ARGB_4444);

        }

        @Override
        protected void onDraw(Canvas canvas) {
            canvas.drawColor(0xFFCCCCCC);
            Paint p = new Paint();
            p.setAntiAlias(true); //设置防锯齿
            canvas.drawBitmap(mBitmap, 10, 10, null);
            canvas.drawBitmap(mBitmap2, 10, 170, null);
            canvas.drawBitmap(mBitmap3, 110, 170, null);
            canvas.drawBitmap(mBitmap4, 210, 170, null); //通过drawBitmap绘制图
              
        }
    }

演示样例1通过继承基类View实现了一个绘制Bitmap的自己定义View SampleViewSampleView在构造函数中通过几种不同的方式分别构造了四个不同的Bitmap,在onDraw方法中,通过onDraw方法传入的canvas绘制Bitmap

这样实现的控件在界面上将依据不同的坐标位置绘制出四幅图片的效果。注意,因为Android ViewonDraw方法在界面显示、隐藏、遮挡等非常多场合都会被系统频繁调用,因此,像构造Bitmap这种耗费较大内存资源的工资不应该放在onDraw方法中去运行。

演示样例2:绘制顶点数据实现变形效果

  private static class SampleView extends View {
        private final Paint mPaint = new Paint();
        private final float[] mVerts = new float[10];
        private final float[] mTexs = new float[10];
        private final short[] mIndices = { 0, 1, 2, 3, 4, 1 };

        private final Matrix mMatrix = new Matrix();
        private final Matrix mInverse = new Matrix();

        private static void setXY(float[] array, int index, float x, float y) {
            array[index*2 + 0] = x;
            array[index*2 + 1] = y;
        }

        public SampleView(Context context) {
            super(context);
            setFocusable(true);

            Bitmap bm = BitmapFactory.decodeResource(getResources(),
                                                     R.drawable.beach);
            Shader s = new BitmapShader(bm, Shader.TileMode.CLAMP,
                                        Shader.TileMode.CLAMP);
            mPaint.setShader(s);//通过BitmapShader设置Paint的Shader

            float w = bm.getWidth();
            float h = bm.getHeight();
            // construct our mesh
            setXY(mTexs, 0, w/2, h/2);
            setXY(mTexs, 1, 0, 0);
            setXY(mTexs, 2, w, 0);
            setXY(mTexs, 3, w, h);
            setXY(mTexs, 4, 0, h); //初始化图片纹理映射坐标

            setXY(mVerts, 0, w/2, h/2);
            setXY(mVerts, 1, 0, 0);
            setXY(mVerts, 2, w, 0);
            setXY(mVerts, 3, w, h);
            setXY(mVerts, 4, 0, h);//初始化顶点数据数组

            mMatrix.setScale(0.8f, 0.8f);
            mMatrix.preTranslate(20, 20);
            mMatrix.invert(mInverse); //初始化变形矩阵
        }

        @Override protected void onDraw(Canvas canvas) {
            canvas.drawColor(0xFFCCCCCC); //绘制背景色
            canvas.save();  //变形canvas前先保存canvas现场
            canvas.concat(mMatrix); //设置变换矩阵

            canvas.drawVertices(Canvas.VertexMode.TRIANGLE_FAN, 10, mVerts, 0,
                                mTexs, 0, null, 0, null, 0, 0, mPaint);
                            //绘制当前顶点坐标和纹理坐标决定的图片Paint,得到图片变形效果。

canvas.translate(0, 240);//向下平移canvas canvas.drawVertices(Canvas.VertexMode.TRIANGLE_FAN, 10, mVerts, 0, mTexs, 0, null, 0, mIndices, 0, 6, mPaint); //绘制当前顶点坐标和纹理坐标和索引数组决定的图片Paint,得到另外的图片变形效果。

canvas.restore();//变换完毕后恢复canvas现场 } @Override public boolean onTouchEvent(MotionEvent event) { float[] pt = { event.getX(), event.getY() }; mInverse.mapPoints(pt); // 依据当前的触摸位置变换Marix setXY(mVerts, 0, pt[0], pt[1]); //据触摸的位置变换顶点坐标 invalidate();//刷新界面。触发onDraw方法被再次调用 return true; } }

 演示样例2Canvas的一个比較综合性的使用方法演示样例,用到了canvas的多个API,理解了该演示样例。对Canvas的使用方法基本就达到了比較熟练的程度。该演示样例的一些新的概念如PaintShaderMatrix等概念兴许专题会作有专门的介绍,敬请关注。

演示样例2有例如以下一些知识点:

1) 用户的触摸事件在onTouchEvent中传入,通过传入event參数能够获取当前触摸膜的坐标点,并依据这个坐标位置參数来改变算法的相关參数,进而达到依据不同的坐标位置来达到变形的目的。

2)  调用Viewinvaidate方法能够触发View的重绘流程。重而触发onDraw方法的调用。

3)能够通过canvasdrawColor方法来绘制View的背景色。

4) 因为在onDraw方法中传入的Canvas參数是一个引用。该canvas对象在其它地方还会使用,因此。假设绘制中会改变canvas的几何參数,须要在变换前后採用canvas.save()canvas.restore()方法对来备份和恢复canvas现场。

注意,这两个方法必须成对出现。否则会导致严重的波及问题。

5)  变换canvas的几何參数能够通过concat连接Maxrix矩阵或者translate平移、scalse缩放、rotate旋转等方法来实现。

6)能够通过drawVertices方法绘制具有变形图片的效果,详细变形的样式取决于顶点坐标、纹理坐标、索引数组和Paint设置的BitmapShader

灵活使用该API能够以2DAPI实现近似3D的效果。与drawVertices类似的API还有drawBitmapMesh,能够实现基于网格顶点的复杂3D效果。

演示样例2的执行效果參见下图:

本文是Android Canvas的基础篇,主要讨论Canvas的一些基本概念和经常使用API,兴许文章中将继续涉及Canvas的方方面面。

Android Graphics专题的下一篇文章将聚焦Graphics中用得很普通的概念——Paint。敬请期待。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

我的手机专卖店,欢迎各位看官捧场http://vpclub.octech.com.cn/ztewd/9495.html 

本文为原创文章,转载请注明出处http://blog.csdn.net/droidpioneer








本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5071999.html,如需转载请自行联系原作者

相关文章
|
前端开发 Android开发
Android Canvas之Path操作
Android Canvas之Path操作
208 0
|
前端开发 Android开发 Python
|
前端开发 Android开发 图形学
Android自定义View工具:Paint&Canvas(一)
本文主要讲的是自定义View时我们经常用到的Canvas和Paint,像平时画画一样,我们需要画布和画笔,而Canvas就是画布,Paint就是画笔
|
搜索推荐 Java Linux
Android基础入门教程
Android是一种基于Linux的自由及开放源代码的操作系统,Android 分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和Linux内核层。
206 0
|
XML 存储 移动开发
Android基础 | 关于Activity你应该明白的一切
Activity 作为一个老生常谈的话题,它是我们刚接触Android开发就遇到,虽然已有一段时间开发经验,但谈起完全搞懂Activity相关,不敢妄言,故结合个人理解及书籍参考,简单总结一下 Activity基础相关知识,其中也留出了一些有意思的问题。
145 0
|
XML 存储 安全
Android四大组件全面解析,夯实基础。(下)
Android四大组件 lay a solid foundation 夯实基础
167 0
|
Android开发
Android四大组件全面解析,夯实基础。(中)
Android四大组件 lay a solid foundation 夯实基础
181 0
Android四大组件全面解析,夯实基础。(中)
|
存储 设计模式 前端开发
Android四大组件全面解析,夯实基础。(上)
Android四大组件 lay a solid foundation 夯实基础
108 0
|
安全 Java 调度
Android多线程编程——线程基础
Android沿用了Java的线程模型,一个Android应用在创建的时候会开启一个线程,我们叫它主线程或者UI线程。
209 0
Android多线程编程——线程基础
|
Java Android开发
移动应用程序设计基础——Android环境构建与Activity生命周期
安装智能手机开发相关软件平台,并在此基础上测试Activity的生命周期过程。 5、 完成智能手机开发平台安装、以及相关配置; 6、 并实现Hello World; 7、 添加Log日志,通过Log日志验证Ac 1、 安装JAVA JDK 2、 安装Android Studio,熟悉AS的基本操作,改变AS的字体,显示方式;截图和文字说明。 3、 建立新项目,实现Hello World。说明各个文件的作用,以及各个关键语句的作用或含义,给出程序的运行结果。 4、 设置生命周期的Log日志,分别执行相关操作
237 0
移动应用程序设计基础——Android环境构建与Activity生命周期