【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

相关文章
|
3天前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
22 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
15天前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
115 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
12天前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
36 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
28天前
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
79 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
1月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
36 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
2月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
77 19
|
2月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
91 14
|
2月前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
173 3
|
2月前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
2月前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
85 0

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 6
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 7
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 8
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 9
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
  • 10
    Android学习自定义View(四)——继承控件(滑动时ListView的Item出现删除按钮)