Android笔记二十九. 一款简易画图板开发

一款简易画图板开发

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空)

一、画图板原理

1.直线效果

画图板表面上看起来可以随用户在触摸屏上自由的绘制任意图形,但是实际上当用户在触摸屏上移动时,两次拖动事件发生点的距离很小,多条极短的直线连接起来我们肉眼看起来就是直接了。在触摸屏绘制图形时,每条直线都是从上一次拖动事件发生点画到本次拖动事件的发生点,可以借助于Android提供的Path类来实现。然后,如果程序每次都是从上次拖动事件的发生点绘一条直线到本次拖动事件的发生点,那么用户前面绘制的图形就会丢失。所以,为了保留用户之前绘制的内容,我们引用"双缓冲技术"来处理。

2.双缓冲技术

所谓双缓冲技术,即当程序需要在指定View上进行绘制时,程序并不直接绘制到该View组件上,而是先绘制到一个内存中的Bitmap图片(即为缓冲)上,等到内存中的Bitmap绘制好之后,再一次性地将Bitmap绘制到View组件上。

二、开发思路

为了实现触摸屏画图效果,我们还需要为触摸屏的拖动事件提供响应,即当接触触摸屏时先记录下上一次拖动事件发生的位置并将其作为此次拖动动作的起点;然后,在拖动事件中再次获取接触点的坐标(x,y)并将其作为曲线末端点坐标;最后,当手指离开屏幕时又会产生一个事件调用Canvas的drawPath方法绘制path路径图形并保存到缓冲Bitmap中。双缓冲技术实现:

1.定义一个缓冲Bitmap对象并在该Bitmap实现一个Canvas对象,调用缓冲Bitmap的Canvas向缓冲绘图;

Bitmap cacheBitmap= Bitmap.createBitmap(VIEW_WIDTH,
VIEW_HEIGHT, Config.ARGB_8888);

new Canvas().setBitmap(cacheBitmap)
           //指定Canvas的位图

.drawPath(path, paint);                //将图形绘制到位图中

2.调用View的Canvas将缓冲中的Bitmap对象(假设为cacheBitmap)绘制到View组件上

Paint bmpPaint = new Paint();

new
Canvas().drawBitmap(cacheBitmap, 0, 0, bmpPaint)    //将cacheBitmap绘制到该View组件上

.drawPath(path, paint);                                    //沿着path路径绘制图形

三、源码实现

1.HandDraw.java:继承于View的子类

package com.example.path3;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/*实现:采用双缓冲实现画图板*/
public class DrawView extends View {
 float preX;
 float preY;
 private Path path;
 public Paint paint=null;
 final int VIEW_WIDTH = 320;
 final int VIEW_HEIGHT = 480;
 Bitmap cacheBitmap = null;	 //定义一个内存中的图片,该图片将作为缓存区
 Canvas cacheCanvas = null;	 //定义cacheCanvas上的Canvas对象
 /*---------------------构造方法----------------------------*/
 public DrawView(Context context, AttributeSet set)
 {
  super(context, set);
  path = new Path();
 //1.创建一个与该View相同大小的缓存区
  cacheBitmap = Bitmap.createBitmap(VIEW_WIDTH, VIEW_HEIGHT, Config.ARGB_8888);
 //2.设置cacheCanvas将会绘制到内存中的cacheBitmap上
  cacheCanvas = new Canvas();
  cacheCanvas.setBitmap(cacheBitmap);
 //3.设置画笔(颜色、风格、反锯齿)
  paint = new Paint(Paint.DITHER_FLAG);	 //创建画笔
  paint.setColor(Color.RED);	 //画笔颜色为红色
  paint.setStyle(Paint.Style.STROKE);	 //设置画笔风格
  paint.setAntiAlias(true);	 //反锯齿
  paint.setDither(true);
 }
 /*---------------------监听触摸事件----------------------------*/
 @Override
 public boolean onTouchEvent(MotionEvent event)
 {
  //获取拖动事件的发生位置
  float x = event.getX();
  float y = event.getY();
  switch (event.getAction())
  {
   case MotionEvent.ACTION_DOWN:
     path.moveTo(x, y);
     preX = x;
     preY = y;
     break;
   case MotionEvent.ACTION_MOVE:
    path.quadTo(preX, preY, x, y);
    preX = x;
    preY = y;
    break;
   case MotionEvent.ACTION_UP:
    cacheCanvas.drawPath(path, paint);	//沿着路径绘制图形存放到cacheBitmap中
    path.reset();
    break;
  }
  invalidate();
  return true;	//返回true表明处理方法已经处理该事件
 }
//	/*--------------------绘图-------------------------*/
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  Paint bmpPaint = new Paint();
  //a.将cacheBitmap绘制到该View组件上
  canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint);
  //b.沿着path绘制
  canvas.drawPath(path, paint);
 }
}

2.HandDraw.java:程序主界面

package com.example.path3;

import android.graphics.BlurMaskFilter;
import android.graphics.Color;
import android.graphics.EmbossMaskFilter;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
public class HandDraw extends ActionBarActivity {
 EmbossMaskFilter emboss;
 BlurMaskFilter blur;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        emboss = new EmbossMaskFilter(new float[] {1.5f,1.5f,1.5f}, 0.6f, 6,4.2f);
        blur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
    }
  //装载R.menu.hand_draw对应的菜单并添加到menu中
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.hand_draw, menu);
        return true;
    }
    //菜单被单击后的回调方法
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        DrawView dv = (DrawView)findViewById(R.id.draw);
        //判断单击的是哪个菜单项并有针对性地作出响应
        switch(id)
        {
         //颜色设置
         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);
          break;
         case R.id.width_2:	 //号画笔
          dv.paint.setStrokeWidth(2);
          break;
         case R.id.width_5:	 //号画笔
          dv.paint.setStrokeWidth(5);
          break;

         //模糊效果
         case R.id.blur:
          dv.paint.setMaskFilter(blur);
          break;
         //浮雕效果
         case R.id.emboss:
          dv.paint.setMaskFilter(emboss);
          break;
        }
        return super.onOptionsItemSelected(item);
    }
}

3.menu.xml:菜单布局文件

<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.path3.HandDraw" >
<!-- 定义一组颜色选项菜单项 -->
<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 android:checkableBehavior="single">
<item
android:id="@+id/width_1"
android:title="@string/width1"/>
<item
android:id="@+id/width_2"
android:title="@string/width2"/>
<item
android:id="@+id/width_5"
android:title="@string/width5"/>
</group>
</menu>
</item>
<!-- -->
<item android:id="@+id/blur"
android:title="@string/blur"/>
<!-- 定义一组宽度选项菜单项 -->
<item android:id="@+id/emboss"
android:title="@string/emboss"/>
</menu> 

4.main.xml:主界面布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.path3.HandDraw" >
<com.example.path3.DrawView
android:id="@+id/draw"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="320px"
android:height="480px" />
</RelativeLayout> 

5./res/values/string.xml:字符串资源文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <string name="app_name">简易画板</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="width1">1号画笔</string>
 <string name="width2">2号画笔</string>
 <string name="width5">3号画笔</string>

 <string name="blur">模糊效果</string>
 <string name="emboss">浮雕效果</string>
</resources>

效果演示:

时间: 2024-10-21 02:34:20

Android笔记二十九. 一款简易画图板开发的相关文章

Android学习笔记二十九之SwipeRefreshLayout、RecyclerView和CardView

Android学习笔记二十九之SwipeRefreshLayout.RecyclerView和CardView 前面我们介绍了AlertDialog和几个常用的Dialog,ProgressDialog进度条提示框.DatePickerDialog日期选择对话框和TimePickerDialog时间选择对话框.这一节我们介绍几个新的API控件SwipeRefreshLayout.RecyclerView和CardView,这几个API控件都是google在Android5.0推出的.下面我们来学

【Unity 3D】学习笔记二十九:游戏实例——简单小地图制作

任何的学习,光看不练是学不好的.所以这次就总结回顾下怎么制作MMROPG类游戏中的小地图.在MMROPG类游戏里,主角在游戏世界里走动时,一般在屏幕右上角都会有一个区域来显示当前游戏场景的小地图.主角在游戏世界里走动,小地图里代表着主角的小标记也会随之移动.那怎么实现咧? 首先需要确定两个贴图,第一个是右上角的小地图背景贴图,应该是从Y轴俯视向下截取主角所在的位置大地图.第二个就是主角的位置大贴图.在本例中,因为没有学习unity地图制作,所以地图用一个面对象代替,主角用立方体代替,使用GUI来

Android笔记二十六.Android异步任务处理(AsyncTask)

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 一.引言 我们知道Android的UI线程主要负责处理用户的按键事件.用户触屏事件及屏幕绘图事件等,对于其他的操作尽量不要在UI线程中实现,因为这些操作很有可能会阻塞UI线程,比如一些耗时操作,会导致UI界面停止响应,从而降低了用户的体验.所以,为了避免UI线程失去响应的问题,Android建议将耗时操作放在新线程中完成,但新线程也可能需要动态更新UI组件:比如需要从网上获取一个网页,然后在Te

Android笔记二十五.Android事件Handler消息传递机制

因为Android平台不同意Activity新启动的线程訪问该Activity里的界面控件.这样就会导致新启动的线程无法动态改变界面控件的属性值.但在实际Android应用开发中,尤其是涉及动画的游戏开发中,须要让新启动的线程周期性地改变界面控件的属性值,这就须要借助Handler的消息传递机制实现. 一.Handler类简单介绍 1.功能 Handler类主要有两个作用 (1)在新启动的线程中发送消息; (2)在主线程中获取消息.处理消息.即当须要界面发生变化的时候.在子线程中调用Handle

[傅里叶变换及其应用学习笔记] 二十九. 高维Ш函数修改版

一维Ш函数复习 我们前面(十六课,十七课)已经学习过一维的Ш函数,标准的Ш函数表现为无数个脉冲函数分布在整数点上, 我们定义Ш为 $Ш(x) = \displaystyle{ \sum_{k=-\infty}^{\infty}\delta(x-k) }$ 而Ш函数最为深刻的一个性质就是:Ш的傅里叶变换是它自身 $\mathcal{F}Ш=Ш$ 进一步推广到脉冲间隔为$p$的函数$Ш_p$ $\displaystyle{ Ш_p(x)=\sum_{k=-\infty}^{\infty}\delta

Android笔记二十四.Android基于回调的事件处理机制

如果说事件监听机制是一种委托式的事件处理,那么回调机制则与之相反,对于基于回调的事件处理模型来说,事件源和事件监听器是统一的,或者说事件监听器完全消失了,当用户在GUI控件上激发某个事件时,控件自己特定的方法将会负责处理该事件. 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 一.View类的常见回调方法 为了使用回调机制来处理GUI控件上所发生的事件,需要为该组件提供对应的事件处理方法,而Java又是一种静态语言,我们无法为每个对象动态地添

Android笔记二十八.Android绘图深度解析

Android绘图深度解析 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) Android绘图方法主要有两个步骤: (1)实现一个继承于View组件的类,并重写它的onDraw(Canavas canvas)方法; (2)显示定义的View子类,有两种方法:a.使用一个Activity来显示View子类,即 setContentView(new MyView(this, null));b.在Acitviy的布局文件中增加"包名.View子类&

Android笔记(十九) Android中的Fragment

通常我们使用Activity来展示界面,但是在手机上界面可能显示的很好看,但在平板上,因为平板的屏幕非常大,手机的界面放在平板上可能会出现控件被拉长.控件之间间距变大等问题.为了更好的体验效果,在Activity中嵌入“子Activity”,然后每个“子Activity”又可以拥有自己的布局,于是Fragment登场了. 什么是Fragment? Fragment是Activity界面中的一部分或一种行为,你可以把多个Fragment组合到一个Acvitity中来创建一个多面界面,并且你也可以在

PHP学习笔记二十九【接口】

<?php //定义接口 //接口可以定义属性,但必须是常量而且是public //接口的所有方法必须是public interface Iusb{ public function start(); public function stop(); } //手机类实现接口关键字implements,必须实现这个所有方法 //类可以同时实现多个接口 //一个类可以实现多个接口 implements 接口1,接口2,接口 class Phone implements Iusb{ public func