2010-09-04 12 views
19

Qual è il modo migliore per verificare se la vista è visibile sulla finestra?Qual è il modo migliore per verificare se la vista è visibile sulla finestra?

Ho un CustomView che fa parte del mio SDK e chiunque può aggiungere CustomView ai propri layout. My CustomView sta eseguendo alcune azioni quando è visibile all'utente periodicamente. Quindi se la vista diventa invisibile all'utente, allora ha bisogno di fermare il timer e quando diventa di nuovo visibile dovrebbe ricominciare il suo corso.

Ma sfortunatamente non esiste un modo specifico per verificare se il mio CustomView diventa visibile o invisibile all'utente. Ci sono alcune cose che posso controllare e ascoltare:

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.
Quindi, se qualcuno ha affrontato questo tipo di problemi, per favore getta un po 'di luce.

risposta

7

onDraw() viene chiamato ogni volta che è necessario disegnare la vista. Quando la vista è disattivata sullo schermo, onDraw() non viene mai chiamato. Quando un piccolo frammento della vista diventa visibile all'utente, viene chiamato onDraw(). Questo non è l'ideale, ma non riesco a vedere un'altra chiamata da utilizzare come voglio fare la stessa cosa. Ricordati di chiamare il super.onDraw o la vista non verrà disegnata. Fai attenzione a modificare qualsiasi cosa in onDraw che fa sì che la vista sia invalidata in quanto ciò causerà un'altra chiamata a onDraw.

Se si utilizza un listview, è possibile utilizzare getView ogni volta che viene visualizzata la visualizzazione list dell'utente.

Ovviamente l'attività onPause() viene chiamata tutte le viste sono coperte e non sono visibili all'utente. forse chiamando invalidate() sul genitore e se ondraw() non viene chiamato allora non è visibile.

+2

Ho anche avuto a che fare [questa] (http://stackoverflow.com/questions/7781892/own -defined-layout-ondraw-method-not-getting-called/7784369 # 7784369) affinché funzioni correttamente –

4

Questo è un metodo che ho usato un po 'nel mio apps e hanno avuto funzionato abbastanza bene per me:

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

Per usarlo, basta passare in qualsiasi vista o una sottoclasse di vista (IE , quasi tutto ciò che disegna sullo schermo in Android.) Restituirà true se è sullo schermo o false se non è ... piuttosto intuitivo, penso.

Se non si sta utilizzando il metodo di cui sopra come statico, allora probabilmente si può ottenere un contesto in qualche altro modo, ma al fine di ottenere il contesto di applicazione da un metodo statico, è necessario fare queste due cose:

1 - Aggiungere il seguente attributo al tag application nel vostro manifesto:

android:name="com.package.MyApplication" 

2 - Aggiungere in una classe che estende applicazione, in questo modo:

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 dovrebbe avere un contesto associato (tramite getContext()), quindi non puoi usarlo al posto del Applicazione/contesto condiviso? Fare così salverebbe molto codice. – greg7gkb

+0

Non ho ancora toccato Android dal 2012, ma, come ricordo, avevo bisogno che questo codice fosse in grado di funzionare molto presto nel processo di avvio, prima che tutti i contesti fossero stati assegnati. Non riesco a ricordare perché ne avevo bisogno. – ArtOfWarfare

+0

Penso che questo metodo controlli solo se la vista è all'interno dei limiti dello schermo. Per rilevare se la vista è effettivamente mostrata, dovrai combinarla con isShown(). –

3

Oltre a view.getVisibility() c'è view.isShown().
isShown controlla l'albero delle viste per determinare se tutti gli antenati sono visibili.

Anche se questo non gestisce le viste ostruite, solo le viste che sono nascoste o perse in se stesse o in uno dei suoi genitori.

+0

Questo mi ha davvero aiutato (view.isShown()). Grazie +1. –

6

Nel mio caso il seguente codice funziona meglio ad ascoltare se la vista è visibile o meno:

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

è possibile aggiungere al vostro constractor di CustomView un un onScrollChangedListener from ViewTreeObserver

quindi se il vostro punto di vista è Scrolled dello schermo è possibile chiamare view.getLocalVisibleRect() e determinare se il punto di vista è in parte fuori campo ...

si può dare un'occhiata al codice della mia biblioteca: PercentVisibleLayout

Spero che aiuti!

0

Nel trattare con un problema analogo, in cui avevo bisogno di sapere se la vista ha qualche altra finestra su di esso, ho usato questo nel mio costume Vista:

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

    } else { 

    } 
} 
0

nella visualizzazione personalizzata, impostare la listener:

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

Sto usando questo codice per animare una vista una volta quando è visibile all'utente.

2 casi devono essere considerati.

  1. La vista non è visualizzata sullo schermo. Ma sarà visibile se l'utente scorrere lo

    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. Il vostro punto di vista è inizialmente a schermo. (Non in qualche altra parte invisibile per l'utente in ScrollView, è in inizialmente sullo schermo e visibile all'utente)

    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

Questo può essere controllato utilizzando il metodo getGlobalVisibleRect. Se il rettangolo restituito da questo metodo ha esattamente le stesse dimensioni di View, l'attuale View è completamente visibile sullo schermo.

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

Se avete bisogno di calcolare la percentuale di visibilità è possibile farlo mediante il calcolo piazza:

float visiblePercentage = (visibleRect.height() * visibleRect.width())/(float)(view.getHeight() * view.getWidth()) 
Problemi correlati