Преобразование представления в растровое изображение без его отображения в Android?


133

Я постараюсь объяснить, что именно мне нужно сделать.

У меня есть 3 отдельных экрана: A, B, C. Существует еще один экран, называемый, скажем, HomeScreen, где все 3 растровых изображения экрана должны отображаться в представлении галереи, и пользователь может выбрать, в каком представлении он хочет перейти.

Мне удалось получить растровые изображения всех трех экранов и отобразить их в виде галереи, поместив весь код только в действие HomeScreen. Это сильно усложнило код, и я хотел бы его упростить.

Итак, могу ли я вызвать другое действие из HomeScreen и не отображать его, а просто получить растровое изображение этого экрана. Например, скажем, я просто вызываю HomeScreen, и он вызывает Activity A, B, C, и никакие действия из A, B, C не отображаются. Он просто дает Bitmap этого экрана с помощью getDrawingCache (). И затем мы можем отображать эти растровые изображения в режиме галереи на домашнем экране.

Надеюсь, я очень ясно объяснил проблему.

Пожалуйста, дайте мне знать, возможно ли это на самом деле.


1
Я не совсем уверен, но думаю, вы не сможете этого сделать. Проблема в том, что действия предназначены для отображения пользователю. Вы можете запустить действие, а затем сразу скрыть его, но действие все равно будет видно пользователю в течение доли секунды. Оно показывается достаточно долго, чтобы его можно было заметить, поэтому из-за того, что экран мерцает несколько раз, приложение выглядит непрофессионально. Однако вполне возможно, что есть команда для запуска действия, не отображая его; Я просто не знаю ни одного, существует ли он.
Стив Хейли

5
Собственно, мне это удалось.
Sunil

О, как вы можете вызвать это действие, но не показать его? Могу ли я взять макет текущего действия в качестве шаблона для генерации растрового изображения при загрузке в него другого контента?
zionpi 09

Проверьте ответ в этом сообщении, я нашел какое-то решение: stackoverflow.com/questions/36424381/…
Wackaloon

Ответы:


213

есть способ сделать это. вам нужно создать Bitmap и Canvas и вызвать view.draw (canvas);

вот код:

public static Bitmap loadBitmapFromView(View v) {
    Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);                
    Canvas c = new Canvas(b);
    v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
    v.draw(c);
    return b;
}

если представление не отображалось раньше, его размер будет равен нулю. Его можно измерить так:

if (v.getMeasuredHeight() <= 0) {
    v.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    v.draw(c);
    return b;
}

РЕДАКТИРОВАТЬ: согласно этому сообщению , передача в WRAP_CONTENTкачестве значения makeMeasureSpec()не приносит никакой пользы (хотя для некоторых классов представления это работает), и рекомендуемый метод:

// Either this
int specWidth = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.AT_MOST);
// Or this
int specWidth = MeasureSpec.makeMeasureSpec(0 /* any */, MeasureSpec.UNSPECIFIED);
view.measure(specWidth, specWidth);
int questionWidth = view.getMeasuredWidth();

1
Я пробовал это, но все, что у меня получилось, - это полупрозрачный черный ящик. Нужно ли мне что-то делать с видом, чтобы подготовить его к отрисовке растрового изображения?
Bobbake4

4
Мне на самом деле пришлось изменить это на, v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());чтобы заставить это работать правильно, но спасибо за код :)
триада

3
Мне пришлось использовать v.getWidth () вместо v.getLayoutParams (). Width и аналогичные по высоте. В остальном сейчас работает.
Дэвид Манперл

1
Я использовал v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
Brais Gabin

7
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);Работает лучше
Пьер

29

вот мое решение:

public static Bitmap getBitmapFromView(View view) {
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable bgDrawable =view.getBackground();
    if (bgDrawable!=null) 
        bgDrawable.draw(canvas);
    else 
        canvas.drawColor(Color.WHITE);
    view.draw(canvas);
    return returnedBitmap;
}

Наслаждаться :)


Спасибо. У меня были проблемы на некоторых устройствах, если высота превышала определенное значение. Я не тестировал полностью, но, похоже, это решает эту проблему.
BMF

22

Попробуй это,

/**
 * Draw the view into a bitmap.
 */
public static Bitmap getViewBitmap(View v) {
    v.clearFocus();
    v.setPressed(false);

    boolean willNotCache = v.willNotCacheDrawing();
    v.setWillNotCacheDrawing(false);

    // Reset the drawing cache background color to fully transparent
    // for the duration of this operation
    int color = v.getDrawingCacheBackgroundColor();
    v.setDrawingCacheBackgroundColor(0);

    if (color != 0) {
        v.destroyDrawingCache();
    }
    v.buildDrawingCache();
    Bitmap cacheBitmap = v.getDrawingCache();
    if (cacheBitmap == null) {
        Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
        return null;
    }

    Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);

    // Restore the view
    v.destroyDrawingCache();
    v.setWillNotCacheDrawing(willNotCache);
    v.setDrawingCacheBackgroundColor(color);

    return bitmap;
}

Как использовать его в моем основном классе активности?
Si8 06

Это устарело
Ch Vas

20

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

Вот метод, который в моем случае сработал. Однако с одной оговоркой. перед вызовом getViewBitmap(View)я раздул свой вид и попросил его разметку с известными размерами. Это было необходимо, так как мой макет вида будет иметь нулевую высоту / ширину, пока содержимое не будет помещено внутрь.

View view = LayoutInflater.from(context).inflate(layoutID, null);
//Do some stuff to the view, like add an ImageView, etc.
view.layout(0, 0, width, height);

Bitmap getViewBitmap(View view)
{
    //Get the dimensions of the view so we can re-layout the view at its current size
    //and create a bitmap of the same size 
    int width = view.getWidth();
    int height = view.getHeight();

    int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
    int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);

    //Cause the view to re-layout
    view.measure(measuredWidth, measuredHeight);
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

    //Create a bitmap backed Canvas to draw the view into
    Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);

    //Now that the view is laid out and we have a canvas, ask the view to draw itself into the canvas
    view.draw(c);

    return b;
}

«Волшебный соус» для меня был найден здесь: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

Ура,

Леви


Ура! Похоже, что после любых изменений макета нужно вызывать меры и requestLayout, чтобы они отображались
TheIT

1
Спасибо за это решение! У меня такая же проблема. Я использовал measure()и layout()до того, как заполнил свое представление, поэтому у меня были странные результаты. Переместив эти звонки вниз, выше createBitmap()исправил это для меня!
Свен Джейкобс

6

В Android KTX есть отличная функция расширения Kotlin: View.drawToBitmap(Bitmap.Config)


3
Это не сработает, если вид не представлен в макете. Ошибка: «IllegalStateException: перед вызовом drawToBitmap () необходимо разметить представление»
Val

2

Надеюсь это поможет

View view="some view instance";        
view.setDrawingCacheEnabled(true);
Bitmap bitmap=view.getDrawingCache();
view.setDrawingCacheEnabled(false);


getDrawingCache()Метод обновления устарел на уровне API 28. Поэтому ищите другую альтернативу для уровня API> 28.


1
getDrawingCacheв настоящее время не рекомендуется.
Дэвид Мигель

1

Макет или вид в растровое изображение:

 private Bitmap createBitmapFromLayout(View tv) {      
    int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    tv.measure(spec, spec);
    tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
    Bitmap b = Bitmap.createBitmap(tv.getMeasuredWidth(), tv.getMeasuredWidth(),
            Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    c.translate((-tv.getScrollX()), (-tv.getScrollY()));
    tv.draw(c);
    return b;
}

Метод вызова:

Bitmap src = createBitmapFromLayout(View.inflate(this, R.layout.sample, null)/* or pass your view object*/);

0

Думаю, это немного лучше:

/**
 * draws the view's content to a bitmap. code initially based on :
 * http://nadavfima.com/android-snippet-inflate-a-layout-draw-to-a-bitmap/
 */
@Nullable
public static Bitmap drawToBitmap(final View viewToDrawFrom, int width, int height) {
    boolean wasDrawingCacheEnabled = viewToDrawFrom.isDrawingCacheEnabled();
    if (!wasDrawingCacheEnabled)
        viewToDrawFrom.setDrawingCacheEnabled(true);
    if (width <= 0 || height <= 0) {
        if (viewToDrawFrom.getWidth() <= 0 || viewToDrawFrom.getHeight() <= 0) {
            viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            width = viewToDrawFrom.getMeasuredWidth();
            height = viewToDrawFrom.getMeasuredHeight();
        }
        if (width <= 0 || height <= 0) {
            final Bitmap bmp = viewToDrawFrom.getDrawingCache();
            final Bitmap result = bmp == null ? null : Bitmap.createBitmap(bmp);
            if (!wasDrawingCacheEnabled)
                viewToDrawFrom.setDrawingCacheEnabled(false);
            return result;
        }
        viewToDrawFrom.layout(0, 0, width, height);
    } else {
        viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        viewToDrawFrom.layout(0, 0, viewToDrawFrom.getMeasuredWidth(), viewToDrawFrom.getMeasuredHeight());
    }
    final Bitmap drawingCache = viewToDrawFrom.getDrawingCache();
    final Bitmap bmp = ThumbnailUtils.extractThumbnail(drawingCache, width, height);
    final Bitmap result = bmp == null || bmp != drawingCache ? bmp : Bitmap.createBitmap(bmp);
    if (!wasDrawingCacheEnabled)
        viewToDrawFrom.setDrawingCacheEnabled(false);
    return result;
}

Используя приведенный выше код, вам не нужно указывать размер растрового изображения (используйте 0 для ширины и высоты), если вы хотите использовать одно из самого представления.

Кроме того, если вы хотите преобразовать специальные представления (например, SurfaceView, Surface или Window) в растровое изображение, вам следует подумать об использовании вместо этого класса PixelCopy . Однако для этого требуется API 24 и выше. Я не знаю, как это сделать раньше.


Любая идея, TextView не добавляется в растровое изображение. Добавляются только ImageView.
Khemraj

@Khemraj Я не понимаю вопроса.
разработчик Android

Это была моя вина, что моего TextView не было в растровом изображении. Поскольку у меня была применена светлая цветовая тема, спасибо за ответ.
Khemraj

1
@Khemraj Простите, но я все еще не понимаю. Теперь все в порядке?
разработчик Android

Да, братан, я не знаю, почему ты меня не понимаешь :). У меня был один TextView в макете, который я хотел преобразовать в Bitmap. Макет имел один ImageView и один TextView. ImageView преобразовывался в Bitmap. Но TextView не отображался в Bitmap. Это была проблема. После этого я понял, что применил тему, которая делала текст TextView белым цветом. Я починил это. И теперь все в порядке. Спасибо.
Khemraj

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.