Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

removeView not working after LayoutInflater.inflate(resource, root, true)

I add and remove a view dynamically to a custom view (FrameLayout) doing this:

LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mMyView = inflater.inflate(resId, this, true);

Later on I try to remove the view but the view won't remove:

removeView(mMyView);

If I do this instead everything works as expected:

mMyView = inflater.inflate(resId, this, **false**);
addView(mMyView);

The only difference is that I manually add the view instead of letting the inflate call do it. Does anyone know why this would make a difference?

like image 357
Emanuel Moecklin Avatar asked Dec 06 '13 18:12

Emanuel Moecklin


4 Answers

The answer lies in this call

mMyView = inflater.inflate(resId, this, true);

The documentation states:

Returns The root View of the inflated hierarchy. If root was supplied and attachToRoot is true, this is root; otherwise it is the root of the inflated XML file.

http://developer.android.com/reference/android/view/LayoutInflater.html#inflate(int, android.view.ViewGroup, boolean)

I was assuming the inflate call always returns the root of the inflated layout (defined by resId) but if the root parameter is provided and attachToRoot is true then it's the root of the layout the inflated layout was added to (it's new parent view) and that's root (in this case root == this).

So while

mMyView = inflater.inflate(resId, this, false);

assigns the view to mMyView that we want to remove later

mMyView = inflater.inflate(resId, this, true);

assigns the parent of that view. The call removeView(mMyView) is actually identical to removeView(this) and that obviously doesn't do anything.

This behavior is IMO very counter-intuitive and prone to errors. Why should it return the root layout instead of the inflated layout when we already have that root layout with the root parameter while we don't have a reference to the inflated layout yet?

like image 169
Emanuel Moecklin Avatar answered Oct 06 '22 01:10

Emanuel Moecklin


It seems like in newer Android versions, the layout transitions may delay removing views, either delay the call to addView() or use parent.setLayoutTransition(null).

like image 27
Steelight Avatar answered Oct 06 '22 03:10

Steelight


In my experience, it depends on where do you create your reference to the parent view which you pass into inflate(...) call. I've been in your situation and (even though it shouldn't fail, it does sometimes if we don't call it from the right place) I've proven that it's much better when creating a custom view in code, to set the LayoutParams programatically as well. From the documentation you can deduce that the parent ViewGroup is mainly used to get the layout parameters. For that reason, after you inflate (with 'false'), you can build your own FrameLayout.LayoutParams, provide the inflated view with them, and then adding the view to the parent. You may wanna try also instead of removing the view directly from your custom layout, calling View.getParent(), casting it to your custom FrameLayout, and then calling removeView(...) from the casted result.

   ((MyCustomFrameLayout)mMyView.getParent()).removeView(mMyView); 

For debugging purposes (when you use 'true'), you can check if the resulting parent of the inflated view (and the child itself) are the same as the one that get passed to the original calls. Maybe they are getting changed (or the reference is lost) at some point in the code.

Hope it works for you.

like image 32
Junior Buckeridge Avatar answered Oct 06 '22 02:10

Junior Buckeridge


If you are using list view, have you tried invalidating it after removing.

ListView.invalidate()
like image 23
Piyush Avatar answered Oct 06 '22 02:10

Piyush