Bitmap详细讲解

  • Bitmap描述:

每一张图片都是有无数点组成,我们取其中足够数量的点(可以形成视觉连贯性,以便形成近似真正图片的视觉效果),这些点就是像素点。比如某张图片大小为:1080*1920 ,它的像素数就为1080*1920个。这些像素点的信息就是这张图片的表述。

每个像素的信息,就是ARGB四个通道的值。其中,A代表透明度,RGB代表红绿蓝三种颜色通道值。每个通道的值范围在0~255之间,即有256个值,刚好可以通过一个字节(8bit)进行表示。所以,每个通道值由一个字节表示,四个字节表示一个像素信息。

  • 在这里需要注意两点:

1)在实际需求中,有的时候,我们并不需要这么丰富的颜色数量,所以可以适当减少表示每个颜色通道的bit位数。这么做的好处是节省空间。也就是说,每个颜色通道都采用8bit来表示是代表全部颜色值的集合;而我们可以采用少于8bit的表示方式,尽管这会缺失一部分颜色值,但是只要颜色够用即可,并且这还可以节省内存空间。

2)如果我们需要某个图片作为背景或者图标,这个图片透明度A通道值是必要的。但是如果我们只是普通的图片展示,比如拍摄的照片,透明度值毫无意义。因此,透明度这个通道值是否有必要表示也是根据需求自由变化的。

  • Bitmap.config:

Config是Bitmap的一个枚举内部类,它表示的就是每个像素点对ARGB通道值的存储方案。取值有以下四种:

ARGB_8888:这种方案就是上面所说的每个通道值采8bit来表示,每个像素点需要4字节的内存空间来存储数据。该方案图片质量是最高的,但是占用的内存也是最大的

ARGB_4444:这种方案每个通道都是4位,每个像素占用2个字节,图片的失真比较严重。一般不用这种方案。

RGB_565:这种方案RGB通道值分别占5、6、5位,但是没有存储A通道值,所以不支持透明度。每个像素点占用2字节,是ARGB_8888方案的一半。

ALPHA_8:这种方案不支持颜色值,只存储透明度A通道值,使用场景特殊,比如设置遮盖效果等。

比较分析:一般我们在ARGB_8888方式和RGB_565方式中进行选取:不需要设置透明度时,比如拍摄的照片等,RGB_565是个节省内存空间的不错的选择;既要设置透明度,对图片质量要求又高,就用ARGB_8888。

  • Bitmap的压缩存储

 Bitmap是图片内容在内存中的表示形式,那么如果想要将Bitmap对象进行持久化存储为一张本地图片,需要对Bitmap对象表示的内容进行压缩存储。根据不同的压缩算法可以得到不同的图片压缩格式(简称为图片格式),比如GIF、JPEG、BMP、PNG和WebP等。而从存储中读取Bitmap对象,则是解压缩过程。

 这样的目的是:当图片持久化保存在磁盘上时,我们应该尽可能以最小的体积来保存同一张图片的内容,这样有利于节省磁盘空间;而当图片加载到内存中以显示的时候,应该将磁盘上压缩存储的图片内容完整地展开。前者即为压缩过程,目的是节省磁盘空间;后者即为解压缩过程,目的是在内存中展示图片的完整内容。

一般压缩可分为:有损压缩和无损压缩: 

1)无损压缩基本原理是:相同的颜色信息只需保存一次。具体过程是:首先会确定图像中哪些区域是相同的,哪些是不同的。包括了重复数据的区域就可以被压缩,只需要记录该区域的起始点即可。
从本质上看,无损压缩的方法通过删除一些重复数据,也能在位图持久化存储的过程中减少要在磁盘上保存的图片大小。但是,如果将该图片重新读取到内存中,重复数据会被还原。因此,无损压缩的方法并不能减少图片的内存占用量,如果要减少图片占用内存的容量,就必须使用有损压缩方法

2)有损压缩的原理是:保持颜色的逐渐变化,删除图像中颜色的突然变化。生物学中的大量实验证明,人类大脑会自发地利用与附近最接近的颜色来填补所丢失的颜色。有损压缩的具体实现方法就是删除图像中景物边缘的某些颜色部分,利用有损压缩技术可以在位图持久化存储的过程中大大地压缩图片的存储大小,但是会影响图像质量,这一点在压缩率很高时尤其明显。所以需要选择恰当的压缩率。

对比分析:有损压缩压缩率高而且可以灵能保活设置压缩率,并且删除的数据不可还原,因此可以减少图片的内存占用,但是对图片质量会有一定程度的影响;无损压缩可以很好地保存图片质量,也证一定的压缩率虽然没有有损压缩那么高,并且无损压缩删除的数据在重新加载至内存时会被还原,因此不可以减少图片的内存占用。

几种压缩格式:

1)JPG/JPEG:JPEG/JPG是一种有损压缩格式,JPEG图片以24位颜色压缩存储单个位图。也就是说,JPEG不支持透明通道。JPEG也不支持多帧动画,但JPEG2000作为JPEG的升级版,其压缩率比JPEG高约30%左右,同时支持有损和无损压缩。

2)PNG:

            PNG格式也属于无损压缩,其位深为32位,也就是说它支持所有的颜色类型。同样是无损压缩,PNG的压缩率高于Gif格式,而且PNG支持的颜色数量也远高于Gif,因此:如果是对静态图片进行无损压缩,优先使用PNG取代Gif,因为PNG压缩率高、色彩好;但是PNG不支持动画效果。所以Gif仍然有用武之地。PNG缺点是:由于是无损压缩,因此PNG文件的体积往往比较大,APP瘦身时需要对PNG文件进行优化。

         3)WBEP:

             WebP 是 Google 在 2010 年发布的图片格式,希望以更高的压缩率替代 JPEG。它用 VP8 视频帧内编码作为其算法基础,取得了不错的压缩效果。WebP支持有损和无损压缩、支持完整的透明通道、也支持多帧动画,并且没有版权问题,是一种非常理想的图片格式。WebP支持动图,基本取代gif。WebP不仅集成了PNG、JPEG和Gif的所有功能,而且相同质量的无损压缩WebP图片体积比PNG小大约26%;如果是有损压缩,相同质量的WebP图片体积比JPEG小25%-34%,

             它的缺点为:编解码时间长,约为JPEG的8倍左右。

  • Bitmap的简单使用

1、Bitmap的加载方法

Bitmap的工厂类BitmapFactory提供了四类静态方法用于加载Bitmap对象:decodeFile、decodeResource、decodeStream、decodeByteArray。

分别代表从本地图片文件、项目资源文件、流对象(可以是网络输入流对象或本地文件输入流对象)、字节序列中加载一个Bitmap对象。

之前讲的图片的三个来源,都可以找到对应的decodeXXXX方法来获取该图片对应的Bitmap对象。

举个例子,假设需要通过网络请求一张图片资源并展示:先处理该网络请求并得到返回结果的输入流对象;依据该流对象调用decodeStream方法得到一个Bitmap对象,该对象即表示这张图片的内容;最后通过ImageView的setImageBitmap()方法显示该图片即可。

2、Bitmap的压缩存储方法

Bitmap的压缩存储与Bitmap的加载是相反的过程,通过compress()方法来实现,该方法原型为:

compress(Bitmap.CompressFormat format, int quality, OutputStream stream)

format参数表示压缩存储的格式,可选为PNG、JPEG和WEBP;quality表示压缩率,取值在0~100之间,100表示未压缩,30表示压缩为原大小的30%,但是该参数在format值为PNG时无效,因为PNG属于无损压缩无法设置压缩率;stream就是希望输出到某个位置的输出流对象,比如某个文件的输出流对象。通过compress()方法可以将Bitmap按照指定的格式和压缩率(非PNG格式时)压缩存储到指定的位置。

       3、BitmapFactory.Options类

        BitmapFactory是Bitmap的工厂类,通过BitmapFactory的静态方法来创建Bitmap对象;BitmapFactory.Options类代表对Bitmap对象的属性设置(配置)

 

属性作用
boolean inJustDecodeBounds是否只获取信息,不加载Bitmap
int inSample采样率 默认1
Bitmap.config inPreferredConfig色彩模式
int outwidthBitmap宽
int outheightBitmao高
boolean inDither防抖动 默认false
int inDensity像素密度
boolean isScaled是否可以缩放 默认true
boolean isMutabledecode方法返回的结果是否可变

    inJustDecodeBounds:这个属性表示是否只扫描,默认false,值为true时,不会返回Bitmap对象,返回NULL,但会获取宽高等信息。

    inSample:默认为1,比如说有一张图片是2048像素X1024像素,那么默认情况下该图片加载到内存中的Bitmap对象尺寸也是2048像素X1024像素。如果采用的是ARGB_8888方式,那么该Bitmap对象加载所消耗的内存为2048X1024X4/1024/1024=8M,如果值为2,则加载结果Bitmap宽高都为1/2,消耗内存为1024X512X4/1024/1024*=2M,如果为4,消耗内存为512X256X4/1024/1024*=0.5M,这极大的节省了内存资源从而避免了OOM错误。

切记:官方对于inSample值的要求是,必须为2的幂,比如2、4、8...等整数值。

   inPreferredConfig:该属性指定Bitmap的色深值,该属性类型为Bitmap.Config值。例如你可以指定某图片加载为Bitmap对象的色深模式为ARGB_8888,即:options.inPreferredConfig=Bitmap.Config.ARGB_8888。

   isMutable:该属性表示通过decodeXXXX方法创建的Bitmap对象其代表的图片内容是否允许被外部修改,比如利用Canvas重新绘制其内容等

  • Bitmap的进阶使用  

1)官方推荐计算合理采样率:

基本步骤就是:①获取位图原尺寸 ②获取ImageView即最终图片显示的尺寸  ③依据两种尺寸计算采样率

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // 位图的原宽高通过options对象获取
    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;
     //当要显示的目标大小和图像的实际大小比较接近时,会产生没必要的采样,先除以2再判断以防止过度采样
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

 

<think>我们正在讨论Android SeekBar的详细讲解和使用指南。根据之前的对话历史,用户已经询问了关于`trackWidth`的详细解释,现在用户希望获得关于Android SeekBar的详细介绍和使用指南。我们参考了站内的引用内容,这些引用提供了关于SeekBar的基础使用、自定义、事件监听以及一些常见问题的解决方案。下面,我们将结合这些引用内容,系统地介绍SeekBar。 回答结构: 1. SeekBar概述:介绍SeekBar是什么以及它的基本作用。 2. 基础使用:包括XML声明和Java/Kotlin代码初始化,设置基本属性。 3. 事件监听:介绍如何监听SeekBar的进度变化。 4. 自定义SeekBar:包括自定义轨道、滑块和刻度等,引用中提到了通过progressDrawable设置高度等。 5. 常见问题及解决方案:如高度设置不生效、切换页面后进度不展示等(引用[5]提到的问题)。 6. 高级功能:如添加手势滑动支持、自定义绘制、动态反馈等(引用[1]和引用[2]提到的自定义扩展)。 7. 总结。 注意:在回答中,我们将适当地引用站内引用内容,并在引用处标注引用标识。 另外,根据系统指令,数学表达式使用$...$,独立公式使用$$...$$,并且使用中文回答。最后,生成相关问题。 下面开始回答: </think>### Android SeekBar 全面解析:从基础到高级应用 SeekBar 是 Android 开发中用于调整数值的核心交互控件,结合进度显示与用户滑动操作。以下从基础使用到深度优化系统讲解: --- #### 一、基础使用指南 1. **XML 声明与属性配置** ```xml <SeekBar android:id="@+id/seekBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" <!-- 进度最大值 --> android:progress="30" <!-- 初始进度值 --> android:paddingStart="12dp" <!-- 轨道起始内边距 --> android:paddingEnd="12dp" /> <!-- 轨道结束内边距 --> ``` 2. **Java/Kotlin 初始化** ```kotlin val seekBar = findViewById<SeekBar>(R.id.seekBar) seekBar.max = 200 // 动态修改最大值 seekBar.progress = 75 // 设置当前进度 ``` --- #### 二、事件监听与交互逻辑 1. **进度变化监听(核心)** ```kotlin seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { // 进度实时变化时触发 override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { Log.d("SeekBar", "当前进度: $progress") } // 用户开始触摸滑块 override fun onStartTrackingTouch(seekBar: SeekBar?) {} // 用户释放滑块 override fun onStopTrackingTouch(seekBar: SeekBar?) {} }) ``` 2. **触摸位置转换公式** 用户触摸点转换为进度值(关键逻辑): $$ progress = \frac{touchX - P_{start}}{trackWidth} \times max $$ 其中 $trackWidth = W_{total} - P_{start} - P_{end}$([详解见前文](#trackwidth-详解含义计算方式与技术应用))[^1][^4] --- #### 三、自定义样式深度实践 1. **轨道高度问题解决方案** 通过 `progressDrawable` 自定义高度(引用[5]避坑指南): ```xml <!-- res/drawable/custom_track.xml --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background" android:height="8dp"> <!-- 背景轨道高度 --> <shape android:shape="rectangle"> <corners android:radius="4dp"/> <solid android:color="#E0E0E0"/> </shape> </item> <item android:id="@android:id/progress" android:height="8dp"> <!-- 进度条轨道高度 --> <scale android:scaleWidth="100%"> <shape android:shape="rectangle"> <solid android:color="#FF5722"/> </shape> </scale> </item> </layer-list> ``` 应用样式:`android:progressDrawable="@drawable/custom_track"` 2. **滑块(Thumb)自定义** ```xml <SeekBar ... android:thumb="@drawable/custom_thumb" <!-- 自定义滑块图标 --> android:splitTrack="false" /> <!-- 避免滑块切割轨道 --> ``` 3. **刻度模式(TickMark)** ```xml <SeekBar android:tickMark="@drawable/tick_mark" <!-- 刻度图标 --> android:tickMarkTintMode="src_over" /> ``` --- #### 四、高频问题解决方案 | 问题现象 | 原因分析 | 解决方案 | |---------------------------|---------------------------|--------------------------------------------------------------------------| | 轨道高度设置无效 | 需在 `progressDrawable` 中定义高度 | 在 XML 的 `<item>` 中设置 `android:height`(参考前文自定义样式)[^5] | | 页面切换后进度不显示 | 进度未持久化 | 在 `onSaveInstanceState()` 中保存状态 | | 滑块超出边界 | `padding` 计算错误 | 正确计算 `trackWidth`(公式见第二节)[^1] | | 触摸精度偏差 | 未处理手势事件坐标系 | 重写 `onTouchEvent` 并转换坐标[^2] | --- #### 五、高级功能扩展 1. **手势增强(引用[1])** ```kotlin // 扩展双指缩放调整进度 seekBar.setOnTouchListener { v, event -> if (event.pointerCount == 2) { val scaleFactor = calculateScale(event) // 计算缩放比例 seekBar.progress = (seekBar.max * scaleFactor).toInt() true } else false } ``` 2. **动态反馈动画** ```kotlin ObjectAnimator.ofInt(seekBar, "progress", 0, 100).apply { duration = 1000 interpolator = OvershootInterpolator() start() } ``` 3. **不规则轨道绘制(引用[2])** 重写 `onDraw()` 实现波浪形/圆形轨道: ```kotlin override fun onDraw(canvas: Canvas) { val centerY = height / 2f val path = Path().apply { moveTo(paddingStart, centerY) cubicTo(...) // 贝塞尔曲线绘制波浪 } canvas.drawPath(path, progressPaint) } ``` --- #### 六、性能优化要点 1. **避免频繁重绘** 在 `onProgressChanged()` 中限制刷新频率: ```kotlin private var lastUpdateTime = 0L override fun onProgressChanged(...) { if (SystemClock.elapsedRealtime() - lastUpdateTime > 50) { updateViews() lastUpdateTime = SystemClock.elapsedRealtime() } } ``` 2. **内存优化** 复用 `Bitmap` 资源: ```kotlin val thumbBitmap = BitmapFactory.decodeResource(resources, R.drawable.thumb) // 在onDraw中使用canvas.drawBitmap(thumbBitmap, ...) ``` > **最佳实践总结**:结合 `trackWidth` 精确控制交互逻辑,通过 `progressDrawable` 深度定制视觉效果,利用事件监听实现动态反馈[^1][^4][^5]。 --- ### 相关问题 1. 如何实现SeekBar进度值的双向绑定(如MVVM架构)? 2. 当SeekBar嵌入RecyclerView时如何避免性能问题? 3. 如何为SeekBar添加磁性吸附到刻度的功能? 4. 自定义垂直方向SeekBar的关键实现步骤是什么? [引用说明] [^1]: 自定义功能扩展参考SeekBarDemo项目 [^2]: 不规则轨道实现源自刻度盘案例 [^4]: 基础使用指南 [^5]: 样式问题解决方案
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值