Convertir une vue en Bitmap sans l'afficher dans Android?

133

Je vais essayer d'expliquer exactement ce que je dois faire.

J'ai 3 écrans séparés: A, B, C. Il y a un autre écran appelé HomeScreen où tous les 3 écrans bitmap doivent être affichés dans la vue Galerie et l'utilisateur peut sélectionner dans quelle vue il veut aller.

J'ai pu obtenir les bitmaps de tous les 3 écrans et les afficher dans la vue Galerie en plaçant tout le code dans l'activité de l'écran d'accueil uniquement. Maintenant, cela a beaucoup compliqué le code et j'aimerais le simplifier.

Alors, puis-je appeler une autre activité à partir de HomeScreen et ne pas l'afficher et simplement obtenir le Bitmap de cet écran. Par exemple, disons que j'appelle juste HomeScreen et qu'il appelle l'activité A, B, C et qu'aucune des activités de A, B, C ne s'affiche. Il donne juste le Bitmap de cet écran par getDrawingCache (). Et puis nous pouvons afficher ces bitmaps dans la vue Galerie dans HomeScreen.

J'espère avoir expliqué le problème très clairement.

Veuillez me faire savoir si cela est réellement possible.

Sunil
la source
1
Je ne suis pas tout à fait sûr, mais je pense que vous ne pourrez pas faire cela. Le problème est que les activités sont destinées à être affichées pour l'utilisateur. Vous pouvez démarrer l'activité, puis la masquer immédiatement, mais l'activité sera toujours visible par l'utilisateur pendant une fraction de seconde. Il est montré assez longtemps pour être remarqué, donc le fait que l'écran scintille plusieurs fois donne à l'application un aspect non professionnel. Cependant, il est possible qu'il existe une commande pour démarrer une activité sans l'afficher; Je n'en connais tout simplement pas s'il existe.
Steve Haley
5
En fait, j'ai pu le faire.
dim
Oh, comment pouvez-vous appeler cette activité sans la montrer? Puis-je prendre la mise en page de l'activité actuelle comme modèle pour générer un bitmap tout en lui fournissant un contenu différent?
zionpi
Vérifiez la réponse dans cet article, j'ai trouvé une sorte de solution: stackoverflow.com/questions/36424381/…
Wackaloon

Réponses:

213

il existe un moyen de le faire. vous devez créer un Bitmap et un Canvas et appeler view.draw (canvas);

voici le code:

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

si la vue n'était pas affichée avant, sa taille sera égale à zéro. Il est possible de le mesurer comme ceci:

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: selon cet article , passer WRAP_CONTENTcomme valeur à makeMeasureSpec()ne sert à rien (bien que pour certaines classes de vue, cela fonctionne), et la méthode recommandée est:

// 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();
Simon
la source
1
J'ai essayé cela mais tout ce que j'obtiens est une boîte noire semi-transparente. Dois-je faire quelque chose sur la vue pour la préparer pour le dessin bitmap?
Bobbake4
4
En fait, j'ai dû changer cela pour v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());que cela fonctionne correctement, mais merci pour le code :)
triade
3
J'ai dû utiliser v.getWidth () au lieu de v.getLayoutParams (). Width et similaire pour la hauteur. Sinon, fonctionne maintenant.
David Manpearl
1
J'ai utilisé v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
Brais Gabin
7
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);Fonctionne mieux
Pierre
29

voici ma solution:

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

Prendre plaisir :)

Gil SH
la source
Merci. J'avais des problèmes sur certains appareils si la hauteur dépassait une certaine valeur. Je n'ai pas testé complètement mais cela semble résoudre cela.
BMF
22

Essaye ça,

/**
 * 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;
}
Dwivedi Ji
la source
Comment l'utiliser à partir de ma classe d'activité principale?
Si8
Ceci est obsolète
Ch Vas
20

Je sais que c'est peut-être un problème obsolète, mais j'avais des problèmes pour que l'une de ces solutions fonctionne pour moi. Plus précisément, j'ai constaté que si des modifications étaient apportées à la vue après son augmentation, ces modifications ne seraient pas incorporées dans le bitmap rendu.

Voici la méthode qui a fonctionné pour mon cas. Avec une mise en garde, cependant. avant d'appeler, getViewBitmap(View)j'ai gonflé ma vue et lui ai demandé de mettre en page avec des dimensions connues. Cela était nécessaire car ma disposition de vue en ferait une hauteur / largeur nulle jusqu'à ce que le contenu soit placé à l'intérieur.

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

La "sauce magique" pour moi a été trouvée ici: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

À votre santé,

Levi

levigroker
la source
À votre santé! Il semble qu'il faille appeler mesure et requestLayout après toute modification de la mise en page pour qu'ils soient affichés
TheIT
1
Merci pour cette solution! J'ai eu le même problème. J'utilisais measure()et layout()avant de peupler ma vue, j'avais donc des résultats étranges. Déplacer ces appels vers le bas, ci-dessus l'a createBitmap()corrigé pour moi!
Sven Jacobs
6

Il existe une excellente fonction d'extension Kotlin dans Android KTX: View.drawToBitmap(Bitmap.Config)

a.ch.
la source
3
Cela ne fonctionnera pas si la vue n'est pas présentée dans la mise en page. Erreur: "IllegalStateException: la vue doit être mise en page avant d'appeler drawToBitmap ()"
Val
2

J'espère que cela t'aides

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

La
getDrawingCache() méthode de mise à jour est obsolète au niveau de l'API 28. Recherchez donc une autre alternative pour le niveau d'API> 28.

Akhilesh Kumar
la source
1
getDrawingCacheest actuellement obsolète.
David Miguel
1

Disposition ou vue en bitmap:

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

Méthode d'appel:

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

Je pense que c'est un peu mieux:

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

En utilisant le code ci-dessus, vous n'avez pas à spécifier la taille du bitmap (utilisez 0 pour la largeur et la hauteur) si vous souhaitez utiliser celle de la vue elle-même.

De plus, si vous souhaitez convertir des vues spéciales (SurfaceView, Surface ou Window, par exemple) en bitmap, vous devez envisager d'utiliser la classe PixelCopy à la place. Cependant, il nécessite l'API 24 et plus. Je ne sais pas comment le faire avant.

développeur android
la source
N'importe quelle idée, aucun TextView n'est ajouté dans le bitmap. Seuls les ImageViews sont ajoutés.
Khemraj
@Khemraj Je ne comprends pas la question.
développeur android
C'était ma faute si TextView n'était pas là en bitmap. Parce que j'ai appliqué un thème de couleur claire, merci pour la réponse.
Khemraj
1
@Khemraj Désolé mais je ne comprends toujours pas. Tout va bien maintenant?
développeur android
Oui mon frère, je ne sais pas pourquoi tu ne me comprends pas :). J'avais un TextView en mise en page que je voulais convertir en Bitmap. La mise en page avait un ImageView et un TextView. ImageView était en train d'être converti en Bitmap. Mais TextView n'apparaissait pas dans Bitmap. C'était un problème. Après cela, j'ai réalisé que j'avais un thème appliqué qui rendait la couleur du texte TextView blanche. Je l'ai corrigé. Et tout va bien maintenant. Merci.
Khemraj
-3
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
Ashutosh Gupta
la source
Veuillez expliquer ce que cela fait.
Paul Floyd