Как программно повторно применить изображение с 9 патчами к ImageView?

У меня есть активность, в которой ImageView определен внутри HorizontalScrollView. Источником изображения является файл с 9 исправлениями, который ограничивается растягиванием только правого края до заполнения экрана. Я реализовал простую функцию масштабирования, которая позволяет пользователю дважды нажимать для увеличения и уменьшения масштаба, изменяя размер растрового изображения и назначая новое растровое изображение представлению. Моя текущая проблема заключается в том, что при двойном нажатии для уменьшения масштаба 9-патч не применяется, когда я назначаю новое растровое изображение с измененным размером для представления. Другими словами, вместо того, чтобы растягивать только правый край, как определено в файле с 9 патчами, он растягивал все изображение.

Вот мой XML:

<HorizontalScrollView
            android:id="@+id/hScroll"
            android:fillViewport="true"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:fadingEdge="none" >

            <RelativeLayout
                android:id="@+id/rlayoutScrollMap"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" >

                <ImageView
                    android:id="@+id/imgResultMap"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    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 патчей, который не является частью растрового изображения, чтобы воссоздать новый отрисовываемый фрагмент из 9 патчей. См. пример кода ниже:

    ...
 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);
 }

  ....

person Kenny    schedule 16.05.2012    source источник


Ответы (5)


РЕДАКТИРОВАНИЕ: после некоторых исследований я понял это. Вместо того, чтобы просто манипулировать растровым изображением, мне нужно также включить фрагмент из 9 патчей, который не является частью растрового изображения, чтобы воссоздать новый отрисовываемый фрагмент из 9 патчей. См. пример кода ниже:

    ...
 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. Для тех, кто смотрит на мое решение здесь, также обратите внимание на предложения Кая ниже по управлению памятью. Очень полезная информация.

person Kenny    schedule 17.05.2012
comment
Отлично, мне нужно было произвести некоторые манипуляции с ресурсом 9patch, и без этого фрагмента кода я действительно не знал, с чего начать! Спасибо! - person lorenzo-s; 01.10.2014
comment
Попробуйте использовать setImageResource(9patchResourceID) вместо setImageBitmap или setImageBackground, и это сработает :) - person Shilpi; 16.10.2015

Почему бы вам просто не включить два разных масштабированных изображения и переключаться между ними с помощью imgResultMap.setImageResource(resId) при масштабировании? Также обратите внимание, что вы загружаете и создаете растровые изображения в UIThread, что не является хорошим способом обеспечить бесперебойную работу пользователя, по крайней мере, предварительно загрузите растровое изображение только один раз во время onCreate() и кэшируйте его.

person Kai    schedule 17.05.2012
comment
Это хороший момент. Однако у меня есть около 35 элементов, для которых созданы масштабированные версии, подобные тем, что я показал выше, и я выполняю сборку мусора сразу после создания каждой масштабированной версии (используя v.recycle()). Меня беспокоит то, что когда я создаю масштабированную версию во время onCreate(), есть ли вероятность того, что я сразу же получу ошибку OOM, потому что по существу необходимо хранить обе версии в памяти? - person Kenny; 17.05.2012
comment
Вы должны быть в состоянии рассчитать объем памяти, удерживаемый для каждого изображения, используя img_width*img_height*4, чтобы увидеть, сколько памяти займут эти 35 элементов, а затем решить, имеет ли смысл загружать обе версии в память при однажды. - person Kai; 18.05.2012
comment
Для загрузки всех этих изображений в действии требуется в общей сложности ~ 2,4 МБ, поэтому, возможно, их можно создать заранее. Когда я выйду из этого действия, будет ли автоматически выполняться сборка мусора, чтобы освободить память для других моих действий? Кроме того, реалистично ли предположить, что современные телефоны и планшеты позволяют использовать только 24 МБ динамической памяти для каждого приложения? Я помню, когда тестировал одно из моих других действий в приложении в эмуляторе с кучей всего 24 МБ, я получил OOM, и мне пришлось увеличить размер кучи, чтобы продолжить тестирование. - person Kenny; 18.05.2012
comment
Прямо сейчас, вероятно, более старые и недорогие телефоны будут иметь ограничение памяти виртуальной машины на 24 МБ, но вы, вероятно, все равно захотите их поддерживать. Я предлагаю вам использовать LruCache с разным лимитом памяти в зависимости от лимита памяти виртуальной машины устройства (developer.android.com/reference/android/util/LruCache.html). Вы также можете добавить оболочку вокруг него, чтобы автоматически создавать отсутствующие растровые изображения, это может привести к сбою при создании объектов на устройствах с более низкой памятью в зависимости от того, как ведет себя ваша программа, но все же не должно быть хуже, чем то, что у вас было в худшем случае. - person Kai; 18.05.2012
comment
Спасибо за подсказку Кай. Рассмотрю класс LruCache, о котором вы упомянули. - person Kenny; 19.05.2012

Переместите это: ImageView imgResultMap = (ImageView)findViewById(R.id.imgResultMap); в глобальное значение, инициализированное в методе onCreate. В противном случае устройству придется искать вид и находить его каждый раз, когда на него нажимают.

и попробуйте вызвать imgResultMap.invalidate(); перед возвратом из метода onDoubleTap (http://developer.android.com/reference/android/view/View.html#invalidate%28%29)

person you786    schedule 16.05.2012
comment
Пожалуйста, смотрите мое собственное решение выше. - person Kenny; 17.05.2012

Кажется, что изображения с 9 патчами работают только тогда, когда они установлены в качестве фонового изображения View. Так:

  • вместо android:src установите android:background в XML макета и
  • вызовите setBackgroundResource(), когда вы хотите изменить изображение.

Вы также можете использовать простую View вместо ImageView, если хотите; это не имеет большого значения.

person mlc    schedule 01.04.2013

Также другим решением является программная установка изображения как ImageResource и установка scaleType.

imageView.setImageResource(R.drawable.favorite_driver_icon);
imageView.setScaleType(ScaleType.FIT_XY);
person ConJoBa    schedule 19.12.2013