編輯:Android開發實例
前言
除了使用已有的圖片之外,Android應用常常需要在運行時根據場景動態生成2D圖片,比如手機游戲,這就需要借助於Android2D繪圖的支持。本文主要講解一下Android下使用Canvas進行繪圖的相關操作。最後將以一個簡單的Demo演示如何使用Canvas在ImageView上畫圖並保存。
本文的主要內容:
畫布Canvas
在Android下進行2D繪圖需要Canvas類的支持,它位於"android.graphics.Canvas"包下,直譯過來為畫布的意思,用於完成在View上的繪圖。
Canvas為提供了兩個構造函數:
既然Canvas主要用於2D繪圖,那麼它也提供了很多相應的drawXxx()方法,方便我們在Canvas對象上畫畫,drawXxx()具有多種類型,可以畫出:點、線、矩形、圓形、橢圓、文字、位圖等的圖形,這裡就不再一一介紹了,只介紹幾個Canvas中常用的方法:
畫筆Paint
從上面列舉的幾個Canvas.drawXxx()的方法看到,其中都有一個類型為paint的參數,可以把它理解為一個"畫筆",通過這個畫筆,在Canvas這張畫布上作畫。 它位於"android.graphics.Paint"包下,主要用於設置繪圖風格,包括畫筆顏色、畫筆粗細、填充風格等。
Paint中提供了大量設置繪圖風格的方法,這裡僅列出一些常用的,高級的內容有時間再詳細講解:
使用Canvas繪圖Demo
既然已經簡單講解了Android下2D繪圖的兩個重要類,Canvas和Paint,那麼下面通過一個簡單的Demo來演示一下,這樣加深大家的理解。
在這個Demo中,將實現一個畫圖板的功能,當用戶在觸摸屏上移動的時候,即可在屏幕上繪制任意的圖形。實現手繪功能其實是一種假象:表面上看起來可以隨著用戶在觸摸屏上自由地畫線,實際上依然利用的是Canvas的drawLine()方法畫直線,每條直線都是從上一個移動事件發生點畫到本次移動事件的的發生點。當用戶在觸摸屏上連續移動的時候,每次移動點之間的距離很小,多此極短的連線,肉眼看起來就是一個依照手指觸摸移動的軌跡的連線。
需要指出的是,如果程序每次都只是從上次移動事件的發生點繪制一條直線到本次拖動事件的發生點,那麼當用戶手指一旦離開觸摸屏,再次引發觸摸移動事件的時候,會導致前面繪制的內容被丟失。為了保留用戶之前繪制的內容,程序需要借助於一個"雙緩沖"的機制。
之前講解SurfaceView的時候,有講到SurfaceView會自己維護一個雙緩沖的緩沖區,但是在這裡使用ImageView來展示繪圖效果,它需要我們去維護雙緩沖的機制。當用戶在ImageView上進行"繪制"的時候,程序並不直接"繪制"到該ImageView組件上,而是先繪制到一個內存中的Bitmap對象(緩沖)上,等到內存中的Bitmap繪制好之後,再一次性的將Bitmap對象"繪制"到ImageView上。
在這個Demo中,會監聽ImageView的View.OnTouchListener事件的發生,它主要用戶監聽在View上的觸摸事件。其中需要重寫onTouch()方法,當用戶觸摸View的時候會調用這個方法,以下是它的完整簽名:
boolean onTouch(View v,MotionEvent event)
它的返回值用於指定是否連續捕獲觸摸事件,而在它的參數中,View為當前引發觸摸事件的View,而MotionEvent是當前引發觸摸事件一些屬性,這個類中定義了一系列的靜態常量,用於表示當前觸摸的動作,比如:
Demo中的主要內容已經講解清楚,下面直接上代碼了,代碼中注釋比較詳細,就不再贅述了。
布局代碼:activity_main.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"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <Button
- android:id="@+id/btn_resume"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="重新畫圖" />
- <Button
- android:id="@+id/btn_save"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="保存圖片" />
- </LinearLayout>
- <ImageView
- android:id="@+id/iv_canvas"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </LinearLayout>
- activity_main.xml
實現代碼:MainActivity.java
- package cn.bgtx.canvasdemo;
- import java.io.File;
- import java.io.FileOutputStream;
- import android.net.Uri;
- import android.os.Bundle;
- import android.os.Environment;
- import android.app.Activity;
- import android.content.Intent;
- import android.graphics.Bitmap;
- import android.graphics.Bitmap.CompressFormat;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.View.OnTouchListener;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- private Button btn_save, btn_resume;
- private ImageView iv_canvas;
- private Bitmap baseBitmap;
- private Canvas canvas;
- private Paint paint;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // 初始化一個畫筆,筆觸寬度為5,顏色為紅色
- paint = new Paint();
- paint.setStrokeWidth(5);
- paint.setColor(Color.RED);
- iv_canvas = (ImageView) findViewById(R.id.iv_canvas);
- btn_save = (Button) findViewById(R.id.btn_save);
- btn_resume = (Button) findViewById(R.id.btn_resume);
- btn_save.setOnClickListener(click);
- btn_resume.setOnClickListener(click);
- iv_canvas.setOnTouchListener(touch);
- }
- private View.OnTouchListener touch = new OnTouchListener() {
- // 定義手指開始觸摸的坐標
- float startX;
- float startY;
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction()) {
- // 用戶按下動作
- case MotionEvent.ACTION_DOWN:
- // 第一次繪圖初始化內存圖片,指定背景為白色
- if (baseBitmap == null) {
- baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
- iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
- canvas = new Canvas(baseBitmap);
- canvas.drawColor(Color.WHITE);
- }
- // 記錄開始觸摸的點的坐標
- startX = event.getX();
- startY = event.getY();
- break;
- // 用戶手指在屏幕上移動的動作
- case MotionEvent.ACTION_MOVE:
- // 記錄移動位置的點的坐標
- float stopX = event.getX();
- float stopY = event.getY();
- //根據兩點坐標,繪制連線
- canvas.drawLine(startX, startY, stopX, stopY, paint);
- // 更新開始點的位置
- startX = event.getX();
- startY = event.getY();
- // 把圖片展示到ImageView中
- iv_canvas.setImageBitmap(baseBitmap);
- break;
- case MotionEvent.ACTION_UP:
- break;
- default:
- break;
- }
- return true;
- }
- };
- private View.OnClickListener click = new OnClickListener() {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.btn_save:
- saveBitmap();
- break;
- case R.id.btn_resume:
- resumeCanvas();
- break;
- default:
- break;
- }
- }
- };
- /**
- * 保存圖片到SD卡上
- */
- protected void saveBitmap() {
- try {
- // 保存圖片到SD卡上
- File file = new File(Environment.getExternalStorageDirectory(),
- System.currentTimeMillis() + ".png");
- FileOutputStream stream = new FileOutputStream(file);
- baseBitmap.compress(CompressFormat.PNG, 100, stream);
- Toast.makeText(MainActivity.this, "保存圖片成功", 0).show();
- // Android設備Gallery應用只會在啟動的時候掃描系統文件夾
- // 這裡模擬一個媒體裝載的廣播,用於使保存的圖片可以在Gallery中查看
- Intent intent = new Intent();
- intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
- intent.setData(Uri.fromFile(Environment
- .getExternalStorageDirectory()));
- sendBroadcast(intent);
- } catch (Exception e) {
- Toast.makeText(MainActivity.this, "保存圖片失敗", 0).show();
- e.printStackTrace();
- }
- }
- /**
- * 清除畫板
- */
- protected void resumeCanvas() {
- // 手動清除畫板的繪圖,重新創建一個畫板
- if (baseBitmap != null) {
- baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
- iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
- canvas = new Canvas(baseBitmap);
- canvas.drawColor(Color.WHITE);
- iv_canvas.setImageBitmap(baseBitmap);
- Toast.makeText(MainActivity.this, "清除畫板成功,可以重新開始繪圖", 0).show();
- }
- }
- }
效果展示(隨手塗鴉了個笑臉,發現還是蠻有喜感的,大家將就著看看吧):
源碼下載
Android的事件分發和處理方式 對android開發有一定了解的同學一定或多或少知道android的觸摸事件分發,整個事件的分發消耗流程都可以
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
在5.2.1節和5.2.2節介紹了<a>標簽以及TextView自動識別的特殊文本(網址、電話號、Email等),這些都可以通過單擊來觸發不同的動作。