211 votes

Comment savoir si un dessin a été effectué ?

J'ai une vue personnalisée qui dessine une image bitmap défilante à l'écran. Pour l'initialiser, je dois passer la taille en pixels de l'objet de disposition parent. Mais durant les fonctions onCreate et onResume, le Layout n'a pas encore été dessiné, et donc layout.getMeasuredHeight() renvoie 0.

Pour contourner le problème, j'ai ajouté un gestionnaire qui attend une seconde et mesure ensuite. Cela fonctionne, mais c'est bâclé, et je n'ai aucune idée de combien je peux réduire le temps avant de me retrouver avant que la mise en page ne soit dessinée.

Ce que je veux savoir, c'est comment détecter le moment où une mise en page est dessinée. Y a-t-il un événement ou un callback ?

411voto

blessenm Points 14700

Vous pouvez ajouter un observateur d'arbre à la mise en page. Celui-ci devrait renvoyer la largeur et la hauteur correctes. onCreate() est appelé avant que la mise en page des vues enfants ne soit terminée. La largeur et la hauteur ne sont donc pas encore calculées. Pour obtenir la hauteur et la largeur, placez ceci dans le fichier onCreate() méthode :

    final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
    ViewTreeObserver vto = layout.getViewTreeObserver(); 
    vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() { 
        @Override 
        public void onGlobalLayout() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    layout.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    layout.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
            int width  = layout.getMeasuredWidth();
            int height = layout.getMeasuredHeight(); 

        } 
    });

0 votes

Merci beaucoup ! Exactement l'exemple que je cherchais, y compris la suppression immédiate de l'auditeur au premier déclenchement.

0 votes

Oui, c'est ce que Romain Guy a dit de faire !

30 votes

Merci beaucoup ! Mais je me demande pourquoi il n'y a pas de méthode plus simple pour faire cela, car c'est un problème courant.

83voto

Elliptica Points 862

Une méthode très simple consiste à appeler simplement post() sur votre site. Cela exécutera votre code à l'étape suivante, après que votre vue ait déjà été créée.

YOUR_LAYOUT.post( new Runnable() {
    @Override
    public void run() {
        int width  = YOUR_LAYOUT.getMeasuredWidth();
        int height = YOUR_LAYOUT.getMeasuredHeight(); 
    }
});

25voto

murena Points 1066

Pour éviter le code déprécié et les avertissements, vous pouvez utiliser :

view.getViewTreeObserver().addOnGlobalLayoutListener(
        new ViewTreeObserver.OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    view.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    view.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
                yourFunctionHere();
            }
        });

12voto

Sergio Points 51

Amélioration des fonctions d'extension kotlin

inline fun View.waitForLayout(crossinline yourAction: () -> Unit) {
    val vto = viewTreeObserver
    vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            when {
                vto.isAlive -> {
                    vto.removeOnGlobalLayoutListener(this)
                    yourAction()
                }
                else -> viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}

6voto

Androidx.core-ktx contient déjà ceci

/**
 * Performs the given action when this view is next laid out.
 *
 * The action will only be invoked once on the next layout and then removed.
 *
 * @see doOnLayout
 */
public inline fun View.doOnNextLayout(crossinline action: (view: View) -> Unit) {
    addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
        override fun onLayoutChange(
            view: View,
            left: Int,
            top: Int,
            right: Int,
            bottom: Int,
            oldLeft: Int,
            oldTop: Int,
            oldRight: Int,
            oldBottom: Int
        ) {
            view.removeOnLayoutChangeListener(this)
            action(view)
        }
    })
}

/**
 * Performs the given action when this view is laid out. If the view has been laid out and it
 * has not requested a layout, the action will be performed straight away, otherwise the
 * action will be performed after the view is next laid out.
 *
 * The action will only be invoked once on the next layout and then removed.
 *
 * @see doOnNextLayout
 */
public inline fun View.doOnLayout(crossinline action: (view: View) -> Unit) {
    if (ViewCompat.isLaidOut(this) && !isLayoutRequested) {
        action(this)
    } else {
        doOnNextLayout {
            action(it)
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X