吹毛求疵android画图板(1)
吹毛求疵画图板
因为之前java的画图板只是实现了基础功能,所以我想在android上做一个功能完善的画图板。
我们首先要设计界面,现在主流的android 的界面主要是relative layout 和line layout 相互嵌套使用。
另外在android:onClick="cancel" 可以轻松绑定在主界面MainActivity写的方法,轻松快捷,超好用的有木有。组件一般都是先拖上去再在xml中修改代码。另外在布局中,熟练使用 android:layout_weight="1" 我一般先再xml中先写好,然后再在graphical_layout 拉界面调整比例。
然后今天我还知道了如何更改主界面的theme,在AndroidManifest中的application中选择system resours
这是我的界面图:(4寸一般手机是刚好可以铺开的)
布局格式:先是整体用相对布局然后中间嵌套竖直流布局(充满前面一个组件,既充满整个屏幕),然后再在中间加一个imageView横向充满屏幕,下面再加上一个横向线型布局,中间加上各种按钮组件(全部的权重都设为android:layout_weight="1")。下面是代码:
<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:background="@drawable/background" tools:context=".MainActivity" > <LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ImageView android:id="@+id/imageView1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="32.15" android:src="@drawable/backgrand" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="horizontal" ><!--onClick绑定点击方法 --> <Button android:id="@+id/line" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="setLine" android:layout_weight="1" android:text="line" /> <Button android:id="@+id/rectangle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="setRect" android:layout_weight="1" android:text="rect" /> <Button android:id="@+id/pen" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="setPen" android:layout_weight="1" android:text="pen" /> <Button android:id="@+id/cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="cancel" android:text="cancel" /> <Button android:id="@+id/clear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="clear" android:layout_weight="1" android:text="clear" /> </LinearLayout> </LinearLayout> </RelativeLayout>
具体画直线的歩奏这里就先省略。这里为了实现一般画图板的拖动效果,我们首先采用了双缓冲画图技术。就是采用两张Bitmap 用一张bitmap存本来的图,在MotionEvent.ACTION_MOVE:用另一张显示现在正在拖动的的图片。代码如下:
// 如果没有位图,就新建一个if (bitmap == null) {bitmap = Bitmap.createBitmap(imageView.getWidth(),imageView.getHeight(), Config.ARGB_8888);// 宽,高,第一个alpha 是透明度 后面是3原色// 实例化一个canvas对象,通过bitmapcanvas = new Canvas(bitmap);paint.setStyle(Style.STROKE);//画一个空心的paint.setColor(Color.RED);paint.setStrokeWidth(5.0f);canvas.drawRect(0, 0, imageView.getWidth(), imageView.getHeight(), paint);//画入新线//将缓冲图输出imageView.setImageBitmap(bitmap);}// 获取触控方式int action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:x1=event.getX();y1=event.getY();preX=x1;preY=y1;break;case MotionEvent.ACTION_UP:x2=event.getX();y2=event.getY();//设置画笔的属性paint.setColor(Color.BLACK);paint.setStrokeWidth(5.0f);//paint.setStyle(Style.STROKE);//画一个空心的//paint.setStyle(Style.FILL);//画线canvas.drawLine(x1, y1, x2, y2, paint);//输出到位图,再镶嵌到线框line line=new line();line.setAttribute(1);line.setX1(x1);line.setX2(x2);line.setY1(y1);line.setY2(y2);list.add(line);imageView.setImageBitmap(bitmap);break;case MotionEvent.ACTION_MOVE:x2=event.getX();y2=event.getY();//设置一个缓冲位图Bitmap buffer = Bitmap.createBitmap(imageView.getWidth(), imageView.getHeight(), Config.ARGB_8888);Canvas canvas2 = new Canvas(buffer);//新化一个缓冲图canvas2.drawBitmap(bitmap, 0, 0, new Paint());//将以前画的全部写入缓冲区paint.setColor(Color.BLACK);paint.setStrokeWidth(5.0f);canvas2.drawLine(x1, y1, x2, y2, paint);//画入新线//将缓冲图输出imageView.setImageBitmap(buffer);break;}
但是运用这种技术,我们发现手机上可能会有一些不流畅的感觉。但是我们只用一个bitmap将之前存在队列中的图形对象,在move行为结束,新的直线即将即将画出来的时候,先将队列中的图形全部画出。这样就不用每move一下就新建一个对象,减少了内存开销。仅提供思路,有时间再去尝试下。
有了画图板,我们还要做到撤销,要做到撤销为了实现撤销的效果我们每一次画的图都要保存在队列里撤销时,我们删除掉最近一个图形,然后再调用子类中的draw方法,将之前的图形画出来。为了实现这个效果,我们首先要有一个父类(用来做画图存储列表),各个图形对象继承这个父类,当每画一个图形,就把这个图形丢到这个队列中去,子类转换为父类。当点击撤销时,先将父类对象取出(不能直接取出子类),,这时因为在存储时子类转换为父类,所以在JAVA中,子类的方法被隐藏了。再强制转换为子类,就可以调用draw的方法,代码如下:
package com.example.drawboard;import android.graphics.Canvas;import android.graphics.Paint;/** * 所有图形的父类 * @author * */public abstract class paintList {public int getAttribute() {return attribute;}public void setAttribute(int attribute) {this.attribute = attribute;}public float getX1() {return x1;}public void setX1(float x1) {this.x1 = x1;}public float getY1() {return y1;}public void setY1(float y1) {this.y1 = y1;}public float getX2() {return x2;}public void setX2(float x2) {this.x2 = x2;}public float getY2() {return y2;}public void setY2(float y2) {this.y2 = y2;}private int attribute;//属性,1代表1按钮private float x1,y1,x2,y2;/** * 绘制的方法 * @param g */ public abstract void draw(Canvas canvas,Paint paint);
package com.example.drawboard;import android.graphics.Canvas;import android.graphics.Paint;/** * 直线类 * @author * */public class line extends paintList{public line(){//构造时设置标志位this.setAttribute(1);}public void draw(Canvas canvas,Paint paint){canvas.drawLine(this.getX1(), this.getY1(), this.getX2(), this.getY2(), paint);}}
bitmap = Bitmap.createBitmap(imageView.getWidth(),imageView.getHeight(), Config.ARGB_8888);// 宽,高,第一个alpha 是透明度 后面是3原色// 实例化一个canvas对象,通过bitmapcanvas = new Canvas(bitmap);paint.setStyle(Style.STROKE);//画一个空心的paint.setColor(Color.RED);paint.setStrokeWidth(5.0f);canvas.drawRect(0, 0, imageView.getWidth(), imageView.getHeight(), paint);//画入新线//将缓冲图输出if(list.size()!=0){paint.setColor(Color.BLACK);list.remove(list.size()-1);int total=list.size();//还原图片for(int i=0;i<total;i++){paintList T=list.get(i);//如果是直线的话if(T.getAttribute()==1){lineline =(line)T;line.draw(canvas, paint);}if(T.getAttribute()==2){rectrect =(rect)T;rect.draw(canvas, paint);}if(T.getAttribute()==3){penpen =(pen)T;System.out.println("hi");pen.draw(canvas, paint);}}}imageView.setImageBitmap(bitmap);最后我来讲一下,如何做到做到铅笔的效果,正所谓所见接虚,我们看到的一条曲线实际上也是由许多直线所构成。我们每画一个pen对象我们就在pen对象中加上直线队列。撤销时再一个个重画出来,就有我们想要的效果了。
package com.example.drawboard;import java.util.ArrayList;import android.graphics.Canvas;import android.graphics.Paint;/** * 铅笔类 * @author */public class pen extends paintList{public pen(){//构造时设置标志位this.setAttribute(3);}//应该有一个队列存储铅笔的轨迹private ArrayList<line> list=new ArrayList<line>();public ArrayList<line> getList() {return list;}public void setList(ArrayList<line> list) {this.list = list;}@Overridepublic void draw(Canvas canvas, Paint paint) {// TODO Auto-generated method stub//如果是直线的话int total=list.size();for(int i=0;i<total;i++){lineline =list.get(i);line.draw(canvas, paint);}}}
感悟:
命名规则
类名首字母要大写,否则容易与对象名混淆。
另外在android工程中,我们要看到system.out 我们就要在logcat 中 saved filters 旁边的绿色加号按钮,选择要查看的内容的关键字。这点与java不同。
下一篇blog,我就来讲讲自定义组件,如何在画图板上输入文字,以及将这次代码再优化一下。
更多相关文章
- Android中对Log日志文件的分析
- Android(安卓)UI开发专题(一) 之界面设计
- Android开发艺术探索——第二章:IPC机制(上)
- android中的handler的作用
- Android应用程序与SurfaceFlinger服务的连接过程分析
- Android应用程序与SurfaceFlinger服务的连接过程分析
- Android(安卓)Handler机制 - handleMessage究竟在哪个线程执行
- 【Android】注解框架(一)-- 基础知识Java 反射
- Android中对Log日志文件的分析