认识Android中drawable与bitmap
来源:互联网 发布:windows照片查看器重装 编辑:程序博客网 时间:2024/06/04 18:04
一、Drawable
(一)Drawable的基本特点
Android把可绘制的对象抽象为Drawable,不同的图形图像资源就代表着不同的drawable类型。Android FrameWork提供了一些具体的Drawable实现,通常在代码中都不会直接接触Drawable的实现类。
在实际的开发过程中,会把使用到的资源都放置在res/drawable目录,剩下的工作交给Android SDK 就行了,当需要使用图片资源的时候,可以使用@drawable标志在xml中引用drawable资源就行,也可以在代码中使用id引用这些drawable资源。
在使用drawable资源的时,有一点需要注意,drawable默认是内存共享的,也就说在不同的地方使用了同一个drawable,它们都指向相同的资源,而且具有相同的状态,如果在一个地方修改了这个drawable,所有使用它的地方都会改变。
Android内置了如下几种Drawable类型:ColorDrawable、GradientDrawable、BitmapDrawable、 NinePatchDrawable、InsetDrawable、ClipDrawable、ScaleDrawable、RotateDrawable、AnimationDrawable、LayerDrawable、LevelListDrawable、StateListDrawable、TransitionDrawable。
除了这些预置的drawable实现类以外,也可以自定义drawable的实现类型,大部分情况都不需要自定义drawable类型,使用系统提供的这些drawable实现类型已经覆盖了很多情况。在实际的编程过程中也很少会接触这些具体drawable实现类型,因为编写Android应用程序使用xml可以很容易的创建drawable,只有在程序中需要修改drawable的属性时,才需要使用具体的drawable类型提供的方法来处理。下面就来逐个认识这些Drawable类型。
(二)Drawable的子类介绍
(1)ColorDrawable
ColorDrawable 是最简单的Drawable,它实际上是代表了单色可绘制区域,它包装了一种固定的颜色,当ColorDrawable被绘制到画布的时候会使用颜色填充Paint,在画布上绘制一块单色的区域。
在xml文件中使用color作为根节点来创建ColorDrawable,它只有一个android:color属性,通过它来决定ColorDrawable的颜色,Android并没有提供修改这个颜色值的Api,所以这个颜色一旦设置之后,就不能直接修改了。
下面的xml文件定义了一个颜色为红色的ColorDrawable:
<?xml version="1.0" encoding="utf-8"?><color xmlns:android="http://schemas.android.com/apk/res/android"android:color="#FF0000"/>
当然也可以使用Java代码创建ColorDrawable,需要注意的是Android中使用一个int类型的数据表示颜色值,通常习惯使用十六进制格式的数据表示颜色值。一个int类型包含四个字节,分别代表颜色的4个组成部分:透明度(Alpha)、红(RED)、绿(GREEN)、蓝(BLUE),每个部分由一个字节(8个bit)表示,取值范围为0~255。在xml中使用颜色时可以省略透明度(Alpha)部分,如#ff0000表示红色。但是在代码中必须要明确指出透明度(Alpha)代表的数据,如果省略了就表示完全透明的颜色,例如0xFFFF0000表示红色,而0xFF0000虽然也表示红色,但它却是完全透明的,也就是说当绘制到画布上时,看不出有任何效果。
使用Java代码也可以创建ColorDrawable,代码如下:
ColorDrawable drawable = new ColorDrawable(0xffff0000);
(2)GradientDrawable
GradientDrawable 表示一个渐变区域,可以实现线性渐变、发散渐变和平铺渐变效果,在Android中可以使用GradientDrawable表示很多复杂而又绚丽的界面效果。
可以使用xml定义GradientDrawable,相对于ColorDrawable类型,GradientDrawable要复杂很多,它有很多的元素组成。在xml文件中使用shape作为根节点来创建GradientDrawable,它包含很多属性和子节点,下面是GradientDrawable的xml文档节点结构。
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"><size /> //定义区域的大小<gradient>//设置区域背景的渐变效果<solid/>//设置区域的背景颜色,如果设置了solid会覆盖gradient的效果<stroke />//设置区域的边框效果<padding />//设置区域的内边距</shape>
其中每个节点都有许多属性需要设置,以达到不同的渐变效果。
以下是几种不同渐变效果实现的xml文件代码:
- 线性渐变效果的椭圆:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" > <gradient android:angle="90" android:centerColor="#00ff00" android:endColor="#0000ff" android:startColor="#ff0000" /> <stroke android:width="3dp" android:color="#fff" android:dashGap="5dp" android:dashWidth="4dp" /></shape>
效果图如下:
- 平铺渐变效果的圆环:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring" android:innerRadiusRatio="8" android:thicknessRatio="3" android:useLevel="false" > <gradient android:type="sweep" android:useLevel="false" android:startColor="#ff0000" android:endColor="#0000ff" android:centerColor="#00ff00" /></shape>
效果图如下:
- 发散渐变效果的圆:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring" android:innerRadius="0dip" android:thickness="70dip" android:useLevel="false" > <gradient android:type="radial" android:useLevel="false" android:gradientRadius="70" android:startColor="#ff0000" android:endColor="#0000ff" android:centerColor="#00ff00" /></shape>
效果图如下:
(3)BitmapDrawable
BitmapDrawable 是对bitmap的一种包装,可以设置它包装的bitmap在BitmapDrawable区域内的绘制方式,如平铺填充、拉伸填充或者保持图片原始大小,也可以在BitmapDrawable区域内部使用gravity指定的对齐方式。
在xml文件中使用bitmap作为根节点来定义BitmapDrawable。
下面的xml代码定义一个BitmapDrawable,同时设置了BitmapDrawable的tileMode 属性为mirror,通过这样设置会使用小图片在水平和竖直方向做镜面平铺效果。
<?xml version="1.0" encoding="utf-8"?><bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:antialias="true" android:dither="true" android:src="@drawable/test" android:tileMode="mirror" ></bitmap>
也可以使用Java代码实现相同的效果,等价的Java代码如下:
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);BitmapDrawable mBitmapDrawable = new BitmapDrawable(mBitmap); mBitmapDrawable.setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);mBitmapDrawable.setAntiAlias(true);mBitmapDrawable.setDither(true);BitmapDrawable mDrawable = mBitmapDrawable;
(4)NinePatchDrawable
NinePatchDrawable,“点九图”是Andriod平台的一种特殊的图片格式,文件扩展名为:.9.png。支持Android平台的手机类型很多,有多种不同的分辨率,很多控件的切图文件在被放大拉伸后,边角会模糊失真。在Android平台下使用“点九”图片处理技术,可以将图片横向和纵向同时进行拉伸,以实现在多分辨率下的完美显示效果。点九图片在拉伸时仍能保留图像的渐变质感和圆角的精细度。
Android SDK工具集提供了处理点九图片的工具,可以通过draw9patch.bat运行,通过这个工具可以很容易把普通的PNG图片处理成“点九”图片。为什么要叫做”点九”呢?”点九”图像分为3*3的网格,即由9部分或者9 patch组成的网格。网格角落部分不会被缩放,边缘部分的4个patch只按照一维缩放,而中间部分则按照二维缩放,如下图所示:
使用了*.9.png图片技术后,只需要采用一套界面切图去适配不同的分辨率,而且大幅减少安装包的大小。Android FrameWork在显示点九图片时使用了高效的优化算法,所示应用程序不需要专门做处理就可以实现图片拉伸自适应,减少了代码量和实际开发的工作量。
在xml文件中使用使用nine-patch作为根节点创建NinePatchDrawable。同时,也可以使用bitmap包装点九图片,android FrameWork会根据android:src属性设置的图片类型来生成对应的drawable。代码如下:
<?xml version="1.0" encoding="utf-8"?><nine-patch xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/test" android:dither="true" />
或者
<?xml version="1.0" encoding="utf-8"?><bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/test" android:dither="true"/>
最后,需要指出的是,Android虽然可以使用Java代码创建NinePatchDrawable,但是极少情况会那么做,主要的原因是由于Android SDK会在编译工程时对点九图片进行编译,形成特殊格式的图片。使用代码创建NinePatchDrawable时只能针对编译过的点九图片资源,对于没有编译过的点九图片资源都当做BitmapDrawable对待。在使用点九图片时需要注意的是,点九图只能适用于拉伸的情况,对于压缩的情况并不适用,如果需要适配很多分辨率的屏幕时需要把点九图做的小一点。
(5)InsetDrawable
InsetDrawable 表示一个drawable嵌入到另外一个drawable内部,并且在内部留一些间距,这一点很像drawable的padding属性,区别在于 padding表示drawable的内容与drawable本身的边距,insetDrawable表示两个drawable和容器之间的边距。当控件需要的背景比实际的边框小的时候比较适合使用InsetDrawable。
在xml文件中使用inset作为跟节点定义InsetDrawable。
下面的xml定义了一个四边边距都为20dp的InsetDrawable,代码如下:
<?xml version="1.0" encoding="utf-8"?><inset xmlns:android="http://schemas.android.com/apk/res/android" android:insetBottom="20dp" android:insetLeft="20dp" android:insetRight="20dp" android:insetTop="20dp" android:drawable="@drawable/test"/>
(6)ClipDrawable
ClipDrawable 是对一个Drawable进行剪切操作,可以控制这个drawable的剪切区域,以及相相对于容器的对齐方式,android中的进度条就是使用一个ClipDrawable实现效果的,它根据level的属性值,决定剪切区域的大小。
在xml文件中使用clip作为根节点定义ClipDrawable。
需要注意的是ClipDrawable是根据level的大小控制图片剪切操作的,官方文档的note中提到:The drawable is clipped completely and not visible when the level is 0 and fully revealed when the level is 10,000。也就是level的大小从0到10000,level为0时完全不显示,为10000时完全显示。是用Drawable提供的setLevel(int level)方法来设置剪切区域。
下面为定义ClipDrawable的代码:
<?xml version="1.0" encoding="utf-8"?><clip xmlns:android="http://schemas.android.com/apk/res/android" android:clipOrientation="horizontal" android:drawable="@drawable/test" android:gravity="left" ></clip>
如果没有android:drawable属性,必须要设置一个任意类型的drawable作为子节点,代码如下:
<?xml version="1.0" encoding="utf-8"?><clip xmlns:android="http://schemas.android.com/apk/res/android" android:clipOrientation="horizontal" android:gravity="left"> <bitmap android:gravity="center" android:src="@drawable/test" /></clip>
(7)ScaleDrawable
(8)RotateDrawable
(9)AnimationDrawable
(10)LayerDrawable
(11)LevelListDrawable
(12)StateListDrawable
(13)TransitionDrawable
二、Bitmap的区别
(一)Bitmap的基本特点
Bitmap在Android中指的是一张图片,可以是png,也可以是jpg等其他图片格式。
(二)Bitmap的基本加载
Bitmap的加载离不开BitmapFactory类,BitmapFactory类提供了四类方法用来加载Bitmap:
(1)decodeFile 从文件系统加载
a. 通过Intent打开本地图片或照片b. 在onActivityResult中获取图片uric. 根据uri获取图片的路径d. 根据路径解析bitmap:Bitmap bm = BitmapFactory.decodeFile(sd_path)
(2)decodeResource 以R.drawable.xxx的形式从本地资源中加载
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.aaa);
(3)decodeStream 从输入流加载
a.开启异步线程去获取网络图片b.网络返回InputStreamc.解析:Bitmap bm = BitmapFactory.decodeStream(stream),这是一个耗时操作,要在子线程中执行
(4)decodeByteArray 从字节数组中加载
接3.a,3.b,c. 把InputStream转换成byte[]d. 解析:Bitmap bm = BitmapFactory.decodeByteArray(myByte,0,myByte.length);
注意:decodeFile和decodeResource间接调用decodeStream方法。
(三)高效的加载Bitmap
实现方法及原理,见我另外一篇博文:
点击跳转
(四)android 色彩模式说明
android 色彩模式分为四种:
- ALPHA_8:每个像素占用1byte内存
- ARGB_4444:每个像素占用2byte内存
- ARGB_8888:每个像素占用4byte内存
- RGB_565:每个像素占用2byte内存
Android默认的色彩模式为ARGB_8888,这个色彩模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。BitmapFactory.Options的inPreferredConfig参数可以指定decode到内存中,手机中所采用的编码,可选值定义在Bitmap.Config中。缺省值是ARGB_8888。假设一张1024*1024,模式为ARGB_8888的图片,那么它占有的内存就是:1024*1024*4 = 4MB
(1)采样率inSampleSize
- inSampleSize的值必须大于1时才会有效果,且采样率同时作用于宽和高;
- 当inSampleSize=1时,采样后的图片为图片的原始大小
- 当inSampleSize=2时,采样后的图片的宽高均为原始图片宽高的1/2,这时像素为原始图片的1/(22),占用内存也为原始图片的1/(22)
- inSampleSize的取值应该总为2的整数倍,否则会向下取整,取一个最接近2的整数倍,比如inSampleSize=3时,系统会取inSampleSize=2
假设一张1024*1024,模式为ARGB_8888的图片,inSampleSize=2,原始占用内存大小是4MB,采样后的图片占用内存大小就是(1024/2) * (1024/2 )* 4 = 1MB
三、Drawable与Bitmap的区别
(一)Drawable与Bitmap的区别
(二)获取方式
(1)已将图片保存到drawable目录下,通过图片id获得Drawable或者Bitmap
//通过图片id获得DrawableDrawable drawable=getResource().getDrawable(R.drawable.xxx);//通过图片id获得BitmapResource res=gerResource();Bitmap bitmap=BitmapFactory.decodeResource(res, id);
(2)已将图片保存到assest目录下,知道图片的名称,通过inputstream获得图片Drawabl或者 Bitmap
AssetManager asm=getAssetMg();InputStream is=asm.open(name);//name:图片的名称//获得DrawableDrawable da = Drawable.createFromStream(is, null);//获得BitmapBitmap bitmap=BitmapFactory.decodeStream(is);
(3)图片保存在sdcard,通过图片的路径
String imgFilePath = Environment.getExternalStorageDirectory().toString()+ “/DCIM/device.png”;fis = new FileInputStream(new File(imgFilePath));//文件输入流Bitmap bmp = BitmapFactory.decodeStream(fis);Drawable da = Drawable.createFromStream(is, null)ImageView iv = (ImageView) findViewById(R.id.image);Bitmap bit = BitmapFactory.decodeFile(/sdcard/android.bmp);iv.setImageBitmap(bit);iv.setImageDrawable(Drawable.createFromPath(new File(Environment.getExternalStorageDirectory(), camera.jpg).getAbsolutePath()));
(三)Drawable与Bitmap的转化及操作
(1)Bitmap → byte[]
public byte[] Bitmap2Bytes(Bitmap bm) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray();}
(2)byte[] → Bitmap
public Bitmap Bytes2Bimap(byte[] b) { if (b.length != 0) { return BitmapFactory.decodeByteArray(b, 0, b.length); } else { return null; }}
(3)Bitmap缩放
public static Bitmap zoomBitmap(Bitmap bitmap, int width, int height) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); Matrix matrix = new Matrix(); float scaleWidth = ((float) width / w); float scaleHeight = ((float) height / h); matrix.postScale(scaleWidth, scaleHeight); Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true); return newbmp;}
(4)将Drawable转化为Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) { // 取 drawable 的长宽 int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); // 取 drawable 的颜色格式 Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; // 建立对应 bitmap Bitmap bitmap = Bitmap.createBitmap(w, h, config); // 建立对应 bitmap 的画布 Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); // 把 drawable 内容画到画布中 drawable.draw(canvas); return bitmap; }
(5)获得圆角图片
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, w, h); final RectF rectF = new RectF(rect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; }
(6)Bitmap转换成Drawable
Bitmap bm=xxx; //xxx根据你的情况获取BitmapDrawable bd= new BitmapDrawable(getResource(), bm); //因为BtimapDrawable是Drawable的子类,最终直接使用bd对象即可。
(7)Drawable缩放
public static Drawable zoomDrawable(Drawable drawable, int w, int h) { int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); // drawable转换成bitmap Bitmap oldbmp = drawableToBitmap(drawable); // 创建操作图片用的Matrix对象 Matrix matrix = new Matrix(); // 计算缩放比例 float sx = ((float) w / width); float sy = ((float) h / height); // 设置缩放比例 matrix.postScale(sx, sy); // 建立新的bitmap,其内容是对原bitmap的缩放后的图 Bitmap newbmp = Bitmap.createBitmap(oldbmp, 0, 0, width, height,matrix, true); return new BitmapDrawable(newbmp); }
四、Drawable与Bitmap的区别
这篇文章写得不错,大家可以看一下:android 图片占用内存大小及加载解析
参考资料:
- Android的各种Drawable讲解
- Android 之 Bitmap
- 认识Android中drawable与bitmap
- Android Drawable与Bitmap
- Android的Bitmap与Drawable
- android Bitmap 与Drawable转换
- Android 中的bitmap与drawable
- Android中Drawable、Bitmap、byte
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- Android中Bitmap和Drawable
- springcloud
- BZOJ 5108 可做题 贪心,分段,异或拆位
- 毛哥的快乐生活 第二章 毛哥的学生时代
- 设计模式--单例模式
- javascript实现tab切换效果
- 认识Android中drawable与bitmap
- urllib2.HTTPError: HTTP Error 504: Fiddler
- 博客搬家
- [LUOGU1001] A+B Problem
- SQL新函数, 排名函数
- 网络原理学习笔记--基本概念1
- [洪流学堂]Hololens开发高级篇4:立体音效(Spatial sound)
- Map<String, String> 遍历的四种方法
- 数据库之自身连接与外连接