Android学习笔记18 图形图像完全解析

本文主要对Android开发中图形图像部分的相关内容作个简单的学习总结。

一、概述
二、ImageView详解
三、Bitmap详解
四、Canvas详解
五、SurfaceView简介
六、总结

一、概述

在我们的应用开发中,图形图像处理往往是不可避免要接触的内容。一个完整的App,文字、图像、动画等都是用户交互的重要组成元素。本文主要从ImageView开始,对Android开发中与图形图像处理相关的内容作个简单的总结。

二、ImageView用法与解析

1、ImageView简介

ImageView的用途相信大家都已经十分的熟悉,应用中图标或者图片的展示一般都需要用到这个控件。SDK文档中对它的介绍,大致翻译是ImageView,图像视图,是用来展示一个任意的图像的控件,例如图标。它可以从不同的来源载入图像,控制图像的大小,并提供多种展示选项,比如缩放比例或者着色器等。

2、ImageView用法

首先我们看下ImageView一个最简单的使用。

<ImageView
     android:id="@+id/iv_scaletype_test"
     android:layout_width="200dp"
     android:layout_height="200dp"
     android:src="@drawable/img_test" />

当在布局文件里指定ImageView的宽高以及src属性后,一张图片就可以展示在ImageView上了。如果是用Java代码动态控制,那么src属性可以用方法setImageResource来完成。

当然,我们也可以配置很多其它属性。常用的有scaleType,了解它之前我们来看两张对比图。

未设置scaleType的ImageView
scaleType设置为CENTER_CROP的ImageView

可以看到,scaleType的不同设置可以控制图像在ImageView中的显示样式。

scaleType的取值共有8种,分别是:

  • MATRIX,意思是“矩阵”,即用矩阵来绘制,不缩放
  • FIT_XY,不按比例缩放图片,把图片塞满整个ImageView。
  • FIT_START,置顶,图片显示在ImageView的左上角start的位置
  • FIT_CENTER,居中
  • FIT_END,置底,图片显示在ImageView的右下角end的位置
  • CENTER,按图片的原来尺寸居中显示,当图片长/宽超过ImageView的长/宽,则截取图片的居中部分显示
  • CENTER_CROP,按比例扩大图片的size居中显示,使得图片长(宽)等于或大于ImageView的长(宽)
  • CENTER_INSIDE,图片完整居中显示,比例缩小或原来的size使得图片长/宽等于或小于ImageView的长/宽

一图胜千言,请看:

scaleType不同的设置.gif

在未设置scaleType时,系统默认保证图片完整、居中显示,并等比缩放,其效果跟android:scaleType=”fitCenter”的效果一致。

在代码里控制图片的缩放样式是用方法setScaleType来设置的。可以看到,用好这个属性,可以明显地改善图片在界面上的显示效果。

在ImageView的使用过程中,还有个tint属性可以设置。tint翻译是着色,它可以用来改变ImageView中内容的颜色,在代码中对应的设置方法是imageview.setColorFilter(Color),我们可以利用这个属性来修改一些图标的显示颜色。

三、Bitmap详解

Bitmap,翻译为位图,它是一个final类,Android系统图像处理中最重要的类之一。Bitmap可以获取图像文件信息,对图像进行剪切、旋转、缩放,压缩等操作,并可以以指定格式保存图像文件。在实际使用过程中,因为Bitmap很占内存,所以需要注意进行压缩,高效加载避免OOM。

Bitmap类比较特别,我们不能通过它的构造方法来实例化,只能通过Bitmap的静态方法或者借助BitmapFactory的静态方法来实例化。类BitmapFactory,它可以让我们从不同的来源,比如文件,流,字节数组等来源中创建Bitmap对象。下面是BitmapFactory的类结构。可以看到,它提供了一系列从不同来源实例化Bitmap的方法。

BitmapFactory的类结构

下面看个简单的例子。

实例化Bitmap并显示在ImageView上

上图中,已经用OkHttp网络请求获取到了字节数组,在Handler中处理消息时,先获取到字节数组,然后利用BitmapFactory的decodeByteArray方法,实例化Bitmap,最后通过ImageView的setImageBitmap方法将Bitmap设置为ImageView的内容。

Bitmap特别耗内存,所以我们在使用中要十分注意优化。常见的措施有,及时调用recycle()进行内存回收,实例化Bitmap时进行图片压缩,以及采取图片缓存等。

下面是在使用BitmapFactory的静态方法实例化Bitmap时进行压缩。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;

Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);

可以看到上面的代码实例化参数Options时设置inSampleSize为4,这样实例化的Bitmap占用的内存只为原来的1/16。

1.Bitmap的内存占用

上面我们提到,在使用类BitmapFactory加载Bitmap时指定Options可以有效的优化Bitmap的内存占用,那么Bitmap的内存占用具体怎么计算的呢?这里有个公式:

占用内存 = 图片宽 * 图片高 * 单位像素占用的内存

这里简单说明一下,图片的宽和高比较容易理解,单位像素的占用内存一般是由图片加载时的色彩模式决定的,android默认的色彩模式是ARGB_8888,一个像素占用的内存是4个字节。

这里还需要注意的是,图片的来源也会最终影响占用内存。如果我们直接从网络上获取图片,那么图片的占用内存就是上面的公式。但是如果是从drawable目录下加载图片,那么占用内存会因为不同的drawable目录而不同,因为Android系统在加载不同drawable目录下的图片到不用分辨率的机型上时会进行一定的缩放。

2.Bitmap的高效加载

BitmapFactory加载Bitmap时指定Options是一个比较好的习惯,那么Options的inSampleSize指定多少比较合适呢?其实最理想的情况是我们根据需要显示的ImageView的大小计算出Options的inSampleSize。

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

3.Bitmap的缓存

项目中如果需要加载图片,那么图片缓存的设计非常重要。一般来说,图片缓存会涉及到内存缓存和磁盘缓存,当需要加载图片时,先看内存中有没有,没有的话再看磁盘缓存中有没有,最后再尝试网络请求获取图片。

内存缓存和磁盘缓存目前比较常用的算法是LRU(Least Recently Used),即近期最少使用算法,内存缓存我们可以借助LruCache这个类来完成,磁盘缓存可以用DiskLruCache,它们共同的原理是指定一个缓存的总大小,当缓存满时,优先淘汰那些近期最少使用的缓存的对象。

四、Canvas解析

说到图形图像,那么肯定离不开要谈Canvas这个类。当我们想要绘制自己的图形时,或者在自定义View中,一般都会用到它。Canvas,直译为画布,SDK文档对它的介绍如下:

The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).

Canvas类包含很多draw的方法,要画某样东西,一般需要4个基本元素:一是Bitmap,用来包含像素。二是Canvas,包含画的方法。三是画的参数,比如宽高等属性。四是Paint,画笔,用来描述绘画的颜色和样式。

Canvas类中用来draw系列方法

同样的,我们来看个简单的例子。

public class MyDrawView extends View {

    private Paint mPaint;

    public MyDrawView(Context context) {
        super(context);

        mPaint = new Paint();
        mPaint.setColor(Color.RED);// 设置画笔颜色为红色
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setTextSize(50f);
        canvas.drawText("画圆:", 100, 200, mPaint);// 画文本

        canvas.drawCircle(300, 200, 80, mPaint);// 小圆
        mPaint.setAntiAlias(true);// 设置画笔的锯齿效果
        canvas.drawCircle(600, 200, 150, mPaint);// 大圆

        canvas.drawText("画线:", 100, 600, mPaint);
        canvas.drawLine(600, 400, 1000, 400, mPaint);// 画线
        canvas.drawLine(600, 400, 1000, 800, mPaint);// 斜线
    }
}

在上面的代码中,我们创建了一个继承自View的子类,构造函数里我们初始化了画笔Paint,设置颜色为红色。在onDraw()方法里,我们使用Canvas内部的几个draw方法绘制不同的图形,drawText是绘制文本,drawCircle是绘制圆...

最后效果如下:

Canvas画图

除了绘制各种基本的图形,我们也可以通过drawBitmap来绘制图片。实际开发中,利用Canvas、Paint等类,我们可以创造出很出酷炫的自定义图形。

五、SurfaceView简介

自己之前对SurfaceView这个类不是很熟悉,这次特地查阅了官方文档结合相关资料,这里简单地作个介绍。

View在大部分情况下可以满足我们的绘图需求,但是当需要频繁刷新,或者刷新时数据量比较大时,容易造成画面卡顿,SurfaceView是用来在上述两种情况下替代View的。

下面是官方文档的介绍

Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen

SurfaceView提供了一个专门用于绘图的surface,它嵌入在View的内部。我们可以控制这个surface的格式和大小,SurfaceView主要负责把surface放置在屏幕上的正确位置。

SurfaceView可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器。

六、总结

在实际开发中,我们可以为显示的图形图像增加很多的特效,这部分内容这里就不详细介绍了。总的来说,我们可以利用ColorMatrix颜色矩阵类来处理图像的色彩效果,Android系统利用矩阵来进行图像的图形变换,当然,我们还可以充分地利用Android中的动画来完成图形图像的动态效果展示。

关于Android中的图形图像部分的学习总结就是这些,当然实践出真知,只有在实际开发中不断摸索不断尝试,才能更好地掌握Android中的这些图形图像处理,创造出更多更酷炫的自定义效果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容