公司的项目里有一个功能是,用户有个涂鸦签字的功能,需要再白色面板上涂鸦,然后上传到又拍云,把又拍云返回的图片url通过post请求上传到服务器。摸索了一段时间,完成了这个功能,并且实现的不错。下面贴出截图跟部分demo:

    

上传到又拍云的那部分demo就不贴了,涉及到很多图片url定义规则还有项目隐私的东西,只贴到 保存图片到本地的,后面上传图片就是很简单啦!

1.自定义的ImageTextView.class

<span style="font-size:14px;">public class ImageEditTextView extends EditText {private Paint mPaint;private Rect mRect;private Context mContext;public ImageEditTextView(Context context) {super(context);mContext = context;}public ImageEditTextView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stubmPaint = new Paint();mPaint.setStyle(Paint.Style.STROKE);mPaint.setColor(Color.BLACK);mContext = context;}public ImageEditTextView(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stubmPaint = new Paint();mPaint.setStyle(Paint.Style.STROKE);mPaint.setColor(Color.BLUE);mContext = context;}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) {System.out.println("left:"+left+".top:"+top+".right:"+right+".bottom:"+bottom);super.onLayout(changed, left, top, right, bottom);}@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stubsuper.onDraw(canvas);WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);int windowWidth = wm.getDefaultDisplay().getWidth();int windowHeight = wm.getDefaultDisplay().getHeight();Paint paint = new Paint();paint.setStyle(Paint.Style.FILL);paint.setColor(Color.BLACK);paint.setColor(Color.WHITE);int paddingTop    = getPaddingTop();int paddingBottom = getPaddingBottom();int scrollY       = getScrollY();        //int scrollX       = getScrollX()+windowWidth;int innerHeight   = scrollY + getHeight() - paddingTop - paddingBottom;int lineHeight    = getLineHeight();int baseLine      = scrollY + (lineHeight - (scrollY % lineHeight));int x = 8;while (baseLine < innerHeight) {//canvas.drawBitmap(line, x, baseLine + paddingTop, paint);canvas.drawLine(x, baseLine + paddingTop,scrollX, baseLine + paddingTop, paint);baseLine += lineHeight;}}}</span>

2.自定义 HandTouchView.class

public class HandTouchView extends View {public Handler mbitmaphHandler;public Handler getMbitmaphHandler() {return mbitmaphHandler;}public void setMbitmaphHandler(Handler mbitmaphHandler) {this.mbitmaphHandler = mbitmaphHandler;}private DisplayMetrics dm;private Bitmap bitmap=null;private Bitmap mBitmap=null;private Bitmap myBitmap ;private Paint mPaint=null;private Canvas mCanvas=null;private Paint mBitmapPaint=null;private Timer timer=null;private savePath sPath;private List<savePath> lists =null;private FingerMatrix fingerMatrix=null;private float Xi,Yi;private Path path=null;public HandTouchView(Context context) {super(context);dm = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);inter(dm.widthPixels, dm.heightPixels);}public HandTouchView(Context context, AttributeSet attrs) {super(context, attrs);dm = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);inter(dm.widthPixels, dm.heightPixels);}public HandTouchView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);dm = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);inter(dm.widthPixels, dm.heightPixels);}/*** 初始化* @param w* @param h*/public void inter(int w,int h){mBitmap =Bitmap.createBitmap(w, h, Config.ARGB_8888);mCanvas = new Canvas(mBitmap);mBitmapPaint = new Paint(Paint.DITHER_FLAG);path = new Path();mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形状mPaint.setStrokeWidth(15);// 画笔宽度mPaint.setColor(FingerMatrix.colorValue);lists = new ArrayList<HandTouchView.savePath>();fingerMatrix = new FingerMatrix();timer = new Timer(true);             //定时器来截取字}public void minter(int w,int h){mBitmap =Bitmap.createBitmap(w, h, Config.ARGB_8888);mCanvas = new Canvas(mBitmap);mBitmapPaint = new Paint(Paint.DITHER_FLAG);path = new Path();mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形状mPaint.setStrokeWidth(15);// 画笔宽度mPaint.setColor(FingerMatrix.colorValue);lists = new ArrayList<HandTouchView.savePath>();fingerMatrix = new FingerMatrix();timer = new Timer(true);}public void RefreshPaint(){path = new Path();mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形 状mPaint.setStrokeWidth(15);// 画笔宽度mPaint.setColor(Color.BLACK);}protected void onDraw(Canvas canvas) {// 设置canvas画布背景为白色canvas.drawColor(Color.WHITE);canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);if (path!=null) {canvas.drawPath(path, mPaint);}super.onDraw(canvas);}/*** 手指 移动事件*/public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:          //按下屏幕时mPaint.setColor(FingerMatrix.colorValue);float x = event.getX();float y = event.getY();if (task!=null) {          //如果等于空task.cancel();           //关闭定时器将原任务从队列中移除task = new TimerTask() {public void run() {Message message = new Message();message.what = 1;handler.sendMessage(message);       //发给handler}};}//Log.i("线程", "触屏时的坐标"+x+"y坐标"+y);if (fingerMatrix==null) {fingerMatrix = new FingerMatrix();//Log.i("线程", "<staRT>fingerMatrix等于空");fingerMatrix.init(x, y);}else {fingerMatrix.getx(x);//Log.i("线程", "<等于空>fingerMatrix等于空");fingerMatrix.getY(y);}path = new Path();sPath =new savePath();sPath.paint = mPaint;        //保存路径sPath.path = path;path.moveTo(x, y);         //开始划线Xi = x;Yi = y;invalidate();         //刷新break;case MotionEvent.ACTION_MOVE:         //移动中float X1 = event.getX();float Y1 = event.getY();if (task!=null) {task.cancel();task = new TimerTask() {public void run() {Message message = new Message();message.what=1;handler.sendMessage(message);}};}if (fingerMatrix!=null) {fingerMatrix.getx(X1);fingerMatrix.getY(Y1);//Log.i("线程", "fingerMatrix不等于空");}float j = Math.abs(X1-Xi);float i = Math.abs(Yi-Y1);if (j>=5||i>=5) {path.quadTo(Xi, Yi, X1, Y1);Xi = X1;Yi = Y1;}invalidate();break;case MotionEvent.ACTION_UP:          //手指松下离开屏幕时//一旦有在屏幕按下抬起操作时,就给SettleAccountActivity发送消息,告知已签字Message message = new Message();message.what=2;message.arg1=1;if (mbitmaphHandler!=null){mbitmaphHandler.sendMessage(message);}float My = event.getX();      //手指离开屏幕时的坐标float Mx = event.getY();if (fingerMatrix!=null) {fingerMatrix.getx(Mx);//Log.i("线程", "离开"+Mx);fingerMatrix.getY(My);}mCanvas.drawPath(path, mPaint);lists.add(sPath);invalidate();if (timer!=null) {if (task!=null) {task.cancel();task = new TimerTask() {public void run() {Message message = new Message();message.what = 1;handler.sendMessage(message);}};timer.schedule(task, 2200, 2200);                //2200秒后发送消息给handler更新Activity}}else {timer = new Timer(true);timer.schedule(task, 2200, 2200);                    //2200秒后发送消息给handler更新Activity}break;default:if (task!=null) {task.cancel();task = new TimerTask() {public void run() {handler.sendEmptyMessage(0);}};}break;}return true;}/*** 处理屏幕显示*/Handler handler = new Handler(){public void handleMessage(Message msg) {switch (msg.what) {          //handler告诉Activity处理数据case 1:                      //CUT_BITMAP_SEND_TO_ACTIVITY//切割图片发送给Activity处理//Log.i("线程", "handler收到");myBitmap = mBitmap;         //获取写好的图片if (fingerMatrix!=null) {         //获取绘制的区域坐标myBitmap = cutBitmap(myBitmap);                 //切割Bitmap图片方法}fingerMatrix=null;if (mbitmaphHandler!=null){Matrix matrix = new Matrix();matrix.postRotate(90); /*翻转90度*/int width = myBitmap.getWidth();int height =myBitmap.getHeight();myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, width, height, matrix, true);saveBiamtImate(myBitmap);}Message message = new Message();message.what=1;Bundle bundle = new Bundle();bundle.putParcelable("bitmap",myBitmap);message.setData(bundle);if (mbitmaphHandler!=null){mbitmaphHandler.sendMessage(message);}//RefreshCanvas();break;}super.handleMessage(msg);}};/*** 发送消息给handler更新ACTIVITY*/TimerTask task = new TimerTask() {public void run() {Message message = new Message();message.what=1;//Log.i("线程", "来了");handler.sendMessage(message);}};/*** 保存路径类* @author Administrator*/class savePath{Paint paint;Path path;}/*** 切割图片方法和更新Activity* @param bnBitmap*/public Bitmap cutBitmap(Bitmap bnBitmap){//最小float minx = fingerMatrix.getMinX();        //最小的x坐标float miny = fingerMatrix.getMinY();       //最小的y坐标//最大float maxy = fingerMatrix.getMaxY();       //最大的Y坐标float maxX = fingerMatrix.getMaxX();       //最大的x坐标//Log.i("","矩阵的坐标信息为:-======最大X:"+maxX+"----====最大Y:"+maxy+"----====最小X:"+minx+"----====最小Y:"+miny);int cutMinX = (int)(minx-15);        //切割的最小值int cutMinY = (int)(miny-15);      //切割的最小值int cutMaxX = (int)(maxX+15);int cutMaxY = (int)(maxy+15);if (cutMinX<=0) {cutMinX=0;}if (cutMinY<=0) {cutMinY=0;}if (cutMaxX>mBitmap.getWidth()) {            //如果X坐标大于图片的宽度cutMaxX =  mBitmap.getWidth()-1;     //那就把图片的宽度付给X坐标 的最大值}if (cutMaxY>mBitmap.getHeight()) {          //如果Y坐标大于图片的宽度cutMaxY = mBitmap.getHeight()-1;     //那就把图片的宽度付给Y坐标 的最大值}int width =(int)(cutMaxX - cutMinX);      //最大的x坐标减去最小的x坐标int height =(int)(cutMaxY-cutMinY);            //最大的Y坐标减去最小的y坐标Bitmap wBitmap =Bitmap.createBitmap(bnBitmap, cutMinX, cutMinY, width, height);        //这个步骤意思是在什么坐标位置创建一张图片if (myBitmap!=null ) {//myBitmap.recycle();myBitmap= null;}//Log.i("线程", "剪切图片成功");return wBitmap;}/*** 保存图片* @param bitmap1*/public boolean saveBiamtImate(Bitmap bitmap1){Date localDate=new Date();String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "AthenaImage";File file=new File(path);if (!file.exists()){file.mkdirs();}//String fileUrl = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"AthenaImage"+ "/save.jpg";String fileUrl ="save.png";try {FileOutputStream fos = new FileOutputStream(new File(file,fileUrl));bitmap1.compress(CompressFormat.PNG, 100, fos);fos.flush();fos.close();Log.i("TAG", "2成功!" + fileUrl);return true;} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return false;}/*** 刷新画布*/public void RefreshCanvas(){//Log.i("线程", "初始化数据并且 刷新画布");if (lists!=null&&lists.size()>0) {lists.remove(lists);if (mBitmap!=null) {mBitmap=null;}path=null;
//          inter(dm.widthPixels,dm.heightPixels);inter(dm.widthPixels,dm.heightPixels);//Log.i("线程", "初始化数据成功");invalidate();}if (task!=null) {task.cancel();}}
}

3.布局文件引用:

<FrameLayoutandroid:layout_below="@+id/tv2"android:layout_width="fill_parent"android:layout_height="wrap_content"android:id="@+id/finger_layout"android:background="@drawable/tv_border_thin"android:layout_margin="@dimen/common_sw320dp_of_10"><com.canvas.ImageEditTextViewandroid:id="@+id/main_image_edit"android:layout_width="fill_parent"android:layout_height="wrap_content"android:background="@color/transparent"android:focusable="true"android:focusableInTouchMode="true"android:gravity="top|left"android:paddingBottom="3dip"android:paddingTop="3dip"android:textSize="50dip" ></com.canvas.ImageEditTextView><com.canvas.HandTouchViewandroid:id="@+id/hand_touch"android:layout_width="fill_parent"android:layout_height="wrap_content"android:background="@color/transparent"></com.canvas.HandTouchView>
</FrameLayout>
4.Activity:
private void initView() {Display display = this.getWindowManager().getDefaultDisplay();mWidth = display.getWidth();mHeight = display.getHeight();touchView=(HandTouchView) include2.findViewById(R.id.hand_touch);imageEditTextView = (ImageEditTextView)include2.findViewById(R.id.main_image_edit);}
public int mWidth,mHeight;public HandTouchView touchView;private Bitmap tempBitmap;
/*** 图片处理方法
*/
Handler handler = new Handler(){public void handleMessage(Message msg) {switch (msg.what) {case 1:Bundle bundle = new Bundle();bundle = msg.getData();tempBitmap = bundle.getParcelable("bitmap");            //获取图片if (null!=tempBitmap) {isGetBitmap=1;//0:未签字;1:已签字Log.i("线程", "前台取到VIEW的值");int w  = tempBitmap.getWidth();                          //获取图片的宽度如果大就缩小如果小就不变int h = tempBitmap.getHeight();//取到当前的行高和行宽int lineHeight =  (int) (mHeight/6f);int lineWidth = (int) (mWidth/4f);if(mHeight == 1280 && mWidth == 800){lineHeight =  (int) (mHeight/5.4f);lineWidth = (int) (mWidth/4f);}tempBitmap =BitmapAmplification(tempBitmap);editInsertBitmap(tempBitmap);               //显示在EditText上try {EditTextSaveImage();tv_submit_c.setEnabled(true);Log.i("TAG","①图片已保存本地");/*if (tv_submit_c.getText().toString().equals("保存")){tv_submit_c.setEnabled(true);Log.i("TAG","①图片已保存本地");}*/} catch (IOException e) {e.printStackTrace();}}else {if (dialog.isShowing()){dialog.dismiss();}Toast.makeText(SettleAccountActivity.this,"提交前请先签字",Toast.LENGTH_SHORT).show();}break;default:isGetBitmap= msg.arg1;//0:未签字;1:已签字break;}super.handleMessage(msg);}};/*** 将bitmap添加到自定义edittext中* @param bitmap*/public void editInsertBitmap(Bitmap bitmap) {SpannableString ss = new SpannableString("1");ImageSpan span = new ImageSpan(bitmap, ImageSpan.ALIGN_BOTTOM);ss.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);imageEditTextView.append(ss);imageEditTextView.setPadding(0, 0, 0, 10);}/*** 删除方法*/public void editImageDelete(){Editable editable = imageEditTextView.getText();int end = imageEditTextView.getSelectionEnd();      //删除最后的一个if (end==1) {imageEditTextView.setText("");return;}if (end<=0) {imageEditTextView.setText("");return;}CharSequence sequence = editable.subSequence(0, end-1);imageEditTextView.setText(sequence);imageEditTextView.setSelection(end - 1);}Bitmap bp;public Bitmap BitmapAmplification(Bitmap path) {if (path!=null) {if(bp!=null){bp=null;}int w = path.getWidth(); //得到图片的宽度int h = path.getHeight();//得到图片的高度Log.v("nnn", "宽"+w+"高"+h);float ww = ((float) 62) / w;float hh = ((float) 45) / h;Matrix matrix = new Matrix();matrix.postScale(ww, hh);Log.v("nnn", "可以www");bp = Bitmap.createBitmap(path, 0, 0, w, h, matrix, true);if (tempBitmap!=null ) {tempBitmap=null;}return bp;}return null;}//private Bitmap savebitmap;public void EditTextSaveImage() throws IOException {//取本地图片String fileUrl = Environment.getExternalStorageDirectory().getAbsoluteFile()+File.separator+"AthenaImage"+File.separator+"save.png";//String fileUrl=Environment.getExternalStorageDirectory().getAbsoluteFile()+File.separator+"iv.jpg";Log.i("TAG", "②取到本地图片地址:fileUrl:" + fileUrl);if (!StringUtils.isEmpty(fileUrl)&&!hasSaved) {saveUPYun(fileUrl);}}
</pre><pre name="code" class="java">
好了,写到这里,其实你的本地就已经存好了图片,剩下来的步骤就很简单啦
注意保存的图片格式是PNG,千万一定要用PNG,不要用JPG啊,因为JPG保存的图片背景是黑色的,有好长一段时间我都掉进了这个坑!具体什么原因,我也不清楚,欢迎交流学习。

涂鸦,Canvas绘图相关推荐

  1. Android--使用Canvas绘图

    前言 除了使用已有的图片之外,Android应用常常需要在运行时根据场景动态生成2D图片,比如手机游戏,这就需要借助于Android2D绘图的支持.本篇博客主要讲解一下Android下使用Canvas ...

  2. canvas绘图工具

    关于canvas绘图,在html页面上太方便了.为什么不用SVG呢?SVG大量的操作DOM元素你会发现网页的内存一下就达到几个G非常恐怖,更别说应用到移动端了.百度取了不少经,什么画板涂鸦只是小把戏缺 ...

  3. 如何使用canvas绘图

    这里是修真院前端小课堂,每篇分享文从 [背景介绍][知识剖析][常见问题][解决方案][编码实战][扩展思考][更多讨论][参考文献] 八个方面深度解析前端知识/技能,本篇分享的是: [如何使用can ...

  4. Android中Canvas绘图之Shader使用图文详解

    概述 我们在用Android中的Canvas绘制各种图形时,可以通过Paint.setShader(shader)方法为画笔Paint设置shader,这样就可以绘制出多彩的图形.那么Shader是什 ...

  5. H5 canvas 绘图

    H5的canvas绘图技术 canvas元素是HTML5中新添加的一个元素,该元素是HTML5中的一个亮点.Canvas元素就像一块画布,通过该元素自带的API结合JavaScript代码可以绘制各种 ...

  6. Canvas绘图在微信小程序中的应用:生成个性化海报

    Canvas绘图在微信小程序中的应用:生成个性化海报 如极客时间的一些实现案例: 基础语法 Canvas本质是一个可以使用脚本(通常为JavaScript)来绘制图形的 HTML 元素,默认大小为30 ...

  7. [转]基于Prototype,利用Canvas绘图实现的web流程图设计器(原型)

    基于Prototype,利用Canvas绘图实现的web流程图设计器(原型) 关键字: javascript prototype script.aculo.us canvas 流程图 web画线 刚才 ...

  8. 每天一个JavaScript实例-canvas绘图

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  9. canvas 绘制直线 并选中_javascript自学记录:canvas绘图

    由于学习js是为爬虫服务,所以canvas绘图学习并不完整. 第15章 使用Canvas绘图 15.1 基本用法 HTML文件中需要有canvas元素,两标签之间的文字是浏览器不支持时显示的. A d ...

  10. Canvas绘图基础(一)

    简单图形绘制 矩形:描边与填充 Canvas的API提供了三个方法,分别用于矩形的清除.描边及填充 clearRect(double x, double y, double w, double h) ...

最新文章

  1. Redis学习笔记——初级
  2. Python教程:多态与多态性
  3. Debug a Server–Side Rendered SAP Spartacus Storefront Using Chrome Dev Tools
  4. [Java基础]IO流小结
  5. 不同用户同时并发测压_教你 7 招,迅速提高服务器并发能力!
  6. (30)FPGA米勒型状态机设计(一段式)(第6天)
  7. oracle 精度异常01438,序列值超过字段精度导致ORA-01438
  8. php连接Access实例
  9. mysql数据库操作函数_MySQL中几个常用的数据库操作函数
  10. 5V转3.3V原理图(AMS1117)
  11. 药企大数据分析应用案例——药企数据分析
  12. shift键计算机功能,电脑shift键常用快捷键使用攻略
  13. [软件更新]Pidgin 2.5.8_IM 即时通信
  14. 移动GIS开发:手机基站定位+离线切片地图(矢量vtpk+栅格tpk)导航安卓APP
  15. java实现天气预报
  16. 利用百度云API接口上传照片进行植物识别
  17. 推荐四个不错的公众号
  18. 深圳“托育”放大招!政府出钱帮你带娃啦!
  19. Golang(二十八)[map-底层数据结构]
  20. C++ 函数实参传递 (argument passing)

热门文章

  1. Grafana监控安装和监控看板创建
  2. 关于for循环的一些题目(二)
  3. 邮件发送失败服务器繁忙,★邮件发送失败的原因和解决方法
  4. HDU oj wod sticks
  5. Python-property
  6. 电脑如何修改图片尺寸大小?怎么图片改大小?
  7. DNS提示错误无法上网怎么办?苹果电脑如何修改DNS?
  8. SpringSecurity安全框架简介
  9. 单片机-stm32-使用cdc类实现vcp(虚拟串口)
  10. gb2312的字符串(包括中午)转16进制字符串以及反转义原始字符