Перетворення подання в Bitmap, не відображаючи його в Android?


133

Я спробую пояснити, що саме мені потрібно зробити.

У мене є 3 окремі екрани: A, B, C. Існує ще один екран з назвою HomeScreen, де всі 3-х екранні растрові карти повинні відображатися в режимі перегляду Галерея, і користувач може вибрати, в якому вікні він хоче перейти.

Мені вдалося отримати растрові карти з усіх 3-х екранів і відобразити їх у поданні Галерея, розмістивши весь код лише у розділі Діяльність на домашньому екрані. Тепер це дуже ускладнило код, і я хотів би його спростити.

Отже, чи можу я зателефонувати на іншу активність з HomeScreen і не відображати її та просто отримати Bitmap цього екрану. Наприклад, скажіть, що я просто дзвоню на HomeScreen, і він викликає активність A, B, C, і жодна діяльність з A, B, C не відображається. Він просто дає Bitmap цього екрану за допомогою getDrawingCache (). І тоді ми можемо відобразити ці растрові карти в перегляді Галерея на HomeScreen.

Я сподіваюся, що я пояснив проблему дуже чітко.

Будь ласка, дайте мені знати, чи це насправді можливо.


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

5
Власне, мені вдалося це зробити.
суніль

О, як ви можете викликати цю діяльність, але не показувати її? Чи можу я взяти макет поточної активності як шаблон для генерування растрових зображень під час подачі на нього різного вмісту?
zionpi

Перевірте відповідь на цей пост, я знайшов якесь - то рішення: stackoverflow.com/questions/36424381 / ...
Wackaloon

Відповіді:


213

є спосіб це зробити. ви повинні створити Bitmap та Canvas та визивати view.draw (полотно);

ось код:

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

EDIT: згідно з цим повідомленням , передача 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();.
Браїс Габін

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

Це застаріло
Ч. Вас

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

Ура,

Леві


Ура! Здається, що потрібно викликати міру та просити макет після зміни будь-яких змін у макеті для їх відображення
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 Я не розумію питання.
андроїд розробник

З моєї вини, TextView не був у растровій карті. Оскільки у мене була застосована тема світлого кольору, дякую за відповідь.
Хемрай

1
@Khemraj Вибачте, але я все ще не розумію. Все гаразд?
андроїд розробник

Так, брате, я не знаю, чому ти мене не отримуєш :). У мене був один TextView в макеті, який я хотів перетворити в Bitmap. У макеті було один ImageView і один TextView. ImageView перетворювався на Bitmap. Але TextView не відображався в Bitmap. Це була проблема. Після цього я зрозумів, що у мене застосована тема, яка робить текст TextView білим. Я полагодив це. І все нормально зараз. Дякую.
Хемрадж

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.