可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm looking into writing a custom adapter to populate a listview with 3 textviews per line. I've found quite a bit of example code to do this, but the one that seemed the best was at: http://www.anddev.org/custom_widget_adapters-t1796.html
After a few minor tweaks to fix some compiler issues with the latest Android SDK, I got it running, only to get the exception:
ERROR/AndroidRuntime(281): java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView
So I did a lot of research and found lots of possible reasons and fixes for this. None of which changed a thing. My adapter code is currently:
public class WeatherAdapter extends BaseAdapter {
private Context context;
private List<Weather> weatherList;
public WeatherAdapter(Context context, int rowResID,
List<Weather> weatherList ) {
this.context = context;
this.weatherList = weatherList;
}
public int getCount() {
return weatherList.size();
}
public Object getItem(int position) {
return weatherList.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
Weather weather = weatherList.get(position);
//LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.weather_row, null, true);
TextView cityControl = (TextView)v.findViewById( R.id.city );
TextView temperatureControl = (TextView)v.findViewById( R.id.temperature );
ImageView skyControl = (ImageView)v.findViewById( R.id.sky );
return v;
}
}
So I have tried the commented out way of getting the inflater, and the currently uncommented out. I have tried passing "parent" to inflate as well as null, and passing "true", "false" and omitting completely the last parameter. None of them have worked, and all examples I've found so far have been from 2008 which I get the feeling are a bit outdated.
If anyone could help with this then I would love to resolve the issue.
回答1:
I'm a beginner also so take this answer with a pinch of salt - if it doesn't work, move on and Google some more. Not familiar with AdapterView
since I traditionally have a ListView
or GridView
and a custom Adapter extended off a BaseAdapter
and then listView.setAdapter(myCustomAdapter)
.
You could try making something like this inside the WeatherAdapter
class:
public void addToList(Weather mWeather) {
weatherList.add(mWeather);
}
Then in the class that calls WeatherAdapter
:
weatherAdapter.addToList(weatherToAdd);
weatherAdapter.notifyDataSetChanged();
Also you need to optimize it more in the getView method:
http://www.youtube.com/watch?v=wDBM6wVEO70
回答2:
I believe this line is at fault:
View v = inflater.inflate(R.layout.weather_row, null, true);
You need instead:
View v = inflater.inflate(R.layout.weather_row, parent, false);
The false makes the inflated view independent of the parent, not attached to it, which very oddly seems to be the accepted design for custom views within AdapterView
s. Why this is so, I find utterly baffling, but the pattern above worked for me.
回答3:
For the AdaptertView addView
method:
void addView(View child)
This method is not supported and
throws an
UnsupportedOperationException when
called." (From Android documentation)
Probably the inflating procedure calls the addView
method and this is not possible from an AdapterView
, or its AdapterView
.
From the documentation:
"An Adapter object acts as a bridge
between an AdapterView and the
underlying data for that view. The
Adapter provides access to the data
items. The Adapter is also responsible
for making a View for each item in the
data set".
I think that the inflating operation could be done from a simple Activity that models your view and all other operations, for example, retrieving data and showing data in other classes.
Hope it will be helpful!
回答4:
@Axel22's answer is key, but there are a few other things missing from your code. First, you should be extending either BaseAdapter or ArrayAdapter, depending on your preference. Second, you want to get in the practice of using a ViewHolder to avoid making excessive calls to findViewById, and (most importantly) recycling your View.
private Class ViewHolder {
public TextView cityControl;
public TextView temperatureControl;
public ImageView skyControl;
public ViewHolder(TextView cityControl, TextView temperatureControl, ImageView skyControl) {
this.cityControl = cityControl;
this.temperatureControl = temperatureControl;
this.skyControl = skyControl;
}
Your getView function can recycle views and utilize the ViewHolder
class as follows:
public View getView(int position, View convertView, ViewGroup parent) {
Weather weather = weatherList.get(position);
// This is how you attempt to recycle the convertView, so you aren't
// needlessly inflating layouts.
View v = convertView;
ViewHolder holder;
if (null == v) {
v = LayoutInflater.from(getContext()).inflate(R.layout.weather_row, parent, false);
TextView cityControl = (TextView)v.findViewById( R.id.city );
TextView temperatureControl = (TextView)v.findViewById( R.id.temperature );
ImageView skyControl = (ImageView)v.findViewById( R.id.sky );
holder = new ViewHolder(cityControl, temperatureControl, skyControl);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
holder.cityControl.setText("Metropolis");
holder.temperatureControl.setText("78");
holder.skyControl.setImageResource(R.drawable.daily_planet);
return v;
}
For tons more examples (and other optimization tips), see this blog post (or just google for ViewHolder).