Android繪制基礎及手寫繪制實例


 

Android二維圖形繪制

  Android提供了一系列用於二維繪制的APIs,當繪制2D圖形時,通常有兩種選擇:

  1.在一個View對象中繪制。繼承View類,在子類的 onDraw()方法中寫入自己定義的繪制代碼。

  2.直接在畫布(Canvas)上繪制。

 

  Canvas是一個管理繪制操作的類。

  Canvas的底層有一個Bitmap,你的繪制實際上是在這個位圖對象上。

  當你需要繪制某些東西的時候,你實際上需要四個基本組件:

  1.一個Bitmap來存放像素。

  2.一個Canvas來調用繪制函數,向位圖中寫入內容。

  3.要繪制的基本圖元,如矩形(Rect), 路徑(Path), 位圖(BitMap),或者文字等。

  4.一個畫筆(Paint),指定了繪制時所用的顏色和樣式等。

 

程序實例:

  這個程序實際是ApiDemos中FingerPaint程序的一小部分,實現了用戶在控件中的自由繪制。

  程序自定義的MyPaintView類繼承了View類,用Path記錄用戶點擊的軌跡,在onDraw()方法中將軌跡畫出。當然這個程序中最重要的是各種觸摸事件的處理。

  其中invalidate()方法的作用是及時調用onDraw()方法進行繪制。

  程序更新:2013/2/28 

  首先,糾正之前犯的一個錯誤,就是在自定義View的子類時,應該將其基類的三個構造函數全都覆寫,並在其中調用基類構造函數后進行初始化。

  之前的做法在整個布局中只有一個控件時沒有問題,但是要使用xml布局文件則會出錯,因為必要的構造方法沒有提供,或沒有在其中調用初始化方法。

  其次,使用了布局文件進行布局,加入了一個TextView顯示歡迎語句,底部加入Clear按鈕可以進行畫面清除。

  關於畫面清除我想到了兩種方法:

  1.重新生成位圖對象;2.Bitmap類中有一個eraseColor函數,利用它把位圖繪制為白色。

  應該是第二種方法比較好吧,畢竟總是重新生成對象,應該會有一些內存清理方面的問題。

  附上代碼(2013/2/18版本):

  自定義View: 

MyPaintView
package com.mengexample.hellofreepaint;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;

import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MyPaintView extends View
{
    private Resources myResources;

    // 畫筆,定義繪制屬性
    private Paint myPaint;
    private Paint mBitmapPaint;

    // 繪制路徑
    private Path myPath;

    // 畫布及其底層位圖
    private Bitmap myBitmap;
    private Canvas myCanvas;

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    // 記錄寬度和高度
    private int mWidth;
    private int mHeight;

    public MyPaintView(Context context)
    {
        super(context);
        initialize();
    }

    public MyPaintView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        initialize();
    }

    public MyPaintView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        initialize();
    }

    /**
     * 初始化工作
     */
    private void initialize()
    {
        // Get a reference to our resource table.
        myResources = getResources();

        // 繪制自由曲線用的畫筆
        myPaint = new Paint();
        myPaint.setAntiAlias(true);
        myPaint.setDither(true);
        myPaint.setColor(myResources.getColor(R.color.purple_dark));
        myPaint.setStyle(Paint.Style.STROKE);
        myPaint.setStrokeJoin(Paint.Join.ROUND);
        myPaint.setStrokeCap(Paint.Cap.ROUND);
        myPaint.setStrokeWidth(12);

        myPath = new Path();

        mBitmapPaint = new Paint(Paint.DITHER_FLAG);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        myBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        myCanvas = new Canvas(myBitmap);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction())
        {
        case MotionEvent.ACTION_DOWN:
            touch_start(x, y);
            invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            touch_move(x, y);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            touch_up();
            invalidate();
            break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);

        // 背景顏色
        // canvas.drawColor(getResources().getColor(R.color.blue_dark));

        // 如果不調用這個方法,繪制結束后畫布將清空
        canvas.drawBitmap(myBitmap, 0, 0, mBitmapPaint);

        // 繪制路徑
        canvas.drawPath(myPath, myPaint);

    }

    private void touch_start(float x, float y)
    {
        myPath.reset();
        myPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y)
    {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
        {
            myPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
    }

    private void touch_up()
    {
        myPath.lineTo(mX, mY);
        // commit the path to our offscreen
        // 如果少了這一句,筆觸抬起時myPath重置,那么繪制的線將消失
        myCanvas.drawPath(myPath, myPaint);
        // kill this so we don't double draw
        myPath.reset();
    }

    /**
     * 清除整個圖像
     */
    public void clear()
    {
        // 清除方法1:重新生成位圖
        // myBitmap = Bitmap
        // .createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        // myCanvas = new Canvas(myBitmap);

        // 清除方法2:將位圖清除為白色
        myBitmap.eraseColor(myResources.getColor(R.color.white));

        // 兩種清除方法都必須加上后面這兩步:
        // 路徑重置
        myPath.reset();
        // 刷新繪制
        invalidate();

    }

}

  Activity:

Activity
package com.mengexample.hellofreepaint;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;

public class PaintActivity extends Activity
{
    Button clearBtn;
    MyPaintView paintView;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // 將自定義的控件類作為整個布局
        //setContentView(new MyPaintView(this));
        
        //使用布局文件
        setContentView(R.layout.activity_paint);
        
        paintView = (MyPaintView) findViewById(R.id.view_paint);
        
        clearBtn = (Button)findViewById(R.id.btn_clear);
        clearBtn.setOnClickListener(new View.OnClickListener()
        {
            
            @Override
            public void onClick(View v)
            {
                paintView.clear();
                
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.activity_paint, menu);
        return true;
    }

}

  布局文件:

activity_paint.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="10" >

        <com.mengexample.hellofreepaint.MyPaintView
            android:id="@+id/view_paint"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" >
        </com.mengexample.hellofreepaint.MyPaintView>

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/text_welcome" />
    </FrameLayout>

    <Button
        android:id="@+id/btn_clear"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:text="@string/btn_clear" />

</LinearLayout>

 

  附上一些顏色值,這里雖然沒怎么用到,但以后可以復用:

colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <color name="red">#f00</color>
    <color name="green">#0f0</color>
    <color name="blue">#0000ff</color>
    <color name="black">#000</color>
    <color name="blue_light">#33b5e5</color>
    <color name="blue_dark">#0099cc</color>
    <color name="purple_light">#aa66cc</color>
    <color name="purple_dark">#9933cc</color>
    <color name="green_light">#99cc00</color>
    <color name="green_dark">#669900</color>
    <color name="yellow_light">#ffbb33</color>
    <color name="yellow_dark">#ff8800</color>
    <color name="red_light">#ff4444</color>
    <color name="red_dark">#cc0000</color>

</resources>

   程序運行如圖(我自己手寫的Hello Wind,比較幼稚,哈~):

  

  程序還可進一步改進,加入調色板、橡皮擦、清除、形狀選擇與繪制、線型選擇等功能,變成一個完善的畫圖程序。

 

參考資料:

  API DEMOS: FingerPaint

  API Guides: Canvas and Drawables

  http://developer.android.com/guide/topics/graphics/2d-graphics.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM