`

View实现涂鸦、撤销以及重做功能

阅读更多
eoe上既然看见了,就备份一下,少许更改了部分代码:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Bitmap.CompressFormat;
import android.os.Environment;
import android.view.MotionEvent;
import android.view.View;

/**
 * View实现涂鸦、撤销以及重做功能
 */

public class TuyaView extends View {

	private Bitmap mBitmap;
	private Canvas mCanvas;
	private Path mPath;
	private Paint mBitmapPaint;// 画布的画笔
	private Paint mPaint;// 真实的画笔
	private float mX, mY;// 临时点坐标
	private static final float TOUCH_TOLERANCE = 4;
	
	// 保存Path路径的集合,用List集合来模拟栈
	private static List<DrawPath> savePath;
	// 记录Path路径的对象
	private DrawPath dp;

	private int screenWidth, screenHeight;

	private class DrawPath {
		public Path path;// 路径
		public Paint paint;// 画笔
	}

	public TuyaView(Context context, int w, int h) {
		super(context);
		screenWidth = w;
		screenHeight = h;

		mBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888);
		// 保存一次一次绘制出来的图形
		mCanvas = new Canvas(mBitmap);

		mBitmapPaint = new Paint(Paint.DITHER_FLAG);
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘
		mPaint.setStrokeCap(Paint.Cap.ROUND);// 形状
		mPaint.setStrokeWidth(5);// 画笔宽度

		savePath = new ArrayList<DrawPath>();
	}

	@Override
	public void onDraw(Canvas canvas) {
		canvas.drawColor(0xFFAAAAAA);
		// 将前面已经画过得显示出来
		canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
		if (mPath != null) {
			// 实时的显示
			canvas.drawPath(mPath, mPaint);
		}
	}

	private void touch_start(float x, float y) {
		mPath.moveTo(x, y);
		mX = x;
		mY = y;
	}

	private void touch_move(float x, float y) {
		float dx = Math.abs(x - mX);
		float dy = Math.abs(mY - y);
		if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
			// 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)
			mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
			mX = x;
			mY = y;
		}
	}

	private void touch_up() {
		mPath.lineTo(mX, mY);
		mCanvas.drawPath(mPath, mPaint);
		//将一条完整的路径保存下来(相当于入栈操作)
		savePath.add(dp);
		mPath = null;// 重新置空
	}
	/**
	 * 撤销的核心思想就是将画布清空,
	 * 将保存下来的Path路径最后一个移除掉,
	 * 重新将路径画在画布上面。
	 */
	public void undo() {
		if (savePath != null && savePath.size() > 0) {
			savePath.remove(savePath.size() - 1);
			redrawOnBitmap();
		}
	}
	/**
	 * 重做
	 */
	public void redo(){
		if (savePath != null && savePath.size() > 0) {
			savePath.clear();
			redrawOnBitmap();
		}
	}
	
	private void redrawOnBitmap(){
		mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
				Bitmap.Config.ARGB_8888);
		mCanvas.setBitmap(mBitmap);// 重新设置画布,相当于清空画布 
		Iterator<DrawPath> iter = savePath.iterator();
		while (iter.hasNext()) {
			DrawPath drawPath = iter.next();
			mCanvas.drawPath(drawPath.path, drawPath.paint);
		}
		invalidate();// 刷新
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX();
		float y = event.getY();

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			// 每次down下去重新new一个Path
			mPath = new Path();
			//每一次记录的路径对象是不一样的
			dp = new DrawPath();
			dp.path = mPath;
			dp.paint = mPaint;
			touch_start(x, y);
			invalidate();
			break;
		case MotionEvent.ACTION_MOVE:
			touch_move(x, y);
			invalidate();
			break;
		case MotionEvent.ACTION_UP:
			touch_up();
			invalidate();
			break;
		}
		return true;
	}

	public void saveToSDCard(){
		String fileUrl = Environment.getExternalStorageDirectory()
				.toString() + "/android/data/test.png";
		try {
			FileOutputStream fos = new FileOutputStream(new File(fileUrl));
			mBitmap.compress(CompressFormat.PNG, 100, fos);
			fos.flush();
			fos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;

public class TuyaActivity extends Activity {

	private TuyaView tuyaView = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);

		tuyaView = new TuyaView(this, dm.widthPixels, dm.heightPixels);
		setContentView(tuyaView);
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {// 返回键
			tuyaView.undo();
			return true;
		}else if(keyCode == KeyEvent.KEYCODE_MENU){//MENU
			tuyaView.redo();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

}



  • 大小: 23.5 KB
分享到:
评论
2 楼 tonytony3 2015-01-06  
哇靠,你妹的,世界因你的分享而更美丽
1 楼 yawin 2012-02-20  
学习了 ,

相关推荐

    自定义控件--涂鸦View

    "自定义控件--涂鸦View"这个主题聚焦于如何在Android中创建一个允许用户自由绘画的自定义视图,即实现一个可以涂鸦的功能。这篇博客(原文链接:http://blog.csdn.net/u010593680/article/details/38539913)深入...

    Android 实现画布涂鸦功能 源码

    通过这个“Android 实现画布涂鸦功能 源码”项目,开发者不仅可以学习到如何在Android上实现基本的绘图功能,还能了解到如何处理触摸事件、优化绘图性能以及实现高级特性,如颜色选择、橡皮擦和撤销/重做。...

    仿qq实现图文混排以及涂鸦等功能.zip

    在Android开发中,实现...通过学习和分析这个仿QQ的项目源码,开发者不仅可以掌握图文混排和涂鸦功能的实现,还能深入理解Android图形系统、事件处理机制以及自定义View的相关知识,对提升Android开发能力大有裨益。

    仿qq实现图文混排以及涂鸦等功能

    在“涂鸦”部分,开发者通常会使用Canvas和Paint类来构建一个画板,让用户能够自由绘制线条、填充颜色,并提供橡皮擦、撤销、重做等操作。为了保存和发送涂鸦内容,可以将绘制的图形序列化为Bitmap,然后转换为Base...

    安卓Android源码——仿qq实现图文混排以及涂鸦等功能.zip

    总结,实现“安卓Android源码——仿qq实现图文混排以及涂鸦等功能”涉及到Android UI设计、触摸事件处理、自定义View、图形绘制等多个核心知识点。通过深入理解和实践,开发者可以构建出更加丰富和有趣的用户体验。

    Android-用于涂鸦的自定义AndroidView

    7. **撤销和重做功能**:为了提供用户友好性,可以实现撤销和重做的功能。这通常需要维护一个历史记录栈,保存用户的每一个绘画动作。当用户触发撤销或重做时,可以从栈中取出相应的动作并执行。 8. **优化性能**:...

    Android高级应用源码-仿qq实现图文混排以及涂鸦等功能.zip

    4. **保存与恢复绘图状态**:为了支持撤销/重做操作,需要保存和恢复绘图的状态,这可以通过序列化`Path`对象,或者使用Android的`SaveLayer`功能来实现。 5. **颜色选择器与画笔粗细调节**:提供用户选择画笔颜色...

    安卓开发-仿qq实现图文混排以及涂鸦等功能.zip

    实现撤销和重做功能,可以利用栈的数据结构,每当用户完成一次绘图操作,就将当前状态压入栈中。撤销操作就是弹出栈顶状态,重做则是将上次被撤销的状态重新压回栈顶并绘制。 6. **手势识别**: 可以使用`...

    android涂鸦功能

    8. **撤销/重做机制**:为了提高用户体验,可以实现撤销和重做功能。这通常涉及到记录每一步的画笔动作,当用户触发撤销或重做时,回退或前进到历史记录中的某一步。 9. **画板大小调整**:允许用户调整画板的大小...

    swift-一款简单的图片涂鸦iOS控件方便实用可以轻松引用到你的工程中

    5. **撤销/重做功能**:许多涂鸦应用都提供了撤销和重做功能,这需要开发者维护一个操作历史栈,并能够根据用户的选择回滚或恢复操作。 6. **图片处理**:控件可能还包含了加载、保存和显示图片的功能。这可能涉及...

    一个简单的安卓涂鸦功能app

    这个“简单的安卓涂鸦功能app”就是这样一个实例,它旨在帮助初学者理解如何在安卓平台上实现画笔交互和图形绘制。下面我们将深入探讨这个应用的关键知识点。 1. **画布(Canvas)与画笔(Paint)**: 在安卓中,...

    android实现简易的涂鸦板

    此外,还需要一个保存和撤销/重做的功能,这可能需要额外的UI元素。 其次,触摸事件处理是实现涂鸦功能的关键。Android中的MotionEvent类用于处理触摸事件,包括ACTION_DOWN(按下)、ACTION_MOVE(移动)和ACTION_...

    Android仿微信图片编辑涂鸦.zip

    此外,撤销和重做功能的实现通常需要维护一个操作历史栈,每次用户绘制一笔或进行其他操作时,都将当前状态压入栈中。撤销时,从栈顶弹出状态并回放之前的操作;重做则将刚刚撤销的状态再次推入栈并回放。 最后,...

    Android画板 涂鸦板

    撤销/重做功能也是常见需求,这需要保存用户的绘图历史,每次绘图操作后更新历史栈,当用户触发撤销或重做时,根据历史记录回滚或推进状态。 最后,为了保存用户的创作,应用需要提供保存和分享功能。保存一般将...

    QT_简易涂鸦板

    撤销/重做功能则可以通过保存每次绘图操作的历史记录来实现,使用栈数据结构(如std::stack)来存储这些操作,当用户触发撤销或重做时,进行相应的操作回退或恢复。 对于文件保存和打开,QT提供了QFile、QIODevice...

    涂鸦画笔与橡皮擦

    可以通过维护一个操作栈来实现撤销与重做功能。每次用户绘制新的线条,就将其作为一个操作压入栈中。当用户点击撤销时,从栈顶弹出一个操作并反向执行;点击重做时,将最近撤销的操作重新执行。 7. **优化性能**:...

    android画写板涂鸦软件源码

    通过维护一个操作历史栈,可以实现撤销和重做功能。每次用户进行绘制操作时,都将当前状态压入栈中;当用户选择撤销或重做时,从栈中弹出或推回状态。 8. **画板背景**: 用户可能希望选择不同的背景,这涉及到...

    一个涂鸦画板控件。。

    3. **撤销与重做**:为了提供用户友好的体验,涂鸦画板通常会有撤销和重做功能。这需要记录每次绘制操作的历史记录,以便在需要时恢复或取消上一步操作。 4. **橡皮擦功能**:除了画笔,还需要一个橡皮擦模式,可以...

    android涂鸦

    此外,为了提供更好的用户体验,涂鸦应用还可以实现撤销/重做功能,这需要维护一个历史栈,每次操作都入栈,撤销时出栈恢复,重做时则从栈顶取出状态重新绘制。 总结来说,基于Android的涂鸦程序开发涵盖了Android...

Global site tag (gtag.js) - Google Analytics