簡單實現(xiàn)Android繪圖板
本文這個實例通過前面學(xué)過的Paint、Canvas等2D繪畫技術(shù)來實現(xiàn)一個簡單的Android的繪圖板。
具體實現(xiàn)代碼:
創(chuàng)建一個名為DrawView的類,該類繼承自android.view.View類。在該類中,首先定義程序中所需的屬性,然后添加構(gòu)造方法,并重寫onDraw(Canvas canvas)方法:
DrawView.java:
package com.example.test;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
public class DrawView extends View{
private int view_width=0;//屏幕的寬度
private int view_height=0;//屏幕的高度
private float preX;//起始點的x坐標
private float preY;//起始點的y坐標
private Path path;//路徑
public Paint paint;//畫筆
Bitmap cacheBitmap=null;//定義一個內(nèi)存中的圖片,該圖片將作為緩沖區(qū)
Canvas cacheCanvas=null;//定義cacheBitmap上的Canvas對象
/*
* 功能:構(gòu)造方法
* */
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/*
* 功能:重寫onDraw方法
* */
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
創(chuàng)建布局文件,選擇幀布局,并加入上面創(chuàng)建的繼承了View的自定義畫圖控件:
res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/frameLayout1" android:orientation="vertical" > <com.example.test.DrawView android:id="@+id/drawView1" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>
在DrawView類的構(gòu)造方法中,首先獲取屏幕的高度和寬度,并創(chuàng)建一個與該View相同大小的緩存區(qū),然后創(chuàng)建一個新的畫面,并實例化一個路徑,再將內(nèi)存中的位圖繪制到cacheCanvas中,最后實例化一個畫筆,并設(shè)置畫筆的相關(guān)屬性。
關(guān)鍵代碼如下:
/*
* 功能:構(gòu)造方法
* */
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
view_width=context.getResources().getDisplayMetrics().widthPixels;//獲取屏幕寬度
view_height=context.getResources().getDisplayMetrics().heightPixels;//獲取屏幕高度
//創(chuàng)建一個與該View相同大小的緩存區(qū)
cacheBitmap=Bitmap.createBitmap(view_width,view_height,Config.ARGB_8888);
cacheCanvas=new Canvas();//創(chuàng)建一個新的畫布
path=new Path();
//在cacheCanvas上繪制cacheBitmap
cacheCanvas.setBitmap(cacheBitmap);
paint=new Paint(Paint.DITHER_FLAG);//Paint.DITHER_FLAG防抖動的
paint.setColor(Color.RED);
//設(shè)置畫筆風(fēng)格
paint.setStyle(Paint.Style.STROKE);//設(shè)置填充方式為描邊
paint.setStrokeJoin(Paint.Join.ROUND);//設(shè)置筆刷轉(zhuǎn)彎處的連接風(fēng)格
paint.setStrokeCap(Paint.Cap.ROUND);//設(shè)置筆刷的圖形樣式(體現(xiàn)在線的端點上)
paint.setStrokeWidth(1);//設(shè)置默認筆觸的寬度為1像素
paint.setAntiAlias(true);//設(shè)置抗鋸齒效果
paint.setDither(true);//使用抖動效果
}
在DrawView類的onDraw()方法中,添加以下代碼,用于設(shè)置背景顏色、繪制cacheBitmap、繪制路徑以及保存當前繪圖狀態(tài)到棧中,并調(diào)用restore()方法恢復(fù)所保存的狀態(tài),關(guān)鍵代碼如下:
/*
* 功能:重寫onDraw方法
* */
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(0xFFFFFFFF);//設(shè)置背景色
Paint bmpPaint=new Paint();//采用默認設(shè)置創(chuàng)建一個畫筆
canvas.drawBitmap(cacheBitmap, 0, 0,bmpPaint);//繪制cacheBitmap
canvas.drawPath(path, paint);//繪制路徑
canvas.save(Canvas.ALL_SAVE_FLAG);//保存canvas的狀態(tài)
//恢復(fù)canvas之前保存的狀態(tài),防止保存后對canvas執(zhí)行的操作對后續(xù)的繪制有影響
canvas.restore();
}
在Draw類中,重寫onTouchEvent()方法,為該視圖添加觸摸事件監(jiān)聽器,在該方法中,首先獲取觸摸事件發(fā)生的位置,然后用switch語句對事件的不同狀態(tài)添加響應(yīng)代碼,最后調(diào)用invalidate()方法更新視圖。具體代碼如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
//獲取觸摸事件發(fā)生的位置
float x=event.getX();
float y=event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//將繪圖的起始點移到(x,y)坐標點的位置
path.moveTo(x, y);
preX=x;
preY=y;
break;
case MotionEvent.ACTION_MOVE:
//保證橫豎繪制距離不能超過625
float dx=Math.abs(x-preX);
float dy=Math.abs(y-preY);
if(dx>5||dy>5){
//.quadTo貝塞爾曲線,實現(xiàn)平滑曲線(對比lineTo)
//x1,y1為控制點的坐標值,x2,y2為終點的坐標值
path.quadTo(preX, preY, (x+preX)/2, (y+preY)/2);
preX=x;
preY=y;
}
break;
case MotionEvent.ACTION_UP:
cacheCanvas.drawPath(path, paint);//繪制路徑
path.reset();
break;
}
invalidate();
return true;//返回true,表明處理方法已經(jīng)處理該事件
}
編寫clear()方法,用于實現(xiàn)橡皮擦功能,具體代碼如下:
public void clear(){
//設(shè)置圖形重疊時的處理方式
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//設(shè)置筆觸的寬度
paint.setStrokeWidth(50);
}
編寫保存當前繪圖的save方法,在該方法中,調(diào)用saveBitmap()方法將當前繪圖保存為PNG圖片。save()方法的具體代碼如下:
public void save(){
try{
saveBitmap("myPitcture");
}catch(IOException e){
e.printStackTrace();
}
}
編寫保存繪制好的位圖的方法saveBitmap(),在該方法中,首先在SD卡上創(chuàng)建一個文件,然后創(chuàng)建一個文件輸出流對象,并調(diào)用Bitmap類的compress()方法將繪圖內(nèi)容壓縮為PNG格式輸出到剛剛創(chuàng)建的文件輸出流對象中,最后將緩沖區(qū)的數(shù)據(jù)全部寫出到輸出流中,并關(guān)閉文件輸出流對象。saveBitmap()方法的具體代碼如下:
private void saveBitmap(String fileName) throws IOException {
File file=new File(getSDPath()+fileName+".png");
file.createNewFile();
FileOutputStream fileOS=new FileOutputStream(file);
//將繪圖內(nèi)容壓縮為PNG格式輸出到輸出流對象中
cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOS);
fileOS.flush();//將緩沖區(qū)中的數(shù)據(jù)全部寫出到輸出流中
fileOS.close();//關(guān)閉文件輸出流對象
}
//獲得SD卡的根目錄
public String getSDPath(){
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState()
.equals(android.os.Environment.MEDIA_MOUNTED); //判斷sd卡是否存在
if (sdCardExist) //如果SD卡存在,則獲取跟目錄
{
sdDir = Environment.getExternalStorageDirectory();//獲取跟目錄
}
return sdDir.toString();
}
在程序中需要向SD卡上保存文件,那么需要在AndroidManifest.xml文件中賦予相應(yīng)的權(quán)限,具體代碼入下:
<!-- 執(zhí)行SD卡檢查的權(quán)限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- SD卡寫入權(quán)限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
在res目錄中,創(chuàng)建一個menu目錄,并在該目錄中創(chuàng)建一個名稱為toolsmenu.xml的菜單資源文件,在該文件中編寫實例中所應(yīng)用的功能菜單,關(guān)鍵代碼如下:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:title="@string/color"> <menu> <!-- 定義一組單選菜單項 --> <group android:checkableBehavior="single"> <!-- 定義子菜單 --> <item android:id="@+id/red" android:title="@string/color_red"/> <item android:id="@+id/green" android:title="@string/color_green"/> <item android:id="@+id/blue" android:title="@string/color_blue"/> </group> </menu> </item> <item android:title="@string/width"> <menu> <!-- 定義子菜單 --> <group> <item android:id="@+id/width_1" android:title="@string/width_1"/> <item android:id="@+id/width_2" android:title="@string/width_2"/> <item android:id="@+id/width_3" android:title="@string/width_3"/> </group> </menu> </item> <item android:id="@+id/clear" android:title="@string/clear"/> <item android:id="@+id/save" android:title="@string/save"/> </menu>
其中values/strings.xml中:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">test</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="color">畫筆顏色</string> <string name="color_red">紅色</string> <string name="color_green">綠色</string> <string name="color_blue">藍色</string> <string name="width">畫筆寬度</string> <string name="width_1">細</string> <string name="width_2">中</string> <string name="width_3">粗</string> <string name="clear">擦除繪畫</string> <string name="save">保存繪畫</string> </resources>
在默認創(chuàng)建的MainActivity中,為實例添加選項菜單。
首先,重寫onCreatOptionsMenu()方法,在該方法中,實例化一個MenuInflater對象,并調(diào)用該對象的inflate方法解析toolsmenu.xml的菜單資源文件。具體代碼如下:
/*
* 創(chuàng)建選項菜單
* */
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflator=new MenuInflater(this);
inflator.inflate(R.menu.toolsmenu, menu);
return super.onCreateOptionsMenu(menu);
}
然后,重寫onOptionsItemSelected方法,分別對各個菜單項被選擇時做出相應(yīng)的處理,具體代碼如下:
/*
* 當菜單項被選擇時,做出相應(yīng)的處理
* */
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//獲取自定義的繪圖視圖
DrawView dv=(DrawView)findViewById(R.id.drawView1);
dv.paint.setXfermode(null);//取消擦除效果
dv.paint.setStrokeWidth(1);//初始化畫筆的寬度
switch(item.getItemId()){
case R.id.red:
dv.paint.setColor(Color.RED);//設(shè)置筆的顏色為紅色
item.setChecked(true);
break;
case R.id.green:
dv.paint.setColor(Color.GREEN);//設(shè)置筆的顏色為綠色
item.setChecked(true);
break;
case R.id.blue:
dv.paint.setColor(Color.BLUE);//設(shè)置筆的顏色為藍色
item.setChecked(true);
break;
case R.id.width_1:
dv.paint.setStrokeWidth(1);//設(shè)置筆觸的寬度為1像素
break;
case R.id.width_2:
dv.paint.setStrokeWidth(5);//設(shè)置筆觸的寬度為5像素
break;
case R.id.width_3:
dv.paint.setStrokeWidth(10);//設(shè)置筆觸的寬度為10像素
break;
case R.id.clear:
dv.clear();//擦除繪畫
break;
case R.id.save:
dv.save();//保存繪畫
break;
}
return true;
}
運行效果如圖

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android通過XListView實現(xiàn)上拉加載下拉刷新功能
這篇文章主要為大家詳細介紹了Android通過XListView實現(xiàn)上拉加載下拉刷新功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
Android 實現(xiàn)徹底退出自己APP 并殺掉所有相關(guān)的進程
這篇文章主要介紹了Android 實現(xiàn)徹底退出自己APP 并殺掉所有相關(guān)的進程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android應(yīng)用內(nèi)存泄漏優(yōu)化指南
內(nèi)存泄漏(Memory Leak)是指應(yīng)用中不再使用的對象因錯誤引用無法被垃圾回收(GC),導(dǎo)致內(nèi)存占用持續(xù)增長,最終可能引發(fā) OOM(Out Of Memory)崩潰?或 應(yīng)用卡頓,以下是 Android 內(nèi)存泄漏的優(yōu)化方案,涵蓋檢測工具、常見場景及解決方案,需要的朋友可以參考下2025-03-03
Android實現(xiàn)倒計時結(jié)束后跳轉(zhuǎn)頁面功能
最近在工作中遇到一個需求,需要在倒計時一段時間后進行跳轉(zhuǎn)頁面,通過查找相關(guān)資料發(fā)現(xiàn)其中涉及的知識還不少,所以分享出來,下面這篇文章主要給大家介紹了關(guān)于Android實現(xiàn)倒計時結(jié)束后跳轉(zhuǎn)頁面功能的相關(guān)資料,需要的朋友可以參考下。2017-11-11
Android實現(xiàn)ViewFlipper圖片動畫滑動
這篇文章主要為大家詳細介紹了Android實現(xiàn)ViewFlipper圖片動畫滑動,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05

