Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Graphics專題(1)--- Canvas基礎

Android Graphics專題(1)--- Canvas基礎

編輯:關於Android編程

作為Android Graphics專題的開篇,毫無疑問,我們將討論Android UI技術的核心概念——Canvas。

Canvas是Android UI框架的基礎,在Android的控件體系中,所有容器類、控件類在實現上都依賴於Canvas,界面的繪制實質上都是Canvas繪制的。本文將討論Canvs的由來,並通過實例展示Canvas的基礎用法。

對於應用開發而言,我們可以不去深究Canvas與Android 控件體系的實現細節,但明白Canvas與控件的關聯有助於我們更好的使用Canvas。Android控件體系不是了解的朋友可以參見博文《Android原理揭秘系列之View、ViewGroup》。

先看下Android所有控件的基類——View.java的代碼片段:

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

熟悉Andriod應用開發的人對onDraw方法一定不會陌生。View基類裡onDraw方法裡是空的,但請注意,方法傳入了形參——Canvas對象,也就是說,Canvas對象是UI體系流程中已經創建好的,我們直接拿來用即可,一般不需要自己構造。Canvas的典型使用場景是,在自定義控件重載基類的onDraw方法,並在onDraw方法中通過Canvas繪制我們想要的圖形、圖片等效果。

我們再看看容器類的基類——ViewGroup.java的dispatchDraw方法的代碼片段:

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。

通過View和ViewGroup兩個核心函數的代碼片段分析,我們能夠非常清晰的明確Canvas在控件體系中的作用,以及我們接下來將討論的Canvas用法的canvas對象來自何處。

Canvas在概念上可以理解為其它編程語言中的畫布,在畫布中,我們可以繪制各種圖形,也可以繪制圖片,更深層次的,如上ViewGroup的dispatchDraw方法所描述的,我們可以通過變換Canvas,進而在容器內中自定義的繪制子控件。

本文只討論Canvas的基礎用法,即在自定義控件重載的onDraw方法中,使用Canvas來繪制基本的圖形、圖像等基礎用法。

Android的官方SDK中羅列了Canvas的所有API,可點擊詳細查看。

這裡羅列下在實際應用開發中用得非常普遍的幾個API:

1) 繪制Bitmap:drawBitmap、drawPicture

2) 繪制顏色:drawColor、drawARGB

3) 繪制基本形狀:drawPoint、drawLine、drawCircle、drawArc、drawRect、drawRoundRect

4) 繪制剪切區:drawPath、clipPath、clipRect、clipRegion

5) 變換Canvas:save、restore、translate、scale、rotate、concat(Matrix matrix)、setMatrix(Matrix matrix)

6) 繪制頂點數據:drawVertices、drawBitmapMesh

上面的六項基本概況了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 SampleView,SampleView在構造函數中通過幾種不同的方式分別構造了四個不同的Bitmap,在onDraw方法中,通過onDraw方法傳入的canvas繪制Bitmap。這樣實現的控件在界面上將根據不同的坐標位置繪制出四幅圖片的效果。注意,由於Android View的onDraw方法在界面顯示、隱藏、遮擋等很多場合都會被系統頻繁調用,因此,像構造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;
        }

    }

示例2是Canvas的一個比較綜合性的用法示例,用到了canvas的多個API,理解了該示例,對Canvas的用法基本就達到了比較熟練的程度。該示例的一些新的概念如Paint、Shader、Matrix等概念後續專題會作有專門的介紹,敬請關注。

示例2有如下一些知識點:

1) 用戶的觸摸事件在onTouchEvent中傳入,通過傳入event參數可以獲取當前觸摸膜的坐標點,並根據這個坐標位置參數來改變算法的相關參數,進而達到根據不同的坐標位置來達到變形的目的。

2) 調用View的invaidate方法可以觸發View的重繪流程,重而觸發onDraw方法的調用。

3)可以通過canvas的drawColor方法來繪制View的背景色。

4) 由於在onDraw方法中傳入的Canvas參數是一個引用,該canvas對象在其他地方還會使用,因此,如果繪制中會改變canvas的幾何參數,需要在變換前後采用canvas.save()、canvas.restore()方法對來備份和恢復canvas現場。注意,這兩個方法必須成對出現,否則會導致嚴重的波及問題。

5) 變換canvas的幾何參數可以通過concat連接Maxrix矩陣或者translate平移、scalse縮放、rotate旋轉等方法來實現。

6)可以通過drawVertices方法繪制具有變形圖片的效果,具體變形的樣式取決於頂點坐標、紋理坐標、索引數組和Paint設置的BitmapShader。靈活使用該API可以以2D的API實現近似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

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved