简单实现Android绘图板

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

运行效果如图

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义View绘图实现拖影动画

    前几天在"Android绘图之渐隐动画"一文中通过画线实现了渐隐动画,但里面有个问题,画笔较粗(大于1)时线段之间会有裂隙,我又改进了一下.这次效果好多了. 先看效果吧: 然后我们来说说基本的做法:  •根据画笔宽度,计算每一条线段两个顶点对应的四个点,四点连线,包围线段,形成一个路径.  •后一条线段的路径的前两个点,取(等于)前一条线段的后两点,这样就衔接起来了. 把Path的Style修改为FILL,效果是这样的: 可以看到一个个四边形,连成了路径. 好啦,现在说说怎样根据两点计

  • Android UI效果之绘图篇(三)

    一. PorterDuffXfermode PorterDuffXfermode类似于数据集合里面的交集并集概念,只是数据里面取的是两个集合,而我们这里取的是两个图形之间的交集并集,我们先来看一张Android API Demo里面一张金典的图 图中的Src和Dst相当于是数学中的两个集合,而在我们的代码中我们可以这样来操作这两个集合 canvas.drawSrc(); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XXX));

  • android图像绘制(七)ClipRect局部绘图/切割原图绘制总结

    杂语:看了很多程序猿都有写博客的习惯,看来我也得练练,不管写的好不好了,学到点什么体会就写写吧. 内容解说:这几天开始学游戏地图制作,今天小小的总结一下Canvas的clipRect()接口的使用. 1)选取要在画布上绘制(刷新)的区域,如图以(x, y)为起点坐标.宽w.高h的区域 2)选择要绘制的图片,不一定是刚好宽高为(w,h),大图就需要切割了(本例子绘制绿色区域) 3)将图片绘制到画布上,使得绿色区域与白色方块重合 4)最后效果图 代码解说: 复制代码 代码如下: canvas.sav

  • Android自定义View绘图实现渐隐动画

    实现了一个有趣的小东西:使用自定义View绘图,一边画线,画出的线条渐渐变淡,直到消失.效果如下图所示: 用属性动画或者渐变填充(Shader)可以做到一笔一笔的变化,但要想一笔渐变(手指不抬起边画边渐隐),没在Android中找到现成的API可用.所以,自己做了一个. 基本的想法是这样的: •在View的onTouchEvent中记录触摸点,生成一条一条的线LineElement,放在一个List中.给每个LineElement配置一个Paint实例. •在onDraw中绘制线段. •变换Li

  • Android 通过onDraw实现在View中绘图操作的示例

    Android绘图操作,通过继承View实现,在onDraw函数中实现绘图.下面是一个简单的例子: 复制代码 代码如下: public class AndroidTest extends Activity {    /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(save

  • Android UI效果之绘图篇(二)

    一.Canvas Canvas中的方法很多,这里我们只挑常用的进行讲解说明 Canvas可以绘制的对象有: 弧线(arcs) canvas. 填充颜色(argb和color) Bitmap 圆(circle和oval) 点(point) 线(line) 矩形(Rect) 图片(Picture) 圆角矩形 (RoundRect) 文本(text) 顶点(Vertices) 路径(path) 绘制弧形 /** * 绘制弧形 * @param oval 绘制区域 * @param startAngle

  • Android UI效果之绘图篇(四)

    上一篇博文说到了Shader的五个子类 - BitmapShader - LinearGradient - RadialGradient - SweepGradient - ComposeShader 其中BitmapShader和LinearGradient已经做了说明,今天就把剩余的三个Shader补充一下 3. RadialGradient 先看下构造方法 /** @param centerX 中心X坐标 @param centerY 中心Y坐标 @param radius 半径 @par

  • Android绘图常用方法汇总

    Android绘图常用方法有哪些,下面一一为大家列举: 1.有关画笔(Paint)的方法 Paint mPaint= new Paint(); mPaint.setAntiAlias(true); // 消除锯齿 mPaint.setStrokeWidth(mCircleWidth); // 设置圆环的宽度 mPaint.setStrokeCap(Paint.Cap.ROUND); // 定义线段断点形状为圆头 mPaint.setAntiAlias(true); // 消除锯齿 mPaint.

  • Android UI效果之绘图篇(一)

    最近准备整理一套关于UI效果的文章,算是对这段时间的一个总结,主要讲Android开发中的UI效果设计模块.初步分为一下几个篇幅: Android XML绘图(Shape.Layer.Selector) Android Canvas绘图(canvas.point.porterDuffXfermode.shader) Android 动画详解 Android 自定义控件 今天就当开胃菜,先讲讲最简单的xml绘图,相信这个大家都用的比较熟,这里就当给大家做一个小文档,当那个参数配置忘了,便于查阅 一

  • Android编程之OpenGL绘图技巧总结

    本文实例讲述了Android编程之OpenGL绘图技巧.分享给大家供大家参考,具体如下: 很久不用OpenGL ES绘图,怕自己忘记了,于是重新复习一遍,顺便原理性的东西总结如下: 1. Android 3D坐标系统 如图: Android的三维坐标系统中: 坐标原点位于中央, X轴从左向右延伸,原点左边的值为负数,右边为正数: Y轴从下向上延伸,原点下边的值为负数,上边为正数: Z轴屏幕里面向外面延伸,屏幕里面为负数,外面为正数. 2. 开发工具(OpenGL和OpenGL ES)介绍 Ope

随机推荐