Android ImageView.setMatrix()和.invalidate() - 重新绘制需要花费太多时间

时间:2015-10-04 19:55:24

标签: java android imageview invalidation scaletype

任务:我想调整大小并在屏幕上移动图片。无论图像有多大,我都想顺利地做到这一点。该API应该由API级别8支持。

问题:我尝试将ImageViewscaleType="matrix"一起使用。调用ImageView.setMatrix()然后调用ImageView.invalidate()可以很好地处理小图像,但对于大图像则很糟糕。无论ImageView有多大。

我可以以某种方式加速重新绘制ImageView,以便不会重新计算整个图像吗?也许有一种方法可以使用不同的组件来完成任务?

编辑:有关我想要实现的目标的更多信息。

  • pw,ph - 图片的宽度和高度(以像素为单位)
  • dw,dh - 设备显示的宽度和高度(以像素为单位)
  • fw,fh - 可见帧的宽度和高度(以像素为单位)
  • x,y - 框架左上角的位置(以像素为单位) Visualisation of the problem.

我想在屏幕上显示部分图像。属性 x y fw fh 正在不断变化。 我正在寻找代码的一部分(想法),或者这8个指定变量的组件将快速生成并显示图像的一部分。

编辑2:关于 pw ph 的信息

我假设 pw ph 可以保存从1到无穷大的值。如果这种方法造成很多麻烦,我们可以假设图片不比用设备相机拍摄的图片大。

3 个答案:

答案 0 :(得分:2)

在您的帮助下(社区),我找到了解决方案。 我确信还有其他更好的方法可以做到,但我的解决方案不是很复杂,应该适用于任何图像,任何Android自API级别8以来。

解决方案是使用两个Pushpin locationPushpin = new Pushpin(); locationPushpin.Background = new SolidColorBrush(Colors.Purple); locationPushpin.Content = "You are here"; locationPushpin.Tag = "locationPushpin"; locationPushpin.Location = watcher.Position.Location; this.map.Children.Add(locationPushpin); this.map.SetView(watcher.Position.Location, 18.0); 个对象而不是一个。

第一个ImageView将像以前一样工作,但加载的图像将按比例缩小,以使其宽度小于ImageView的宽度,并且它的高度将小于高度ImageView

第二个ImageView在开始时将为空白。每当 x y fw fh 属性发生变化时,ImageView将被执行仅加载图像的可见部分。当属性快速变化时,AsyncTask将无法及时完成。它必须被取消,新的将被启动。完成后,结果AsyncTask将加载到第二个Bitmap,以便用户可以看到。当属性发生更改时,加载的ImageView将被删除,因此它不会涵盖移动Bitmap加载到第一个Bitmap注意: ImageView我将用于加载子图像,因为Android API级别为10,因此API 8和API 9用户只能看到按比例缩小的图像。我觉得没事。

所需代码:

  • 设置第一个(底部)BitmapRegionDecoder ImageView(最好用XML)
  • 设置第二个(顶部)scaleType="matrix" ImageView(最好用XML)
  • Android文档中的功能(here) - 感谢用户Vishavjeet Singh

注意:在计算scaleType="fitXY"时,请注意||运算符而不是&&。我们希望加载的图像小于inSampleSize,以便我们确信我们有足够的RAM来加载它。 (我认为ImageView大小不大于设备显示的大小。我还假设设备有足够的内存来加载至少2 ImageView的设备显示大小。请告诉我如果我在这里犯了错误。)
注2:我正在使用Bitmaps加载图片。要以不同的方式加载文件,您必须更改InputStream块中的代码。

try{...} catch(...){...}
  • 返回图像子图像的功能。

注意:将从源图像中剪切出来的矩形大小相对于图像。指定它的值从0到1,因为public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image 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; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight || (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } public Bitmap decodeSampledBitmapFromResource(Uri fileUri, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; try { InputStream is = this.getContentResolver().openInputStream(fileUri); BitmapFactory.decodeStream(is, null, options); } catch (Exception e) { e.printStackTrace(); return null; } // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; try { InputStream is = this.getContentResolver().openInputStream(fileUri); return BitmapFactory.decodeStream(is, null, options); } catch (Exception e) { e.printStackTrace(); return null; } } 和加载的ImageView的大小与原始图像的大小不同。

Bitmap
  • public Bitmap getCroppedBitmap (Uri fileUri, int outWidth, int outHeight, double rl, double rt, double rr, double rb) { // rl, rt, rr, rb are relative (values from 0 to 1) to the size of the image. // That is because image moving will be smaller than the original. if (Build.VERSION.SDK_INT >= 10) { // Ensure that device supports at least API level 10 // so we can use BitmapRegionDecoder BitmapRegionDecoder brd; try { // Again loading from URI. Change the code so it suits yours. InputStream is = this.getContentResolver().openInputStream(fileUri); brd = BitmapRegionDecoder.newInstance(is, true); BitmapFactory.Options options = new BitmapFactory.Options(); options.outWidth = (int)((rr - rl) * brd.getWidth()); options.outHeight = (int)((rb - rt) * brd.getHeight()); options.inSampleSize = calculateInSampleSize(options, outWidth, outHeight); return brd.decodeRegion(new Rect( (int) (rl * brd.getWidth()), (int) (rt * brd.getHeight()), (int) (rr * brd.getWidth()), (int) (rb * brd.getHeight()) ), options); } catch (Exception e) { e.printStackTrace(); return null; } } else return null; } 加载子图像AsyncTask

注意:请注意声明此类类型的变量。它将在以后使用。

Bitmap
  • 使这一切能够协同工作的功能。

每次 x y fw fh 属性更改时,都会调用此函数。<登记/> 注意: hiresImage 在我的代码中是private LoadHiResImageTask loadHiResImageTask = new LoadHiResImageTask(); private class LoadHiResImageTask extends AsyncTask<Double, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(Double... numbers) { return getCroppedBitmap( // You will have to change first parameter here! Uri.parse(imagesToCrop[0]), numbers[0].intValue(), numbers[1].intValue(), numbers[2], numbers[3], numbers[4], numbers[5]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { ImageView hiresImage = (ImageView) findViewById(R.id.hiresImage); hiresImage.setImageBitmap(result); hiresImage.postInvalidate(); } } 的第二个(顶部)id

ImageView

答案 1 :(得分:1)

尝试在解码边界中通过BitmapFactory选项加载源位图

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    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;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

写完这两个方法之后

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

像image this一样在imageview上设置位图

然后尝试缩放它将有效地为大位图做的图像

你可以在这里进一步阅读

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

答案 2 :(得分:1)

正如您已经说过的,您可以剪裁大图像并将其逐个放置到新的imageView。

但是从另一方面我可以建议你将你的大图像切成几个小图像,在这种情况下你可能会节省内存和速度,因为你不会将整个图像加载到你的记忆中。

您将获得与谷歌地图相似的东西 - 它们有许多按要求加载的小块。他们不会加载整个世界地图,而是加载它的一小部分。

在这种情况下,当每个新项目成为图像视图并包含一些大图像的小图像时,您将构建类似AC_OUTPUT的内容。顺便说一句,通过这种方法,您甚至可以获得重复的平铺背景并在运行时更改它们。