Android自定义控件(八)——详解创建bitmap的方式

简介: Android自定义控件(八)——详解创建bitmap的方式

什么是Bitmap


Bitmap是绘图中非常重要的概念,在我们前面自定义的所有View中,他们的画布Canvas说到底都其实是Bitmap,我们先来看看我们常用的代码片段:

Bitmap bgBitmap=Bitmap.createBitmap(getWidth(),getHeight(),Bitmap.Config.ARGB_8888);//创建一个新空白位图
Canvas canvasBg=new Canvas(bgBitmap);
canvasBg.drawColor(Color.RED);


这段代码中,我们先利用bitmap创建一个空白位图,然后在利用Bitmap在创建一个Canvas对象,后续的所有操作其实都是绘制在这个bitmap上的。其实这也间接说明了,bitmap就是由一个个像素点构成的,你可以通过Canvas绘制任何东西在这款bitmap上。


Bitmap格式


既然Bitmap是由一个一个像素点构成的,那么就我们需要知道两个概念:


1.它是如何存储每个像素点的


2.如何进行压缩防止OOM


它是如何存储每个像素点的

一张位图所占用的内存=图片的长度(px)*图片的宽度(px)*一个像素占用的字节数。


而这里一个像素所占用的字节数,就是上面代码中最后一个参数Bitmap.Config.ARGB_8888。


其中A代表透明度,R代表红色,G代表绿色,B代表蓝色。这个参数其实有四种,分别是:


1.ALPHA_8:表示8位的alpha位图,表示只存储图片的透明度,不存储颜色值,一个像素占8位,也就是一个字节。


2.ARGB_4444:表示16位的ARGB位图,即透明度A,红色R,绿色G,蓝色B各占4位,一个像素点占4+4+4+4=16位,2个字节。


3.ARGB_8888:这个是我们最常用的,表示32位的ARGB位图,即透明度A,红色R,绿色G,蓝色B各占8位,一个像素点占8+8+8+8=32位,4个字节。


4.RGB_565:表示16位的RGB位图,没有透明度值,R红色占5位,G绿色占6位,B蓝色占5位,一个像素点占5+6+5=16位,2个字节。


上面的四种格式中,ARGB_444因为画质惨不忍睹,所以在API13中已经弃用,而ARGB_8888一个颜色占了8位,所以画质最高,但如果你的项目中对透明度没什么要求的画,建议使用RGB_565,能节省一半的内存开销。


如何进行压缩防止OOM

如果有需要将bitmap存储在硬盘上,那么必然会考虑到压缩的问题,所以Android也给我们提供了压缩的方案,分别是JPEG,PNG以及WEBP三种。


1.CompressFormat.JPEG:采用JPEG压缩算法,这是一种有损压缩,而且不支持透明度。


2.CompressFormat.PNG:采用PNG压缩算法,这是一种支持透明度的无损压缩方式。


3.CompressFormat.WEBP:这种提供了有损压缩和无损压缩两种,在API14到API17中,是一种有损压缩格式,不支持透明度,在API18以后是无损压缩,支持透明度。在相同质量下,WEBP比JPEG小40%,比PNG小26%,但是都是牺牲压缩时间来减小文件大小,是JPEG编码长8倍,是PNG的5倍。


创建Bitmap


创建Bitmap的方式有两种,一种是通过BitmapFactory进行创建,一种是bitmap的静态方法。


BitmapFactory创建

我们先来介绍BitmapFactory的创建方式,下面是Bitmap的所有函数:

public static Bitmap decodeResource(Resources res,int id)
public static Bitmap decodeResource(Resources res,int id,Options opts)
public static Bitmap decodeFile(String pathName)
public static Bitmap decodeFile(String pathName,Options opts)
public static Bitmap decodeByteArray(byte[] data,int offset,int length)
public static Bitmap decodeByteArray(byte[] data,int offset,int length,Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd)
public static Bitmap decodeFileDescriptor(FileDescriptor fd,Options opts)
public static Bitmap decodeStream(InputStream is)
public static Bitmap decodeStream(InputStream is,Options opts)
public static Bitmap decodeResourceStream(Resources res,TypedValue value,InputStream is,Rect pad,Options opts)


从这些函数可以看出来BitmapFactory的功能很强大,可以针对资源,文件,字节数组,FileDescriptor和InputStream数据流解析出对应的Bitmap对象,而且基本上,每个函数都有两种实现,而多的按个参数就是Options opts(这个参数,最后这标题最后讲解)


其实上面的函数基本都很好理解,有一定Java文件操作经验的应该都能很好的使用,这里我们只要先介绍decodeFileDescriptor这个函数,我们先来看看它的使用代码:

String path="/data/data/background.jpg";
FileInputStream is=new FileInputStream(path)
Bitmap bitmap=BitmapFactory.decodeFileDescriptor(is.getFD());


这里实现起来也很简单,但有一个疑问,就是这里已经有文件路径,可以直接调用decodeFile()直接生成,为何还要多此一举呢?


小编就直接告诉大家把,因为这样更节约内存空间,而decodeFile比decodeFileDescriptor更容易OOM,至于为什么,直接查看源码大家就知道了,因为decodeFile内部调用了decodeStream,并且调用了如下代码,申请了两次空间:

is=new BufferedInputStream(is,16*1024);
tempStorage=new byte[16*2024];


接着,我们来看看decodeByteArray如何生成Bitmap,假如这张图片来自网络,我们看看先如何获取这个byte[],代码如下:

public class LYJMethod {
    public static byte[] getImage(String path) throws Exception {
        URL url=new URL(path);
        HttpURLConnection httpURLConnection=(HttpURLConnection)url.openConnection();
        httpURLConnection.setRequestMethod("GET");
        httpURLConnection.setReadTimeout(6000);
        InputStream is=null;
        if(httpURLConnection.getResponseCode()==200){
            is=httpURLConnection.getInputStream();
            byte[] result=readStream(is);
            is.close();
            return result;
        }
        return null;
    }
    public static byte[] readStream(InputStream is) throws Exception{
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        byte[] buffer=new byte[1024];
        int len=-1;
        while((len=is.read(buffer))!=-1){
            baos.write(buffer,0,len);
        }
        baos.close();
        is.close();
        return baos.toByteArray();
    }
}


这里可能大家有个疑问,为何明明已经获取了buffer数组,为何还要转换位toByteArray呢?这是因为这个函数所需要的字节数组,以大家想象中的不一样,而是把输入流转换位字节内存输出流的字节数组格式。如果不转换返回结果会一直为null,大家可以在程序中调试试试。


这些生成bitmap函数也就这两个函数需要额外注意,其他的基本都差不多,这里就不再赘述了。


BitmapFactory.Options参数详解

接着我们讲解每个函数最后多出来的一个参数Options,这个是设置Bitmap的采样率,通过改变图片的宽度,高度,缩放比例等,以达到减少图片像素的目的。简单的说,通过这个参数,可以更好的控制bimap。下面是这个参数部分成员变量:

public boolean injustDecodeBounds;
public int inSampleSize;
public int inDensity;
public int inTargetDensity;
public int inScreenDensity;
public boolean inScaled;
public Bitmap.Config inPreFerredConfig;
public int outWidht;
public int outHeight;
public String outMimeType;


1.injustDecodeBounds:这个参数用于获取图片信息,如果设置为true,表示只获取图片的信息,不分配内存,也就是说调用如下代码后,bitmap为空(默认为false):

BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
Bitmap bitmap=BitmapFactory.decodeRecource(getResources(),R.drawable.backgroud,options);


2.inSampleSize:这个参数用于压缩图片,比如将这个字段设置为4,意思就是从原来图片每4个像素中取一个像素作为返回结果,也就是图片缩小了4倍。


3.inScaled:这个参数用于设置是否缩放图片,我们都知道,我们的安装项目中图片文件夹有很多,如下图所示:

39.png

drawable文件夹同样也是如此,比如mdpi为240,其中有一张图片,但其他文件夹都没有对应大小的图片,如果应用到xhdpi的屏幕中,那么这个参数设置true,就将这个240下完美显示的图片,缩放成480在当前屏幕显示, 这样就会造成图片模糊,如果不缩放就设置为false(默认为true)。


4.inDensity,inTargetDensity


这二个参数是相互关联的所以,有必要在一起讲解,同样,我们先来看看这二个参数的意思:


inDensity:用于设置文件所在资源文件夹的屏幕分辨率。


inTargetDensity:表示真实显示的屏幕分辨率。


scale=inTargetDensity/inDensity,这个公式就是手动设置文件所在资源文件夹的分辨率和真实显示屏幕分辨率来指定图片的缩放比例,也就是说,可以通过这两个参数,自己掌握缩放的大小,对于第三个参数理解。


5.inPreferredConfig


这个参数用来设置像素的存储格式,我们在前面已经介绍过了Android手机的4中存储格式,分别为ALPAH_8,RGB_565,ARGB_8888,ARGB_4444,这个参数默认ARGB_8888。


这里详细讲解最常用的6个成员变量,还有3个成员变量,其中outWidht,outHeight就是图片的详细宽高信息,outMimeType就是图片的格式,也就是如果你要获取图片信息直接使用这3个参数即可。inScreenDensity根本用不到,所以这里就不讲解。


直接通过Bitmap创建

同样,我们直接看看Bitmap自带的静态方法:

static Bitmap createBitmap(int widht,int height,Bitmap.Config config)
static Bitmao createBitmap(Bitmap src)
static Bitmap createBitmap(Bitmap Source,int x int y,int width,int height)
static Bitmap createBitmap(Bitmap Source,int x int y,int width,int height,Matrix m,boolean filter)
static Bitmap createBitmap(int[] colors,int widht,int height,Bitmap.Config config)
static Bitmap createBitmap(int[] colors,int offset,int stride,int width,int height,Bitmap.Config config)
static Bitmap createScaledBitmap(Bitmap src,int dstWidht,int dstHeight,boolean filter)


最后一个方法我们放大镜那章已经用到过,就是按比例放大缩小图片,这里就不在赘述,同样的createBitmap就是创建一个空白位图,前面也用到过,这里也不在讲解。这里主要讲解第3个到底6个方法。我们先来看一段代码:

//createBitmap(Bitmap Source,int x int y,int width,int height)
Bitmap src=BitmapFactory.decodeResource(getResources(),R.drawable,backgroud);
Bitmap bitmap=Bitmap.createBitmap(src,src.getWidth()/3,src.getHeight()/3,src.getWidth()/3,src.getHeight()/3)


这个方法用于裁剪图片,其中x,y用于定义裁剪图片的左上角坐标,width和height用于设置裁剪图片的宽高,source就是源图像,这里我们获取了原图片中间的1/9图像。

static Bitmap createBitmap(Bitmap Source,int x int y,int width,int height,Matrix m,boolean filter)


第4个方法多了两个参数分别是Matrix m,这个参数用于给裁剪后的图片添加矩阵,boolean filter用于是否给图片添加滤波效果,如果设置为true,能够减少图像中由于噪声引起的突兀的孤立的像素块。比如我这样设置上面的代码:

//createBitmap(Bitmap Source,int x int y,int width,int height)
Bitmap src=BitmapFactory.decodeResource(getResources(),R.drawable,backgroud);
Matrix matrix=new Matrix();
matrix.setScale(2,1);
Bitmap bitmap=Bitmap.createBitmap(src,src.getWidth()/3,src.getHeight()/3,src.getWidth()/3,src.getHeight()/3,matrix,true)


将是将裁剪后的图片宽度方法两倍。


接着我们再来看看,下面两个方法:


static Bitmap createBitmap(int[] colors,int widht,int height,Bitmap.Config config)
static Bitmap createBitmap(int[] colors,int offset,int stride,int width,int height,Bitmap.Config config)


1.colors:当前图像所对应的每个像素的数组,它的数组长度必须是图片的widht*height。


2.width,height:当前图像的宽高。


3.config:用于指定图像的存储格式,比如:ARGB_888。


这种两个方法使用起来难度非常大,你需要设置每一个像素点的ARGB四个颜色,所以这里不再讲解,我们将结合下一节,详细bitmap的应用,教你如何让图片变得更鲜艳。

相关文章
|
2月前
|
搜索推荐 Android开发 开发者
安卓应用开发中的自定义控件实践
在安卓应用开发的广阔天地中,自定义控件如同璀璨的星辰,点亮了用户界面设计的夜空。它们不仅丰富了交互体验,更赋予了应用独特的个性。本文将带你领略自定义控件的魅力,从基础概念到实际应用,一步步揭示其背后的原理与技术细节。我们将通过一个简单的例子——打造一个具有独特动画效果的按钮,来展现自定义控件的强大功能和灵活性。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往更高阶UI设计的大门。
|
3月前
|
前端开发 Android开发 UED
安卓应用开发中的自定义控件实践
【10月更文挑战第35天】在移动应用开发中,自定义控件是提升用户体验、增强界面表现力的重要手段。本文将通过一个安卓自定义控件的创建过程,展示如何从零开始构建一个具有交互功能的自定义视图。我们将探索关键概念和步骤,包括继承View类、处理测量与布局、绘制以及事件处理。最终,我们将实现一个简单的圆形进度条,并分析其性能优化。
|
4月前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件基础与进阶
【10月更文挑战第5天】在Android应用开发中,自定义控件是提升用户体验和界面个性化的重要手段。本文将通过浅显易懂的语言和实例,引导你了解自定义控件的基本概念、创建流程以及高级应用技巧,帮助你在开发过程中更好地掌握自定义控件的使用和优化。
85 10
|
4月前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
|
4月前
|
前端开发 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的世界里,自定义控件如同画家的画笔,能够绘制出独一无二的界面。通过掌握自定义控件的绘制技巧,开发者可以突破系统提供的界面元素限制,创造出既符合品牌形象又提供卓越用户体验的应用。本文将引导你了解自定义控件的核心概念,并通过一个简单的例子展示如何实现一个基本的自定义控件,让你的安卓应用在视觉和交互上与众不同。
|
5月前
|
缓存 前端开发 Android开发
安卓应用开发中的自定义控件
【9月更文挑战第28天】在安卓应用开发中,自定义控件是提升用户界面和交互体验的关键。本文通过介绍如何从零开始构建一个自定义控件,旨在帮助开发者理解并掌握自定义控件的创建过程。内容将涵盖设计思路、实现方法以及性能优化,确保开发者能够有效地集成或扩展现有控件功能,打造独特且高效的用户界面。
|
5月前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
92 20
Android经典面试题之图片Bitmap怎么做优化
|
5月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义控件
【9月更文挑战第5天】在安卓开发的海洋中,自定义控件如同一艘精致的小船,让开发者能够乘风破浪,创造出既独特又高效的用户界面。本文将带你领略自定义控件的魅力,从基础概念到实战应用,一步步深入理解并掌握这一技术。
|
6月前
|
Android开发 UED 开发者
安卓开发中的自定义控件基础
【8月更文挑战第31天】在安卓应用开发过程中,自定义控件是提升用户界面和用户体验的重要手段。本文将通过一个简易的自定义按钮控件示例,介绍如何在安卓中创建和使用自定义控件,包括控件的绘制、事件处理以及与布局的集成。文章旨在帮助初学者理解自定义控件的基本概念,并能够动手实践,为进一步探索安卓UI开发打下坚实的基础。
|
6月前
|
存储 缓存 前端开发
安卓开发中的自定义控件实现及优化策略
【8月更文挑战第31天】在安卓应用的界面设计中,自定义控件是提升用户体验和实现特定功能的关键。本文将引导你理解自定义控件的核心概念,并逐步展示如何创建一个简单的自定义控件,同时分享一些性能优化的技巧。无论你是初学者还是有一定经验的开发者,这篇文章都会让你对自定义控件有更深的认识和应用。

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    Android历史版本与APK文件结构
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 9
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 10
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 1
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    14
  • 2
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    28
  • 3
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    34
  • 4
    Android历史版本与APK文件结构
    121
  • 5
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 6
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 7
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    60
  • 8
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 9
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    73
  • 10
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    121