Konwertowanie widoku na mapę bitową bez wyświetlania go w systemie Android?


137

Postaram się wyjaśnić, co dokładnie muszę zrobić.

Mam 3 oddzielne ekrany, na przykład A, B, C. Jest jeszcze jeden ekran o nazwie HomeScreen, na którym wszystkie 3 ekrany powinny być wyświetlane w widoku galerii, a użytkownik może wybrać, w którym widoku chce przejść.

Udało mi się uzyskać mapy bitowe wszystkich 3 ekranów i wyświetlić je w widoku galerii, umieszczając cały kod tylko w Aktywności ekranu głównego. To bardzo skomplikowało kod i chciałbym go uprościć.

Czy mogę więc wywołać inną aktywność z HomeScreen i nie wyświetlać jej, a po prostu pobrać mapę bitową tego ekranu. Na przykład, powiedzmy, że po prostu wywołuję HomeScreen i wywołuje działanie A, B, C i żadne działania z A, B, C nie są wyświetlane. Po prostu podaje bitmapę tego ekranu przez getDrawingCache (). Następnie możemy wyświetlić te mapy bitowe w widoku galerii w HomeScreen.

Mam nadzieję, że bardzo jasno wyjaśniłem problem.

Daj mi znać, jeśli jest to rzeczywiście możliwe.


1
Nie jestem do końca pewien, ale myślę, że nie będziesz w stanie tego zrobić. Problem polega na tym, że działania mają być wyświetlane użytkownikowi. Możesz rozpocząć działanie, a następnie natychmiast je ukryć, ale będzie ono nadal widoczne dla użytkownika przez ułamek sekundy. Jest wyświetlany wystarczająco długo, aby można go było zauważyć, więc kilkakrotne migotanie ekranu sprawia, że ​​aplikacja wygląda nieprofesjonalnie. Jednak może się zdarzyć, że istnieje polecenie uruchomienia działania bez jego wyświetlania; Po prostu nie wiem, czy taki istnieje.
— Steve Haley

5
Właściwie udało mi się to zrobić.
— sunil

Och, jak możesz nazwać to działanie, ale nie pokazywać go? Czy mogę przyjąć układ bieżącego działania jako szablon do generowania bitmapy, jednocześnie dostarczając do niego różne treści?
— zionpi,

Sprawdź odpowiedź w tym poście, znalazłem jakieś rozwiązanie: stackoverflow.com/questions/36424381/ ...
— Wackaloon

Odpowiedzi:


215

jest na to sposób. musisz stworzyć Bitmapę i Canvas i wywołać view.draw (canvas);

oto kod:

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

jeśli widok nie został wyświetlony przed jego rozmiarem, będzie wynosił zero. Można to zmierzyć w ten sposó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: według tego postu , Przechodząc WRAP_CONTENTjako wartość na makeMeasureSpec()nie coś dobrego (choć dla niektórych klas widzenia to działa), a metoda zalecana jest:

// 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
Próbowałem tego, ale wszystko, co otrzymałem, to półprzezroczyste czarne pudełko. Czy muszę coś zrobić z widokiem, aby przygotować go do rysowania bitmapowego?
— Bobbake4

4
Właściwie musiałem to zmienić, aby v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());to działało poprawnie, ale dzięki za kod :)
— triada

4
Musiałem użyć v.getWidth () zamiast v.getLayoutParams (). Width i podobnych dla wysokości. W przeciwnym razie teraz działa.
— David Manpearl

1
Użyłem v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
— Brais Gabin

7
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);Działa lepiej
— Pierre,

31

oto moje rozwiązanie:

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

Cieszyć się :)


Dzięki. Miałem problemy na niektórych urządzeniach, jeśli wysokość przekraczała określoną wartość. Nie przetestowałem w pełni, ale wydaje się, że to rozwiązuje.
— BMF

23

Spróbuj tego,

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

Jak go używać z mojej głównej klasy aktywności?
— Si8,

To jest przestarzałe
— Ch Vas

22

Wiem, że może to być nieaktualny problem, ale miałem problemy z uruchomieniem któregokolwiek z tych rozwiązań. W szczególności stwierdziłem, że jeśli w widoku zostaną wprowadzone jakiekolwiek zmiany po jego zawyżeniu, zmiany te nie zostaną uwzględnione w renderowanej mapie bitowej.

Oto metoda, która sprawdziła się w moim przypadku. Z jednym zastrzeżeniem. przed zadzwonieniem getViewBitmap(View)zawyżyłem swój widok i poprosiłem o układ ze znanymi wymiarami. Było to potrzebne, ponieważ mój układ widoku powodowałby zerową wysokość / szerokość do momentu umieszczenia treści w środku.

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

„Magiczny sos” dla mnie został znaleziony tutaj: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

Twoje zdrowie,

Levi


Twoje zdrowie! Wygląda na to, że po wszelkich zmianach w układzie trzeba wywołać Measure i
— requestLayout

1
Dzięki za to rozwiązanie! Miałem ten sam problem. Używałem measure()i layout()zanim wypełniłem widok, więc miałem dziwne wyniki. Przenosząc te wywołania w dół, powyżej createBitmap()naprawiłem to dla mnie!
— Sven Jacobs

9

W systemie Android KTX jest świetna funkcja rozszerzenia Kotlin: View.drawToBitmap(Bitmap.Config)


7
To nie zadziała, jeśli widok nie jest przedstawiony w układzie. Błąd: „IllegalStateException: należy określić widok przed wywołaniem funkcji drawToBitmap ()”
— Val

3

Układ lub widok do mapy bitowej:

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

Metoda wywołania:

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

2

Mam nadzieję że to pomoże

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


getDrawingCache()Metoda aktualizacji jest przestarzała na poziomie API 28. Poszukaj więc innej alternatywy dla poziomu API> 28.


2
getDrawingCachejest obecnie przestarzały.
— David Miguel,

1

Myślę, że to jest trochę lepsze:

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

Używając powyższego kodu, nie musisz określać rozmiaru bitmapy (użyj 0 dla szerokości i wysokości), jeśli chcesz użyć jednego z samego widoku.

Ponadto, jeśli chcesz przekonwertować specjalne widoki (na przykład SurfaceView, Surface lub Window) na mapę bitową, powinieneś zamiast tego rozważyć użycie klasy PixelCopy . Wymaga jednak API 24 i nowszych. Wcześniej nie wiem, jak to zrobić.


Każdy pomysł, żaden TextView nie jest dodawany do mapy bitowej. Dodawane są tylko ImageViews.
— Khemraj

@Khemraj Nie rozumiem pytania.
— programista Androida

To była moja wina, że ​​mojego TextView nie było w mapie bitowej. Ponieważ zastosowałem motyw w jasnych kolorach, dzięki za odpowiedź.
— Khemraj

1
@Khemraj Przepraszam, ale nadal nie rozumiem. Wszystko w porządku teraz?
— programista Androida

Tak bracie, nie wiem, dlaczego mnie nie dostajesz :). Miałem jeden TextView w układzie, który chciałem przekonwertować na Bitmap. Układ miał jeden ImageView i jeden TextView. ImageView był konwertowany na Bitmap. Ale TextView nie pojawiał się w Bitmap. To był problem. Po tym zdałem sobie sprawę, że zastosowałem motyw, który sprawiał, że tekst TextView był biały. Naprawiłem to. I teraz wszystko w porządku. Dzięki.
— Khemraj

-3
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);

Proszę wyjaśnij, co to robi.
— Paul Floyd,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.