Reputation: 3457
First off, I've already figured out how to solve this bug, but at this point I'm just wondering why my fix works. The situation I was running into was that I was getting a ClassCastException, cannot convert LinearLayout$LayoutParams to AbsListView$LayoutParams
when I tried to do the following (simplified for these purposes):
mLayout = (LinearLayout) getLayoutInflater()
.inflate(R.layout.my_header_layout,
getListView(), false);
mRootView.addView(mLayout);
getListView().addHeaderView(mLayout);
getListView().setAdapter(mAdapter);
I eventually broke it down and discovered that the issue was fixed when I removed the call to mRootView.addView(mLayout)
My two-fold question is, why is this happening? First, on the conceptual side, why is it that when a header view is already in the layout the whole thing just dies? Is it because that view is effectively trying to be laid out twice: once for the header and once for the actual layout?
Secondly, why this exception? It seems awfully non-descriptive and it doesn't look like it captures the actual problem at all? Is this a case of a high level issue not being handled because a lower level function will just fail on the results anyway?
Upvotes: 4
Views: 1392
Reputation: 22342
My question is, why is this happening? First, on the conceptual side, why is it that when a header view is already in the layout the whole thing just dies? Is it because that view is effectively trying to be laid out twice: once for the header and once for the actual layout?
Your layout params are going crazy here, that's the problem.
inflate()
is called, assigning ListView.LayoutParams
to mLayout
, since you pass a ListView as the root. From the docs:root: Optional view to be the parent of the generated hierarchy (if attachToRoot is true), or else simply an object that provides a set of LayoutParams values for root of the returned hierarchy (if attachToRoot is false.)
When you use addView()
, it checks the type of the child's params. If it doesn't match, it converts it as best it can. It does this by copying the values over in a new constructor. So, your mLayout
now has LinearLayout.LayoutParams
attached.
Once you call addHeaderView()
, mLayout
is now a child of the listview. One of the things done during ListView#setupChild()
is this:
AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
This is almost definitely where you're getting the ClassCastException. You just can't do a straight cast like that.
Secondly, why this exception? It seems awfully non-descriptive and it doesn't look like it captures the actual problem at all?
It's not that bad, but you have to understand how the layout system works. Could they do a simple check, and just throw an error or log message if it's not right? Sure. Could ListView
try to gracefully handle the issue the way a LinearLayout
does? Sure. But they don't.
It might be worth posting on the Android Developer's forum/group/tracker if you think it could be handled better.
Upvotes: 4
Reputation: 6085
It all lies in the statement:
cannot convert LinearLayout$LayoutParams to AbsListView$LayoutParams
LinearLayout
and AbsoluteListView
have 2 different types of layouts, so you would need to setup the LayoutParams
by using the following:
mLayout.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
because now you are using AbsListView
-specific LayoutParams
.
Upvotes: 0