问题
I have a ListView with an ArrayAdapter which uses an XML layout, similar to simple_list_item_1, to inflate its content. I have two options in the app menu to increase/decrease the text size. I managed to modify the text size of the TextViews inside the ListView by overriding the getView function of the ArrayAdapter. Something like this:
adapter = new ArrayAdapter<Line>(MyActivity.this, R.layout.line_list, lines){
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, fontSizeDp + zoom);
return textView;
}
};
listView.setAdapter(adapter);
The menu options change the zoom value and call adapter.notifyDataSetChanged(); to make the adapter change the text size.
Until that point everything works.
The problem is that when I decrease the text size too much, the height of each TextView is not wrapping its content. That is, the ListView items are too high. When I increase the text size I don't have problems with the items height.
If I call super.getView(position, convertView, parent); with convertView being null, I make the adapter to allways inflate. In that case, decreasing the text size works fine since the height is wrapped. However, I'm causing an overload of work and I don't want that.
I tried invalidating or forcing layout to the listview and/or the textviews but it didn't work.
Any help is appreciated
EDIT: This is line_list layout:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/lineTextSize"
android:gravity="center"
android:paddingLeft="6dip"
android:paddingRight="6dip" />
lineTextSize is 22dp for default and 40dp for large
EDIT 2: Look like this
instead of this screen:
回答1:
I found here that this is a known bug for Android 3.1+
The solution is to add a string like this
String ZERO_WIDTH_SPACE = "\u200b";
to the text of the TextViews. So, the correct getView function of the adapter is:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, fontSizeDp + zoom);
textView.setText(textView.getText() + ZERO_WIDTH_SPACE);
return textView;
}
回答2:
I managed to test the code and it seems the ListView doesn't want to update the row's height(when decreasing font size) in Honeycomb and above versions. A simple solution to your problem would be to implement the getView method of your adapter in such way that it will recreate the row view if you have a decrease in font and/or the recycled view isn't appropriate for the current font size. This is based on your observation, that forcing the super.getView() method with convertView set as null works because the ListView will always construct the new row from the ground up. My code is doing something similar only with much better performance:
public class CustomAdapter extends ArrayAdapter<String> {
private LayoutInflater mInflater;
private int mResource;
public CustomAdapter(Context context, int resource, String[] objects) {
super(context, resource, objects);
mInflater = LayoutInflater.from(context);
mResource = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(mResource, parent, false);
} else {
if (mZoom - mOldZoom < 0) {
Integer cacheZoom = (Integer) convertView.getTag();
if (cacheZoom == null || cacheZoom.intValue() != mZoom) {
convertView = mInflater.inflate(mResource, parent, false);
convertView.setTag(mZoom);
}
}
}
((TextView) convertView).setTextSize(
TypedValue.COMPLEX_UNIT_DIP, 40 + mZoom);
((TextView) convertView).setText(getItem(position));
return convertView;
}
}
mZoom, mOldZoom are variables with the new and the old zoom value used to see in which direction the font is heading:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item1:
mOldZoom = mZoom;
mZoom += 2;
mAdapter.notifyDataSetChanged();
break;
case R.id.item2:
mOldZoom = mZoom;
mZoom -= 2;
mAdapter.notifyDataSetChanged();
break;
}
return super.onOptionsItemSelected(item);
}
来源:https://stackoverflow.com/questions/12718831/update-item-height-when-modifying-text-size-in-listview