问题
Let's just say I need to get and set a View's height. In Android, it's known you can get a view height only after it's drawn. If you're using Java, many answers, one of the most well-known way is like this one below, taken from this answer:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
view.getHeight(); //height is ready
}
});
Thus I search C#/Xamarin version, and found this works:
int viewHeight = 0;
ViewTreeObserver vto = view.ViewTreeObserver;
vto.GlobalLayout += (sender, args) =>
{
viewHeight = view.Height;
};
Thing is, it fired again and again. In Java version, it can be removed with
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
How to do it in C#\Xamarin? Should I resort to using boolean properties to know whether it's executed or not? Is there not way to do it like the android one?
回答1:
If you are using C# Events, avoid using anonymous events if you need to unsubscribe, or you can implement the IOnGlobalLayoutListener and add/remove the listener:
C# EventHandler Style:
Create an EventHandler method for the event to invoke:
void Globallayout_handler(object sender, EventArgs e)
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
Subscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout += Globallayout_handler;
Unsubscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout -= Globallayout_handler;
Java Listener Style in C#:
Add and implement ViewTreeObserver.IOnGlobalLayoutListener:
public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer,
ViewTreeObserver.IOnGlobalLayoutListener
{
~~~~
public void OnGlobalLayout()
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
}
Now you can use the Java way to add and remove this listener:
aView.ViewTreeObserver.RemoveOnGlobalLayoutListener(this);
aView.ViewTreeObserver.AddOnGlobalLayoutListener(this);
回答2:
Even though the answer given by ShshiHangover is correct in principle, the unsubscribing didn't work for me as expected (using the regular method #1).
The reason is probably that the ViewTreeObserver in the called method can be different from the one the event handler subscribed to, so removing it may not work (i.e., the handler method is called continuously).
The correct way of doing this is to unsubscribe from the event sender object while ensuring that IsAlive yields true:
void ViewTreeObserver_GlobalLayout(object sender, EventArgs e)
{
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive) {
vto.GlobalLayout -= ViewTreeObserver_GlobalLayout;
}
}
回答3:
Neither @Daniel or @SushiHangover methods would actually unsubscribe for me (maybe an sdk bug?). My only solution was to set a bool flag on first run. It would be nice to know how to actually unsubscribe however...
Getting the ViewTreeObserver via sender never seems to be IsAlive whereas getting the tree from the View does. However either way the event doesn't get properly removed.
private void Setup()
{
cameraView = FindViewById<SurfaceView>(Resource.Id.camera_view);
//need to wait for view to inflate to get size
isSetup = false;
ViewTreeObserver vto = cameraView.ViewTreeObserver;
vto.GlobalLayout += Vto_GlobalLayout;
}
void Vto_GlobalLayout(object sender, System.EventArgs e)
{
//this didn't work either
//ViewTreeObserver vto = cameraView.ViewTreeObserver;
//vto.GlobalLayout -= Vto_GlobalLayout;
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive)
vto.GlobalLayout -= Vto_GlobalLayout; //even after removing it seems to continue to fire...
if (!isSetup)
{
isSetup = true;
DoYourCodeNow();
}
}
来源:https://stackoverflow.com/questions/42575932/how-to-remove-viewtreeobserver-in-xamarin