【Android开发】范例2-实现简易绘图板

简介:
下面这个实例通过前面学过的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;
	}  

运行效果如图


转载请注明出处:http://blog.csdn.net/acmman/article/details/45460663

相关文章
|
1月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
57 19
|
1月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
65 14
|
1月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
1月前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
38 5
|
1月前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
119 3
|
1月前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
1月前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
1月前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
1月前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
40 0
|
1月前
|
存储 监控 Java
探索安卓开发:从基础到进阶的旅程
在这个数字时代,移动应用已成为我们日常生活的一部分。对于开发者来说,掌握安卓开发不仅是技能的提升,更是通往创新世界的钥匙。本文将带你了解安卓开发的核心概念,从搭建开发环境到实现复杂功能,逐步深入安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的见解和技巧,帮助你在安卓开发的道路上更进一步。
28 0