Android开发&Canvas

来源:互联网 发布:苹果app下载不了软件 编辑:程序博客网 时间:2024/04/29 21:52

若非专业设置游戏界面或者从事美工人员,对于2D图形绘制工具Canvas,只需懂得基本用法就好,技术人员主要是为了实现功能,Canvas也主要是在自定义view及其子类时,绘制界面所用。
canvas绘图被人形象的描述为“画布”,这样理解也无可厚非,画笔“paint”工作的地方就是画布。
有时候需要制作一些图形,现有控件是无法办到的,这时需要我们新建view类,然后将内容画到视图中,最常用的就是重写draw方法,这个方法就是显示界面时候你指定要”画“的东西。

class DrawView extends View {    public DrawView(Context context) {        super(context);    }    @Override    public void draw(Canvas canvas){        super.draw(canvas);//参数传入的画布,明显还有其他地方需要引用,因此不要自己去新建canvas了。        int width =canvas.getWidth();//获取画布的宽;        int height = canvas.getHeight();//获取画布的高;        Paint paint = new Paint();//新建画笔类;        paint.setStyle(Paint.Style.STROKE);//设置画笔类型为stroke,表示只描边        paint.setAlpha(250);//设置——不——透明度,为0表示透明。        paint.setAntiAlias(true);//抗锯齿,为了让画出来的线更平滑。        paint.setStrokeWidth(5);//设置画笔线的粗细,是以像素为单位的,例如红米手机像素为720*1280        paint.setColor(Color.BLUE);//设置画笔颜色        canvas.drawPoint(100,100,paint);//在100,100的坐标画一个点,点的大小可以通过设置画笔线的粗细来调节。   }}

如果在linearLayout中横向排列同时设置了一个控件的width=“wrap_content”和weight,那么会优先设置控件的宽度大于等于wrap_content;
基本的canvas和paint用法,网上可以自己找,很多人都有介绍,这里只是画一个点示范,要注意的是,坐标轴是以你自定义的控件的左上角为坐标原点的,如果你控件不是充满整个屏幕,那么canvas的width和height就不是为屏幕的像素了,另外如果你重写了ontouch方法,会发现坐标会有负值出现

为了学习canvas,我自己制作了一个涂鸦板,手指接触屏幕或者滑动时,会在上面留下轨迹,然后可以将画板以图片文件的形式保存下来,且如果再次打开画板,可以将保存的图片文件再显示到画板上,功能大概就是如此,且看代码。

class DrawView extends View {public DrawView(Context context) {super(context);}@Overridepublic void draw(Canvas canvas){super.draw(canvas);int width =canvas.getWidth();int height = canvas.getHeight();Paint paint = new Paint();paint.setStyle(Paint.Style.STROKE);paint.setAlpha(250);paint.setAntiAlias(true);paint.setStrokeWidth(5);paint.setColor(Color.BLUE);//从这里往下看->File file=new File(Environment.getExternalStorageDirectory() + "/" + "bitmap.jpg");if(file.exists()) {try {Bitmap b=BitmapFactory.decodeFile(file.getPath());canvas.setBitmap(b);} catch (Exception e) {Toast.makeText(DrawBitmap.this,e.toString(),Toast.LENGTH_LONG).show();}}}}

为了省事我把很多的代码删除了,所以有些变量看起来跟没有定义一样,没关系,我们直接看。
当有文件bitmap存在时,就通过BitmapFactory.decodeFile()生成bitmap位图,然后直接通过canvas.setBitmap()方法,把位图设为画布的背景,这时会有异常:
IllegalStateException
查看源码发现是因为setBitmap()方法参数必须是可以改变的位图:

public void setBitmap(@Nullable Bitmap bitmap) {    if (!bitmap.isMutable()) {            throw new IllegalStateException();        }    }}//注意这代码都是截取的,因此发现跟源码不同不要惊异。

然后我改变源码,先把bitmap变为isMutable的,再传入:

canvas.setBitmap(b.copy(Bitmap.Config.ARGB_8888, true));

嗯,它又抛出异常:UnsupportOperationException

没办法,我只能再上百度,有大神说这是因为开启了硬件加速,所以导致异常,于是我再然后在Manifest.xml的application中加上android:hardwareAccelerated=”false”,这是关闭硬件加速,结果没有错误了,界面却又无法显示了,手指接触屏幕也不会有痕迹出现,.还得去查看硬件加速的问题……>>>>>>>>>

<application    android:hardwareAccelerated="false"

还有的童鞋发现在copy之后,因为内存中会存在多张图片,会导致内存溢出OOM,嗯,好吧,如果以后想用setBitmap方法,还是省省吧,改用drawBitmap比较好一点,不会有这么多的麻烦。
不过需要注意的是,使用drawbitmap时,要不能使用过大的图片,否则会导致OOM,一般300Kb的就可以,再大就有可能导致画面卡顿甚至卡死,不过我发现一个奇怪的现象,在我的涂鸦界面中,如果把传入的
copy之后再drawBitmap,虽然界面还是会卡顿,但不会直接退出。

bitmap大小=图片长度(px)*图片宽度(px)*单位像素占用的字节数单位像素所占字节数就是图片的色彩模式,安卓默认为四个字节。在BitmapFactory.Options.inPreferredConfig这里可以找到,一共有4种, ARGB代表:A 透明度 , R 红色, G 绿色, B 蓝色。

图片的压缩方法可以参考以下博客:
android图片压缩的三种算法

这是质量压缩算法的实现:

File file=new File(Environment.getExternalStorageDirectory() + "/" + "bitmap.jpg");if (file.exists()) { int p=90; Bitmap b=BitmapFactory.decodeFile(file.getPath()); while(b.getByteCount()>1024*300&&p>20){ //这里为了明显对比,进行的是破坏性实验,把图片最低压缩为30%,可以自己实验看下效果。     try {         b.compress(Bitmap.CompressFormat.JPEG,p--,new FileOutputStream(file));         b=BitmapFactory.decodeFile(file.getPath());     } catch (FileNotFoundException e) {         Toast.makeText(MainActicity.this,"bitmap can't compress",Toast.LENGTH_SHORT).show();         break;     } }}

按比例压缩方法:

private Bitmap compressPicture(String pathName,int height,int width){//按比例压缩    BitmapFactory.Options options=new BitmapFactory.Options();    options.inJustDecodeBounds = true;    //设置options只读取图片的大小。    Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/"+pathName,options);//此时返回bm为空,但必须有这一步,这是获取图片options的。    options.inJustDecodeBounds=false;    int h=options.outHeight;    int w=options.outWidth;    //得到图片的宽高,    int zip= h/height>w/width?h/height:w/width;    options.inSampleSize=zip;//设置缩放的比例,此时改变了像素大小,那么bitmap大小就会改变。    bitmap=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/"+pathName,options);    return bitmap;
0 0