zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    es6-class
    getter与setter
    对象1-属性描述符
    set与map
    vuex状态管理
    VMware虚拟机三种网络模式的区别(上篇)
    VMware虚拟机三种网络模式的区别(上篇)
    如何让FPGA中的SPI与其他模块互动起来
    如何让FPGA中的SPI与其他模块互动起来
    FPGA的SPI从机模块实现
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5071999.html
Copyright © 2011-2022 走看看