Reputation: 9242
Originally I got this error:
The specified child already has a parent. You must call removeView() on the child's parent first
at
customSection.addView(customLayout);
So I added
((LinearLayout)customLayout.getParent()).removeView(customLayout);
and now get
java.lang.NullPointerException
So if the child has a parent, and I must first remove the child from the parent, why does getParent() return null?
I have an abstract fragment that allows derived classes to supply a custom layout for the list adapter. Relevant code:
Binding:
public void bind(DataObject row) {
View customLayout = getChildItemView(row);
if (customLayout != null) {
((LinearLayout) customLayout.getParent()).removeView(customLayout);
customSection.removeAllViews();
customSection.addView(customLayout);
customSection.setVisibility(View.VISIBLE);
} else {
customLayout.setVisibility(View.INVISIBLE);
}
}
protected View getChildItemView(CommonRow row) {
if (parentView == null) {
parentView = (LinearLayout) LayoutInflater.from(getActivity())
.inflate(R.layout.list_item_custom_section,
new LinearLayout(getActivity()), true);
label = (TextView) parentView.findViewById(R.id.txtData1Label);
value = (TextView) parentView.findViewById(R.id.txtData1Value);
}
label.setText("Minimum");
value.setText(manager.formatMoney(((SpecificDataRow) row).minimum));
return parentView;
}
I've also tried inflater.inflate(R.layout.list_item_custom_section, null)
... false, null / false, what gives?
EDIT:
@allprog, I knew some cleanup was needed. I wrote this at the end of the day somewhat in a hurry. I have since cleaned up the code, and separated out the binding and inflating of the view. Cleaned up code:
private class ViewHolder {
....
public ViewHolder(View v) {
Butterknife.inject(this, v);
View custom = createCustomView(customSection);
if (custom != null) {
customSection.setVisibility(View.VISIBLE);
customSection.addView(custom);
}
}
public void bind(CommonRow row) {
......
bindCustomView(row, customSection);
}
}
Child class:
@Override
protected View createCustomView(ViewGroup parent) {
return LayoutInflater.from(getActivity()).inflate(R.layout.list_item_custom_section, parent, false);
}
@Override
protected void bindCustomView(CommonRow row, ViewGroup section) {
TextView label = Views.findById(section, R.id.txtData1Label);
TextView value = Views.findById(section, R.id.txtData1Value);
label.setText("Minimum");
value.setText(manager.formatMoney(((SpecificRow) row).minimum));
}
suitianshi got it first, with my original [unkempt] code that was the solution.
Upvotes: 6
Views: 11180
Reputation: 340
First of all LayoutInflater inflate method always returns view without parent.
if attachToRoot == true
the parentView
will be that new LinearLayout(getActivity())
if attachToRoot == false
the parentView
will be inflated R.layout.list_item_custom_section
whatever it is.
in both cases the ((LinearLayout) customLayout.getParent())
will be null. Thats why you are getting NullPointerException
. You can see it in return statement in LayoutInflater documentation.
As is written above declaring parentView
as field is bad aproach, it should be method parameter that you will inflate if == null (approach from AdapterView).
BTW: line 9 in your code if would be called it would throw NullPointerException
because it is called only in case that customLayout == null
!!!
Upvotes: 0
Reputation: 6462
As I can see your parentView
is a field variable, this is the key. So what I suspect is really going on:
First call of bind()
: you creating parentView
and it is not have a parent yet, customSection.addView(customLayout);
works fine, but after you added a check for parent it fails here.
Second call of bind()
: parentView
is now have a parent and your added check should work now, but you failed at the previous step. Without a check you are failing here with exception in title.
Solution: check for the presence of parent and remove it only if necessery.
Upvotes: 2
Reputation: 3340
try this:
public void bind(DataObject row) {
View customLayout = getChildItemView(row);
if (customLayout != null) {
if(customLayout.getParent() != null) {
((LinearLayout)customLayout.getParent()).removeView(customLayout);
}
customSection.removeAllViews();
customSection.addView(customLayout);
customSection.setVisibility(View.VISIBLE);
} else {
customLayout.setVisibility(View.INVISIBLE);
}
}
I have read related source code, getParent
should return non-null value when view
has a parent. You should make sure it actually has a parent before casting and calling removeView
Wish this helps.
source code :
in View
:
public final ViewParent getParent() {
return mParent;
}
in ViewGroup.addViewInner
if (child.getParent() != null) {
throw new IllegalStateException("The specified child already has a parent. " +
"You must call removeView() on the child's parent first.");
}
Upvotes: 12