如何以编程方式将 9-patch 图像重新应用到 ImageView?
Posted
技术标签:
【中文标题】如何以编程方式将 9-patch 图像重新应用到 ImageView?【英文标题】:How to programmatically re-apply a 9-patch image to an ImageView? 【发布时间】:2012-05-24 07:30:15 【问题描述】:我有一个在 HorizontalScrollView 中定义的 ImageView 的活动。图像源是一个 9-patch 文件,它被限制为仅拉伸右边缘以填充屏幕。我已经实现了一个简单的缩放功能,它允许用户通过调整位图大小并将新位图分配给视图来放大和缩小。我当前的问题是,当我双击以缩小时,当我将新的调整大小的位图分配给视图时,不会应用 9-patch。换句话说,它不是仅拉伸 9-patch 文件中定义的右边缘,而是拉伸了整个图像。
这是我的 XML:
<HorizontalScrollView
android:id="@+id/hScroll"
android:fillViewport="true"
android:layout_
android:layout_
android:fadingEdge="none" >
<RelativeLayout
android:id="@+id/rlayoutScrollMap"
android:layout_
android:layout_ >
<ImageView
android:id="@+id/imgResultMap"
android:layout_
android:layout_
android:scaleType="fitXY"
android:src="@drawable/map_base"/>
</RelativeLayout>
</horizontalScrollView>
这是我的代码的相关部分,在 onDoubleTap() 调用中:
public boolean onDoubleTap(MotionEvent e)
if (zoom == 1)
zoom = 2; // zoom out
else
zoom = 1; // zoom in
Bitmap image = BitmapFactory.decodeResource(getResources(),R.drawable.map_base);
Bitmap bmp = Bitmap.createScaledBitmap(image, image.getWidth() * zoom, image.getHeight() * zoom, false);
ImageView imgResultMap = (ImageView)findViewById(R.id.imgResultMap);
imgResultMap.setImageBitmap(bmp);
return false;
编辑:在做了一些研究之后,我想通了。我不仅需要处理位图,还需要包含 9-patch 块,它不是位图图像的一部分,以重新构造一个新的 9-patch 可绘制对象。请参阅下面的示例代码:
...
else
// Zoom out
zoom = 1;
Bitmap mapBitmapScaled = mapBitmap;
// Load the 9-patch data chunk and apply to the view
byte[] chunk = mapBitmap.getNinePatchChunk();
NinePatchDrawable mapNinePatch = new NinePatchDrawable(getResources(),
mapBitmapScaled, chunk, new Rect(), null);
imgResultMap.setImageDrawable(mapNinePatch);
....
【问题讨论】:
【参考方案1】:编辑:在做了一些研究之后,我想通了。我不仅需要处理位图,还需要包含 9-patch 块,它不是位图图像的一部分,以重新构造一个新的 9-patch 可绘制对象。请参阅下面的示例代码:
...
else
// Zoom out
zoom = 1;
Bitmap mapBitmapScaled = mapBitmap;
// Load the 9-patch data chunk and apply to the view
byte[] chunk = mapBitmap.getNinePatchChunk();
NinePatchDrawable mapNinePatch = new NinePatchDrawable(getResources(),
mapBitmapScaled, chunk, new Rect(), null);
imgResultMap.setImageDrawable(mapNinePatch);
....
编辑#2:对于那些在此处查看我的解决方案的人,还请查看以下 Kai 关于内存管理的建议。非常有用的信息。
【讨论】:
太好了,我需要对 9patch 资源进行一些操作,如果没有这个 sn-p 代码,我真的不知道从哪里开始!谢谢! 尝试使用 setImageResource(9patchResourceID) 代替 setImageBitmap 或 setImageBackground,它可以工作 :)【参考方案2】:为什么不只包含两个不同的缩放图像并在缩放时使用imgResultMap.setImageResource(resId)
在它们之间切换?另请注意,您正在 UIThread 中加载和创建位图,这不是提供流畅用户体验的好方法,至少在 onCreate() 期间仅预加载一次位图并缓存它。
【讨论】:
这是一个很好的观点。但是,我有大约 35 个元素创建了类似于我上面显示的缩放版本,并且我在创建每个缩放版本之后立即进行垃圾收集(使用 v.recycle())。我担心的是,当我在 onCreate() 期间创建缩放版本时,我是否有可能会立即收到 OOM 错误,因为它本质上需要将两个版本都保存在内存中? 您应该能够通过使用 img_width*img_height*4 来计算每个图像占用的内存量,以查看这 35 个元素将占用多少内存,然后决定它是否有意义他们将两个版本同时加载到内存中。 在 Activity 中加载所有这些图像总共需要 ~2.4MB,所以也许可以预先创建它们。当我退出此活动时,是否会自动执行垃圾收集,以便为我的其他活动释放内存?此外,假设当前的手机和平板电脑将只允许每个应用程序使用 24MB 的堆内存是否仍然现实?我记得在只有 24MB 堆的模拟器中测试我在应用程序中的其他活动之一时,我确实得到了 OOM,我必须增加堆大小才能继续我的测试。 现在可能较旧和低端手机的 VM 内存限制设置为 24MB,但您可能仍希望支持它们。我建议您使用具有不同存储限制的 LruCache,具体取决于设备的 VM 内存限制 (developer.android.com/reference/android/util/LruCache.html)。您还可以在其周围添加一个包装器以自动创建丢失的位图,这可能会导致较低内存设备上的对象创建垃圾,具体取决于您的程序行为方式,但仍不应比最坏情况下的情况更糟。跨度> 感谢凯的提示。将研究您提到的 LruCache 类。【参考方案3】:移动这个:
ImageView imgResultMap = (ImageView)findViewById(R.id.imgResultMap);
到在 onCreate 方法中初始化的全局。否则,设备必须搜索视图并在每次点击时找到它。
并在从 onDoubleTap 方法返回之前尝试调用 imgResultMap.invalidate();
(http://developer.android.com/reference/android/view/View.html#invalidate())
【讨论】:
请看上面我自己的解决方案。【参考方案4】:9-patch 图片似乎只有在设置为View
的背景图片时才有效。所以:
android:background
而不是 android:src
,然后
如果您想更换图片,请致电setBackgroundResource()
。
如果需要,您也可以使用普通的 View
代替 ImageView
;没关系。
【讨论】:
【参考方案5】:另一种解决方案是以编程方式将图像设置为 ImageResource 并设置 scaleType
imageView.setImageResource(R.drawable.favorite_driver_icon);
imageView.setScaleType(ScaleType.FIT_XY);
【讨论】:
以上是关于如何以编程方式将 9-patch 图像重新应用到 ImageView?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 ios 4 中以编程方式将图像数组添加到 Imageview?