如何以编程方式将 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 【问题描述】:

我有一个在 Horizo​​ntalScrollView 中定义的 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 的背景图片时才有效。所以:

在您的布局 XML 中设置 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?的主要内容,如果未能解决你的问题,请参考以下文章

使用 ImageMagick 处理 9-Patch 图像

地址簿以编程方式保存联系人图像

Xamarin - 支持 9 补丁?

如何在 ios 4 中以编程方式将图像数组添加到 Imageview?

如何以编程方式将不同单元格的不同图像添加到 tableView (Swift)

如何以编程方式将矢量图像放入 iOS 资产目录