简单实现Android绘图板

作者:光仔December 时间:2021-09-11 06:36:23 

本文这个实例通过前面学过的Paint、Canvas等2D绘画技术来实现一个简单的Android的绘图板。

具体实现代码:

创建一个名为DrawView的类,该类继承自android.view.View类。在该类中,首先定义程序中所需的属性,然后添加构造方法,并重写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;//定义一个内存中的图片,该图片将作为缓冲区
Canvas cacheCanvas=null;//定义cacheBitmap上的Canvas对象
/*
* 功能:构造方法
* */
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);

}

/*
* 功能:重写onDraw方法
* */
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

}
}

创建布局文件,选择帧布局,并加入上面创建的继承了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类的构造方法中,首先获取屏幕的高度和宽度,并创建一个与该View相同大小的缓存区,然后创建一个新的画面,并实例化一个路径,再将内存中的位图绘制到cacheCanvas中,最后实例化一个画笔,并设置画笔的相关属性。
关键代码如下:


/*
* 功能:构造方法
* */
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
view_width=context.getResources().getDisplayMetrics().widthPixels;//获取屏幕宽度
view_height=context.getResources().getDisplayMetrics().heightPixels;//获取屏幕高度
//创建一个与该View相同大小的缓存区
cacheBitmap=Bitmap.createBitmap(view_width,view_height,Config.ARGB_8888);
cacheCanvas=new Canvas();//创建一个新的画布
path=new Path();
//在cacheCanvas上绘制cacheBitmap
cacheCanvas.setBitmap(cacheBitmap);
paint=new Paint(Paint.DITHER_FLAG);//Paint.DITHER_FLAG防抖动的
paint.setColor(Color.RED);
//设置画笔风格
paint.setStyle(Paint.Style.STROKE);//设置填充方式为描边
paint.setStrokeJoin(Paint.Join.ROUND);//设置笔刷转弯处的连接风格
paint.setStrokeCap(Paint.Cap.ROUND);//设置笔刷的图形样式(体现在线的端点上)
paint.setStrokeWidth(1);//设置默认笔触的宽度为1像素
paint.setAntiAlias(true);//设置抗锯齿效果
paint.setDither(true);//使用抖动效果
}

在DrawView类的onDraw()方法中,添加以下代码,用于设置背景颜色、绘制cacheBitmap、绘制路径以及保存当前绘图状态到栈中,并调用restore()方法恢复所保存的状态,关键代码如下:


/*
* 功能:重写onDraw方法
* */
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(0xFFFFFFFF);//设置背景色
Paint bmpPaint=new Paint();//采用默认设置创建一个画笔
canvas.drawBitmap(cacheBitmap, 0, 0,bmpPaint);//绘制cacheBitmap
canvas.drawPath(path, paint);//绘制路径
canvas.save(Canvas.ALL_SAVE_FLAG);//保存canvas的状态
//恢复canvas之前保存的状态,防止保存后对canvas执行的操作对后续的绘制有影响
canvas.restore();
}

在Draw类中,重写onTouchEvent()方法,为该视图添加触摸事件 * ,在该方法中,首先获取触摸事件发生的位置,然后用switch语句对事件的不同状态添加响应代码,最后调用invalidate()方法更新视图。具体代码如下:


@Override
public boolean onTouchEvent(MotionEvent event) {
//获取触摸事件发生的位置
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贝塞尔曲线,实现平滑曲线(对比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,表明处理方法已经处理该事件
}

编写clear()方法,用于实现橡皮擦功能,具体代码如下:


public void clear(){
//设置图形重叠时的处理方式
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//设置笔触的宽度
paint.setStrokeWidth(50);
}

编写保存当前绘图的save方法,在该方法中,调用saveBitmap()方法将当前绘图保存为PNG图片。save()方法的具体代码如下:


public void save(){
try{
 saveBitmap("myPitcture");
}catch(IOException e){
 e.printStackTrace();
}

}

编写保存绘制好的位图的方法saveBitmap(),在该方法中,首先在SD卡上创建一个文件,然后创建一个文件输出流对象,并调用Bitmap类的compress()方法将绘图内容压缩为PNG格式输出到刚刚创建的文件输出流对象中,最后将缓冲区的数据全部写出到输出流中,并关闭文件输出流对象。saveBitmap()方法的具体代码如下:


private void saveBitmap(String fileName) throws IOException {
File file=new File(getSDPath()+fileName+".png");
file.createNewFile();
FileOutputStream fileOS=new FileOutputStream(file);
//将绘图内容压缩为PNG格式输出到输出流对象中
cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOS);
fileOS.flush();//将缓冲区中的数据全部写出到输出流中
fileOS.close();//关闭文件输出流对象
}

//获得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文件中赋予相应的权限,具体代码入下:


<!-- 执行SD卡检查的权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- SD卡写入权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

在res目录中,创建一个menu目录,并在该目录中创建一个名称为toolsmenu.xml的菜单资源文件,在该文件中编写实例中所应用的功能菜单,关键代码如下:


<?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>

在默认创建的MainActivity中,为实例添加选项菜单。
首先,重写onCreatOptionsMenu()方法,在该方法中,实例化一个MenuInflater对象,并调用该对象的inflate方法解析toolsmenu.xml的菜单资源文件。具体代码如下:


/*
* 创建选项菜单
* */
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflator=new MenuInflater(this);
inflator.inflate(R.menu.toolsmenu, menu);
return super.onCreateOptionsMenu(menu);
}

然后,重写onOptionsItemSelected方法,分别对各个菜单项被选择时做出相应的处理,具体代码如下:


/*
* 当菜单项被选择时,做出相应的处理
* */
@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);//设置笔的颜色为红色
 item.setChecked(true);
 break;
 case R.id.green:
 dv.paint.setColor(Color.GREEN);//设置笔的颜色为绿色
 item.setChecked(true);
 break;
 case R.id.blue:
 dv.paint.setColor(Color.BLUE);//设置笔的颜色为蓝色
 item.setChecked(true);
 break;
 case R.id.width_1:
 dv.paint.setStrokeWidth(1);//设置笔触的宽度为1像素
 break;
 case R.id.width_2:
 dv.paint.setStrokeWidth(5);//设置笔触的宽度为5像素
 break;
 case R.id.width_3:
 dv.paint.setStrokeWidth(10);//设置笔触的宽度为10像素
 break;
 case R.id.clear:
 dv.clear();//擦除绘画
 break;
 case R.id.save:
 dv.save();//保存绘画
 break;
}
return true;
}

运行效果如图

简单实现Android绘图板

来源:http://blog.csdn.net/acmman/article/details/45460663

标签:Android,绘图板
0
投稿

猜你喜欢

  • java JVM原理与常识知识点

    2022-06-24 16:26:50
  • C#正则表达式分解和转换IP地址实例(C#正则表达式大全 c#正则表达式语法)

    2023-07-17 07:11:25
  • c#快速写本地日志方法

    2021-08-24 09:31:17
  • SpringBoot接口中如何直接返回图片数据

    2023-04-22 13:10:24
  • Java 1.8使用数组实现循环队列

    2022-02-11 04:00:10
  • Java Swing组件布局管理器之FlowLayout(流式布局)入门教程

    2021-11-08 05:33:06
  • Android 调用系统相机拍摄获取照片的两种方法实现实例

    2022-01-19 21:22:23
  • Java 实战范例之员工管理系统的实现

    2023-03-31 08:09:33
  • Java序列化与反序列化的实例分析讲解

    2022-09-16 05:58:39
  • Android 四种获取屏幕宽度的方法总结

    2021-12-09 04:31:23
  • Android开发Jetpack组件LiveData使用讲解

    2023-03-21 09:27:49
  • java实现网站微信扫码支付

    2023-06-18 18:58:19
  • Android仿360悬浮小球自定义view实现示例

    2021-10-22 10:16:14
  • SpringBoot 实现自定义的 @ConditionalOnXXX 注解示例详解

    2023-04-04 03:37:18
  • 基于C#实现的三层架构实例

    2023-09-09 11:39:33
  • java web实现分页查询实例方法

    2022-12-18 18:07:57
  • C++中auto_ptr智能指针的用法详解

    2023-05-21 02:14:05
  • 解决idea中yml文件图标问题及自动提示失效的情况

    2021-06-08 14:59:42
  • Java中ClassLoader类加载学习总结

    2022-08-26 18:13:42
  • Android实现上传文件功能的方法

    2022-01-18 11:01:02
  • asp之家 软件编程 m.aspxhome.com