23

Is there a way to handle a view visibility change (say, from GONE to VISIBLE) without overriding the view?

Something like View.setOnVisibilityChangeListener();?

Jean-François Corbett
  • 36,032
  • 27
  • 135
  • 183
Federico Ponzi
  • 2,534
  • 4
  • 32
  • 57
  • I don't know for sure, but I'd say there isn't such a thing, as it would put a lot of work onto the whole system to permanently track the visibility of all views all the time, in order to notify a possible listener. – Ridcully Sep 25 '15 at 08:10

4 Answers4

67

You can use a GlobalLayoutListener to determine if there are any changes in the views visibility.

myView.setTag(myView.getVisibility());
myView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int newVis = myView.getVisibility();
        if((int)myView.getTag() != newVis)
        {
            myView.setTag(myView.getVisibility());
            //visibility has changed
        }
    }
});
Nicolas Tyler
  • 9,932
  • 4
  • 40
  • 65
  • 4
    This works but it just works for layout changes respectively for changes from GONE to VISIBLE and vice versa because INVISIBLE does not trigger a layout change. Am I right? – ToBe Nov 16 '17 at 10:13
  • @ToBe in this line myView.setTag(myView.getVisibility()); you can set whatever you want to handle afterwards – Agna JirKon Rx Feb 23 '18 at 17:13
5

Instead of subclassing you can use decoration:

class WatchedView {

    static class Listener {
        void onVisibilityChanged(int visibility);
    }

    private View v;
    private Listener listener;

    WatchedView(View v) {
        this.v = v;
    }

    void setListener(Listener l) {
        this.listener = l;
    }

    public setVisibility(int visibility) {
        v.setVisibility(visibility);
        if(listener != null) {
            listener.onVisibilityChanged(visibility);
        }
    }

}

Then

 WatchedView v = new WatchedView(findViewById(R.id.myview));
 v.setListener(this);
vestlen
  • 962
  • 9
  • 17
Alexander Kulyakhtin
  • 47,110
  • 36
  • 104
  • 156
  • This is like just creating a helper class for the view. Overriding the view would be better than this in my opinion. But yes this would work, assuming that specific `setVisibility()` is being used every time – Nicolas Tyler Sep 25 '15 at 08:21
  • 3
    @Nicolas Tyler Submitter specifically said "without overriding the view" so your statement "Overriding the view would be better" is false. Besides, overriding the views will result in massive xml changes. In general your comment is devoid of meaning – Alexander Kulyakhtin Sep 25 '15 at 08:25
  • 1
    its not false, its just not what the OP asked for. The OP was looking for a listener for visibility changes. And preferably not having to override the view. – Nicolas Tyler Sep 25 '15 at 08:26
  • This solution seems to be better. It fits to all views. – no_cola Feb 19 '19 at 07:37
  • would prefer to use the first submission as this solution forces you each time you create a view you well need to subclass it. – Omar Beshary Oct 11 '21 at 22:12
2

Take a look at ViewTreeObserver.OnGlobalLayoutListener. As written in documentation, its callback method onGlobalLayout() is invoked when the global layout state or the visibility of views within the view tree changes. So you can try to use it to detect when view visibility changes.

greenfrvr
  • 633
  • 6
  • 19
2

Kotlin seems to be much easier if anyone needs that. Using a similar approach as the accepted answer will work as well (setting a tag) if you needed to tracking coming from invisible but I used this for GONE -> VISIBLE -> GONE

binding.myView.viewTreeObserver.addOnGlobalLayoutListener {
    when (binding.myView.visibility) {
        View.VISIBLE -> {
            
        }
        View.GONE -> {
            
        }
    }
}
DevinM
  • 860
  • 1
  • 7
  • 22
  • The OnGlobalLayoutListener triggers when the layout changes. So if you do not check for the change in visibility within the listener then it will trigger unexpectedly. – Nicolas Tyler Mar 29 '22 at 15:20