2010-09-04 22 views
19

¿Cuál es la mejor manera de verificar si la vista es visible en la ventana?¿Cuál es la mejor manera de verificar si la vista es visible en la ventana?

Tengo un CustomView que es parte de mi SDK y cualquiera puede agregar CustomView a sus diseños. My CustomView está tomando algunas medidas cuando el usuario lo puede ver periódicamente. Entonces, si la vista se vuelve invisible para el usuario, entonces debe detener el temporizador y, una vez que se vuelva a ver, debería reiniciar su curso.

Desafortunadamente, no hay una forma segura de verificar si mi CustomView se vuelve visible o invisible para el usuario. Hay pocas cosas que puedo verificar y escuchar:

onVisibilityChange //it is for view's visibility change, and is introduced in new API 8 version so has backward compatibility issue 
onWindowVisibilityChange //but my CustomView can be part of a ViewFlipper's Views so it can pose issues 
onDetachedFromWindows //this not as useful 
onWindowFocusChanged //Again my CustomView can be part of ViewFlipper's views.
Entonces, si alguien se ha enfrentado a este tipo de problemas, por favor arroje algo de luz.

Respuesta

7

onDraw() se llama cada vez que se necesita dibujar la vista. Cuando la vista está fuera de la pantalla, nunca se llama a onDraw(). Cuando un poco de la vista se vuelve visible para el usuario, se llama a onDraw(). Esto no es ideal, pero no puedo ver otra llamada para usar ya que quiero hacer lo mismo. Recuerde llamar al super.onDraw o la vista no se dibujará. Tenga cuidado de cambiar cualquier cosa en onDraw que causa la invalidación de la vista, ya que eso provocará otra llamada a onDraw.

Si está utilizando una vista de lista, entonces se puede usar getView siempre que se muestre su vista de lista al usuario.

obviamente se llama actividad onPause() todas sus vistas están tapadas y no son visibles para el usuario. quizás llamando a invalidate() en el padre y si no se llama a ondraw() entonces no está visible.

+2

también que tenía que hacer [esto] (http://stackoverflow.com/questions/7781892/own -defined-layout-ondraw-method-not-getting-calling/7784369 # 7784369) para que esto funcione –

4

Este es un método que he utilizado un poco en mis aplicaciones y he tenido funcionó bastante bien para mí:

static private int screenW = 0, screenH = 0; 

@SuppressWarnings("deprecation") static public boolean onScreen(View view) { 
    int coordinates[] = { -1, -1 }; 
    view.getLocationOnScreen(coordinates); 

    // Check if view is outside left or top 
    if (coordinates[0] + view.getWidth() < 0) return false; 
    if (coordinates[1] + view.getHeight() < 0) return false; 

    // Lazy get screen size. Only the first time. 
    if (screenW == 0 || screenH == 0) { 
     if (MyApplication.getSharedContext() == null) return false; 
     Display display = ((WindowManager)MyApplication.getSharedContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); 
     try { 
      Point screenSize = new Point(); 
      display.getSize(screenSize); // Only available on API 13+ 
      screenW = screenSize.x; 
      screenH = screenSize.y; 
     } catch (NoSuchMethodError e) { // The backup methods will only be used if the device is running pre-13, so it's fine that they were deprecated in API 13, thus the suppress warnings annotation at the start of the method. 
      screenW = display.getWidth(); 
      screenH = display.getHeight(); 
     } 
    } 

    // Check if view is outside right and bottom 
    if (coordinates[0] > screenW) return false; 
    if (coordinates[1] > screenH) return false; 

    // Else, view is (at least partially) in the screen bounds 
    return true; 
} 

Para usarlo, sólo tiene que pasar en cualquier vista o subclase de vista (IE , casi cualquier cosa que se dibuje en la pantalla de Android.) Devolverá true si está en la pantalla o false si no es ... bastante intuitivo, creo.

Si no está utilizando el método anterior como estático, entonces probablemente pueda obtener un contexto de otra manera, pero para obtener el contexto de la aplicación desde un método estático, debe hacer estas dos cosas:

1 - Añadir el siguiente atributo a la etiqueta application en su manifiesto:

android:name="com.package.MyApplication" 

2 - Añadir en una clase que se extiende de aplicación, así:

public class MyApplication extends Application { 
    // MyApplication exists solely to provide a context accessible from static methods. 
    private static Context context; 

    @Override public void onCreate() { 
     super.onCreate(); 
     MyApplication.context = getApplicationContext(); 
    } 

    public static Context getSharedContext() { 
     return MyApplication.context; 
    } 
} 
+1

Una vista debe tener un contexto asociado (a través de getContext()), por lo que no puede usar eso en lugar del Aplicación/contexto compartido? Hacerlo ahorraría mucho código. – greg7gkb

+0

No he tocado Android desde 2012, pero recuerdo que necesitaba que este código fuera capaz de ejecutarse muy temprano en el proceso de lanzamiento, antes de que todos los contextos estuvieran asignados. No puedo recordar por qué lo necesitaba. – ArtOfWarfare

+0

Creo que este método solo verifica si la vista está dentro de los límites de la pantalla. Para detectar si la vista se muestra realmente, deberá combinarla con un isShown(). –

3

Además de view.getVisibility(), hay view.isShown().
isShown comprueba el árbol de vista para determinar si todos los antepasados ​​también están visibles.

Aunque, esto no maneja las vistas obstruidas, solo las vistas que están ocultas o han desaparecido en sí mismas o en uno de sus elementos principales.

+0

Esto realmente me ayudó (view.isShown()). Gracias +1. –

6

En mi caso el siguiente código funciona mejor para escuchar si la vista es visible o no:

@Override 
protected void onWindowVisibilityChanged(int visibility) { 
    super.onWindowVisibilityChanged(visibility); 
    Log.e(TAG, "is view visible?: " + (visibility == View.VISIBLE)); 
} 
-1

se puede añadir a su constractor de CustomView un un onScrollChangedListener from ViewTreeObserver

así que si su vista se desplaza de la pantalla se puede llamar view.getLocalVisibleRect() y determinar si la vista es en parte fuera de la pantalla ...

se puede echar un vistazo al código de mi biblioteca: PercentVisibleLayout

Espero que ayude!

0

Al tratar con un problema similar, en el que necesitaba saber si la vista tiene alguna otra ventana en la parte superior de la misma, que utiliza esto en mi costumbre Vista:

@Override 
public void onWindowFocusChanged(boolean hasWindowFocus) { 
    super.onWindowFocusChanged(hasWindowFocus); 
    if (!hasWindowFocus) { 

    } else { 

    } 
} 
0

en la vista personalizada, establezca la oyentes:

getViewTreeObserver().addOnScrollChangedListener(this); 
getViewTreeObserver().addOnGlobalLayoutListener(this); 

Estoy utilizando este código para animar una vista una vez cuando esté visible para el usuario.

Se deben considerar 2 casos.

  1. Su vista no aparece en la pantalla. Pero será visible si el usuario desplaza que

    public void onScrollChanged() { 
        final int i[] = new int[2]; 
        this.getLocationOnScreen(i); 
        if (i[1] <= mScreenHeight - 50) { 
         this.post(new Runnable() { 
          @Override 
          public void run() { 
           Log.d("ITEM", "animate"); 
           //animate once 
           showValues(); 
          } 
         }); 
         getViewTreeObserver().removeOnScrollChangedListener(this); 
         getViewTreeObserver().removeOnGlobalLayoutListener(this); 
        } 
    } 
    
  2. Su punto de vista se encuentra inicialmente en la pantalla. (No en algún otro lugar invisible para el usuario en ScrollView, es en un principio en la pantalla y visible para el usuario)

    public void onGlobalLayout() { 
    final int i[] = new int[2]; 
    this.getLocationOnScreen(i); 
    if (i[1] <= mScreenHeight) { 
        this.post(new Runnable() { 
         @Override 
         public void run() { 
          Log.d("ITEM", "animate"); 
          //animate once 
          showValues(); 
         } 
        }); 
        getViewTreeObserver().removeOnGlobalLayoutListener(this); 
        getViewTreeObserver().removeOnScrollChangedListener(this); 
    } 
    } 
    
1

Esto se puede comprobar utilizando el método getGlobalVisibleRect. Si el rectángulo devuelto por este método tiene exactamente el mismo tamaño que View tiene, entonces el actual View es completamente visible en la pantalla.

/** 
* Returns whether this View is completely visible on the screen 
* 
* @param view view to check 
* @return True if this view is completely visible on the screen, or false otherwise. 
*/ 
public static boolean onScreen(@NonNull View view) { 
    Rect visibleRect = new Rect(); 
    view.getGlobalVisibleRect(visibleRect); 
    return visibleRect.height() == view.getHeight() && visibleRect.width() == view.getWidth(); 
} 

Si es necesario calcular porcentaje de visibilidad puede hacerlo mediante el cálculo cuadrado:

float visiblePercentage = (visibleRect.height() * visibleRect.width())/(float)(view.getHeight() * view.getWidth()) 
Cuestiones relacionadas