Drawable、Bitmap、Canvas、Paint和 Matrix 的关系和使用

本文深入解析Android平台显示类,包括Drawable、Bitmap、Canvas、Paint和Matrix之间的关系及使用方法,详细阐述了如何通过这些底层图形接口实现高效的图像处理与显示效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  由于对Drawable、Bitmap、Canvas、Paint和 Matrix 的关系和使用 一直不太清楚,就在网上搜集了一下,摘录一些,主要来看这两篇文章:
Drawable、Bitmap、Canvas和Paint的关系以及部分使用方法   和 Android显示系统之Pixel、Bitmap、Drawable、Canvas、Paint和Matrix之间的联系
   首先让我们理解下Android平台中的显示类是View,但是还提供了底层图形类android.graphics,今天所说的这些均为graphics底层图形接口。
Bitmap -- 称作位图,一般位图的文件格式后缀为bmp,当然编码器也有很多如RGB565、RGB888、ARGB8888。作为一种像素的显示对象 执行效率高,但是存储效率低的缺点也很明显。就理解为一种bmp格式图像存储对象。
Drawable -- 作为Android下通用的绘制图形对象,它可以装载常用格式的图像,比如GIF、PNG、JPG,当然也支持BMP,当然还提供一些高级的可视化对象,比如渐变、图形等。
Canvas -- 画布,我们可以看作是一种处理过程,使用各种方法来管理Bitmap、GL或者Path路径,同时它可以配合Matrix矩阵类给图像做旋转、缩放等操作,同时Canvas类还提供了裁剪、选取等操作。
Paint -- 可以把它看做一个画图工具,比如画笔、画刷。他管理了每个画图工具的字体、颜色、样式。 如果涉及一些Android游戏开发、显示特效可以通过这些底层图形类来高效实现自己的应用。

1. Drawable --> Bitmap
BitmapDrawable 继承自 Drawable

//方法一  
Resources res;  
InputStream is=res.openRawResource(R.drawable.pic);  
BitmapDrawable bitmapDrawable=new BitmapDrawable(is);  
Bitmap bmp=bitmapDrawable.getBitmap();  
  
//方法二  
Resources res;  
BitmapDrawable bitmapDrawable=(BitmapDrawable)res.getDrawable(R.drawable.pic);  
Bitmap bmp=bitmapDrawable.getBitmap();  
  
//方法三  
ImageView image;  
image.setImageBitmap(BitmapFactory.decodeStream(~~~~));  
BitmapDrawable bitmapDrawable=(BitmapDrawable)image.getDrawable();  
Bitmap bmp=bitmapDrawable.getBitmap(); 

2. Bitmap --> Drawable

Drawable d = new BitmapDrawable(bitmap);

3.从资源中获取Bitmap
① 通过decodeResource,这种方式不可取,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。
Resources res=getResources();
Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic);
② 通过BitmapFactory.decodeStream方法,创建出一个bitmap (推荐)
InputStream is = context.getResources().openRawResource(R.drawable.app_sample_code);
Bitmap bmp = BitmapFactory.decodeStream(is);
4. Bitmap --> byte[] 
private byte[] Bitmap2Bytes(Bitmap bm){   
    ByteArrayOutputStream byteos = new ByteArrayOutputStream();     
    bm.compress(Bitmap.CompressFormat.PNG, 100, byteos);     
	return byteos.toByteArray();   
}
5. byte[] --> Bitmap
private Bitmap Bytes2Bimap(byte[] byte){   
	if(byte.length!=0){   
		return BitmapFactory.decodeByteArray(byte, 0, byte.length);   
	   }   
	else {   
		return null;   
	}   
 }

5.id转化graphic.drawable
   Drawable drawable = activity.getResources().getDrawable(R.drawable.icon);

6.id转化成Bitmap
   Bitmap bitmap = BitmapFactory. decodeResource (Resources   res, int id)


7. Matrix
  Matrix为矩阵的意思,一般用来与Bitmap配合,实现图像的缩放、变形、扭曲等操作。

public static Bitmap scaleBitmap(Bitmap bitmap, int scalWidth, int scaleHeight) {    
        int w = bitmap.getWidth();    
        int h = bitmap.getHeight();    
        // 创建操作图片用的Matrix对象    
        Matrix matrix = new Matrix();    
        // 计算缩放比例    
        float sx= ((float) scaleWidth / w);    
        float sy= ((float) scaleHeight / h);    
        // 设置缩放比例    
        matrix.postScale(sx, sy);    
        // 建立新的bitmap,其内容是对原bitmap的缩放后的图   
        Bitmap scaleBmp = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);    
        return scaleBmp;    
    }    
Matrix类的其他典型方法。
boolean  postScale(float sx, float sy)//缩放  
boolean     postSkew(float kx, float ky)//扭曲  
boolean     postTranslate(float dx, float dy)//转换  
boolean     preConcat(Matrix other)//合并  
boolean     preRotate(float degrees)//旋转  


<think>好的,我现在需要解决用户提供的Kotlin代码中rotateBitmap无法显示的问题。首先,我应该仔细阅读代码,理解每个步骤的作用,然后找出可能的问题所在。 用户代码的大致流程是:从资源中解码一个位图,显示在original视图上。然后创建一个Matrix,设置旋转90度。接着创建一个与原始位图相同宽高的rotateBitmap,并用Canvas绘制应用了matrix的原始位图。最后将rotateBitmap设置到rotate视图上,但发现不显示。 首先,我应该检查Matrix使用是否正确。用户调用了matrix.postRotate(90f),这里可能的问题在于旋转的中心点。默认情况下,旋转是围绕坐标原点(0,0)进行的,这可能导致图片旋转后超出画布范围,从而不可见。正确的做法可能是需要调整Matrix的旋转中心,或者调整画布的大小以适应旋转后的图像。 接下来,观察创建rotateBitmap的方式。用户使用Bitmap.createBitmap(bitmap.width, bitmap.height, bitmap.config)来创建新位图,但原始图片旋转90度后,宽度高度会互换。例如,原本宽为W,高为H的图片,旋转90度后应该变为高为W,宽为H。因此,创建新位图时如果保持原始尺寸,会导致图像被裁剪或者显示不全。所以正确的做法应该是将新位图的宽高设置为原始的高宽,即交换两者的值。 另一个可能性是Canvas绘制时的参数是否正确。用户调用canvas.drawBitmap(bitmap, matrix, null),这里是否正确应用了matrix?可能需要检查matrix是否正确配置。另外,Paint参数为null是否会有影响,不过通常可以为null。 另外,可能的问题还有是否位图被正确创建。需要确认Bitmap.createBitmap的参数是否正确,特别是宽高是否合理。例如,原图宽100,高200,旋转后新位图宽应为200,高100,但用户代码中仍然使用原宽高,导致新位图尺寸不足以容纳旋转后的图像,可能被裁剪,导致显示空白。 再考虑旋转后的绘制位置。如果旋转导致图像超出画布范围,即使正确旋转了,也可能在画布外,所以需要调整Matrix的平移,或者使用合适的参数来确保绘制在正确的位置。例如,使用Matrix的postTranslate方法来调整位置。 总结可能的问题点: 1. rotateBitmap的宽高未交换,导致图像被裁剪。 2. Matrix旋转中心点问题,导致图像偏移出画布。 3. 绘制时未调整位置,导致图像在画布外。 解决方案步骤可能包括: - 交换rotateBitmap的宽高。 - 调整Matrix的旋转中心点为原始位图的中心,或调整绘制的位置。 - 使用Bitmap.createBitmap的正确尺寸,并正确应用Matrix。 例如,正确的方式应该是: val matrix = Matrix().apply { postRotate(90f, bitmap.width / 2f, bitmap.height / 2f) // 围绕中心旋转 } // 旋转后的宽高交换 val rotateBitmap = Bitmap.createBitmap(bitmap.height, bitmap.width, bitmap.config) // 或者使用更安全的方法,如: val rotatedWidth = bitmap.height val rotatedHeight = bitmap.width val rotateBitmap = Bitmap.createBitmap(rotatedWidth, rotatedHeight, bitmap.config) val canvas = Canvas(rotateBitmap) // 调整绘制的位置,可能需要在绘制时应用平移,或者Matrix已经处理了位置 或者,更简单的方法是使用Bitmap.createBitmap的另一个重载方法,直接应用Matrix并处理边缘情况: val matrix = Matrix().apply { postRotate(90f) } val rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) 但需要注意,Bitmap.createBitmap的这个方法可能返回null,如果旋转或缩放导致无法创建位图,所以需要检查。但用户原来的代码中并没有使用这个方法,而是手动创建画布绘制,这可能存在问题。 因此,正确的做法应该是调整新位图的宽高,并确保Matrix正确应用了旋转平移,使得图像在画布内可见。例如,当旋转90度后,图像的位置可能需要平移,否则可能绘制到画布外。例如,旋转后原点(0,0)处的图像部分可能不在可视区域,需要平移使得整个图像可见。 因此,修改后的代码可能是: val matrix = Matrix().apply { postRotate(90f) postTranslate(bitmap.height.toFloat(), 0f) // 假设旋转后需要向右平移原图的高度,以显示在画布内 } 或者,更准确的方式是,计算旋转后的位置,确保图像在画布中央。或者,直接使用Bitmap.createBitmap的正确方法。 另外,可能用户将旋转后的位图尺寸保持原图相同,导致无法容纳旋转后的图像,从而被裁剪。例如,原图是横向的,旋转90度后变为竖向,但新位图还是横向的尺寸,导致图像被截断,显示不全,看起来空白。 所以,解决这个问题的关键在于正确设置新位图的尺寸为交换后的宽高,并调整Matrix的旋转中心或平移,使得图像正确绘制在新位图内。 因此,正确的代码应该如下: val matrix = Matrix().apply { // 设置旋转中心为原图的中心点 setRotate(90f, bitmap.width / 2f, bitmap.height / 2f) } // 旋转后的尺寸是原图的高宽 val rotateBitmap = Bitmap.createBitmap(bitmap.height, bitmap.width, bitmap.config) val canvas = Canvas(rotateBitmap) // 调整绘制的位置,可能需要平移 matrix.postTranslate(-bitmap.width / 2f + rotateBitmap.width / 2f, -bitmap.height / 2f + rotateBitmap.height / 2f) canvas.drawBitmap(bitmap, matrix, null) 或者更简单的方式是,使用正确的旋转方法。例如,在创建Matrix后,先旋转,然后根据旋转后的尺寸创建位图,并调整绘制的位置。 或者,直接使用Bitmap.createBitmap的另一个方法: val rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) 但需要确保matrix正确,并且createBitmap的参数正确。 综上,用户的问题可能是因为rotateBitmap的尺寸没有调整,导致图像被裁剪,或者Matrix没有正确调整位置,导致绘制到画布外。解决方案是交换宽高,并调整Matrix的平移。</think>让我们逐步分析并解决`rotateBitmap`无法显示的问题: --- ### **问题分析** 1. **尺寸错误** - 原图旋转90度后,**宽度高度会互换**。若保持`rotateBitmap`尺寸与原图相同,旋转后的图像会被裁剪。 - *示例*:原图尺寸为 $width \times height$,旋转后应为 $height \times width$。 2. **旋转中心偏移** - 默认旋转围绕坐标原点 $(0,0)$,可能导致图像绘制到画布外,无法显示。 --- ### **解决方案** #### **步骤1:调整旋转后的位图尺寸** 交换原图的宽高创建新位图: ```kotlin val rotateBitmap = Bitmap.createBitmap(bitmap.height, bitmap.width, bitmap.config) ``` #### **步骤2:修正旋转中心与平移** 通过`Matrix`调整旋转中心,并平移图像到画布内: ```kotlin val matrix = Matrix().apply { // 将旋转中心设为原图中心 postRotate(90f, bitmap.width / 2f, bitmap.height / 2f) // 平移图像到新位图中心 postTranslate( (rotateBitmap.width - bitmap.width) / 2f, (rotateBitmap.height - bitmap.height) / 2f ) } ``` #### **优化代码(完整版)** ```kotlin private fun initView() { val bitmap = BitmapFactory.decodeResource(resources, R.drawable.snipaste) binding.original.setImageBitmap(bitmap) // 创建Matrix并设置旋转(中心为原图中心) val matrix = Matrix().apply { postRotate(90f, bitmap.width / 2f, bitmap.height / 2f) } // 创建正确尺寸的旋转位图(交换宽高) val rotateBitmap = Bitmap.createBitmap( bitmap.height, bitmap.width, bitmap.config ) // 绘制到新位图 val canvas = Canvas(rotateBitmap) canvas.drawBitmap(bitmap, matrix, null) binding.rotate.setImageBitmap(rotateBitmap) } ``` --- ### **原理说明** - **尺寸调整**:旋转后图像的宽高互换,新位图需匹配新尺寸避免裁剪。 - **矩阵变换**: - `postRotate(angle, px, py)` 围绕原图中心旋转,避免偏移。 - 通过平移确保旋转后的图像位于新画布中央。 --- ### **其他注意事项** - **性能优化**:若处理大图,建议在子线程进行,避免主线程卡顿。 - **资源释放**:不再使用的`Bitmap`应及时调用`recycle()`。 通过以上调整,`rotateBitmap`应能正常显示旋转后的图像。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值