Reputation: 2785
Is there a way to handle a view visibility change (say, from GONE to VISIBLE) without overriding the view?
Something like View.setOnVisibilityChangeListener();
?
Upvotes: 32
Views: 44668
Reputation: 1
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import android.widget.Toast
import java.lang.ref.WeakReference
class MyImageView : ImageView {
var mListener: WeakReference<VisibilityChangeListener>? = null
interface VisibilityChangeListener {
fun onVisibilityChanged(visibility: Int)
}
constructor(context: Context?) : super(context) {}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle
) {
}
fun setVisibilityChangeListener(listener: VisibilityChangeListener) {
mListener = WeakReference(listener)
}
override fun onVisibilityChanged(changedView: View, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
Toast.makeText(context,visibility.toString(), Toast.LENGTH_SHORT).show()
if (mListener != null && changedView === this) {
val listener = mListener!!.get()
listener?.onVisibilityChanged(visibility)
}
}
}
Upvotes: -1
Reputation: 1322
In addition to @string.Empty's solution, this is an extension implementation for Kotlin.
fun View.visibilityChanged(action: (View) -> Unit) {
this.viewTreeObserver.addOnGlobalLayoutListener {
val newVis: Int = this.visibility
if (this.tag as Int? != newVis) {
this.tag = this.visibility
// visibility has changed
action(this)
}
}
}
Implement it like this
myView.visibilityChanged { view ->
when (view.visibility) {
VISIBLE -> { /* Do something here */ }
GONE -> { /* or here */ }
}
}
Upvotes: 12
Reputation: 61019
We can match the visibility of TextView
by a Flow/LiveData. Then listen to View visibility via the Flow/LiveData
class ViewModel : ViewModel() {
private val _textNameVisibility = MutableStateFlow(View.VISIBLE)
val textNameVisibility: LiveData<Int> = _textNameVisibility.asLiveData()
fun setTextNameVisibility(visibility: Int) {
_textNameVisibility.value = visibility
}
}
On Activity/Fragment
viewModel.textNameVisibility.observe(this) {
tvName.visibility = it
Log.i("TAG", "View visibility change" + it)
}
Note that, we need to use setTextNameVisibility
to change visibility of the View.
And StateFlow
won't emit the same value (similar to distinctUntilChanged
) so don't need care about previous visibility
Upvotes: 0
Reputation: 48272
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);
Upvotes: 3
Reputation: 10552
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
}
}
});
Upvotes: 72
Reputation: 643
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.
Upvotes: 2