Question

I have a ListView that uses a custom SimpleCursorAdapter to display information. Each ListView row has Three TextView items.

Due to the nature of the application, the reader may or may not want to change the font size to ease the reading of the text displayed on the row items.

What I want to accomplish is a way to update the text size of this TextView items, without having to do it from bindView().

Here's how I currently am doing it:

Step one: Notify the adapter that the textSize should be changed.

public void setAdjustTextSize(int size) {       
    switch (size) {
    case ArticleViewFragment.FONT_SIZE_SMALL:
        mTitleTextSizeRes = R.dimen.title_size_small;
        mCategoryTextSizeRes = R.dimen.description_size_small;
        mDescripTextSizeRes = R.dimen.description_size_small;
        break;
    case ArticleViewFragment.FONT_SIZE_MEDIUM:
        mTitleTextSizeRes = R.dimen.title_size_medium;
        mCategoryTextSizeRes = R.dimen.description_size_medium;
        mDescripTextSizeRes = R.dimen.description_size_medium;
        break;
    case ArticleViewFragment.FONT_SIZE_LARGE:
        mTitleTextSizeRes = R.dimen.title_size_large;
        mCategoryTextSizeRes = R.dimen.description_size_large;
        mDescripTextSizeRes = R.dimen.description_size_large;
        break;
    case ArticleViewFragment.FONT_SIZE_EXTRA_LARGE:
        mTitleTextSizeRes = R.dimen.title_size_extra_large;
        mCategoryTextSizeRes = R.dimen.description_size_extra_large;
        mDescripTextSizeRes = R.dimen.description_size_extra_large;
        break;
    default:
        break;
    }
}

mTitleTextSizeRes, mCategoryTextSizeRes and mDescripTextSizeRes are instance variables of the Custom Adapter.

Step Two: Set the textSize during bindView().

@Override
public void bindView(View view, Context arg1, Cursor arg2) {
    ViewHolder mHolder = (ViewHolder) view.getTag();
     //Some other initialization
    mHolder.category.setTextSize(TypedValue.COMPLEX_UNIT_PX, mResources.getDimension(mCategoryTextSizeRes));
    mHolder.title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mResources.getDimension(mTitleTextSizeRes));
    mHolder.description.setTextSize(TypedValue.COMPLEX_UNIT_PX, mResources.getDimension(mDescripTextSizeRes));
}

Now, that works, yes, but there are a few point I'd like to argue about:

1- The fact that I am changing the TextSize at runtime each time the convertView is reused. It'd be best to do this via onNewView and then the convertViews will already be using the new set size. However, attempting to do so will fail, since most of the times, the adapter has already been created, and the Views are there already.

2 - The fact that some since the code is ran on bindView, existing views will not see the change right away, and at some point during the scrolling, the user will have a few views with the old text size, and some with the new text size. Example Image attached.

With that said, I was hoping I could accmoplish something of the likes of re-initializing the adapter maybe, but I'm not sure how to do that, other than maybe creating the adapter from scratch. I tried calling notifyDataSetChanged and that did nothing

Any ideas?

Was it helpful?

Solution 3

Apparently it wasn't clear that I was using my own implementation of a SimpleCursorAdapter, @MarsAtomic and @android developer both suggested I override getView(), however, when using a SimpleCursorAdapter you don't, you override both onNewView() and onBindView().

What I ended up doing, which is what I wanted to avoid was simply recreating the adapter from scratch, and setting the TextSize during onNewView(). I am pretty pleased with the results, since that minimizes the amount of calls to textview.setTextSize(size) by only setting the size on new views.

First Step, on my activity, check during onResume if font size changed, if it did, recreate the adapter from scratch:

final int oldSize = mCurFontSize; 
mCurFontSize = Integer.valueOf(mPreferences.getString(getString(R.string.pref_key_font_size), "0"));
if (oldSize != mCurFontSize) {
    //Only re-do the adapter if needed
    Constants.logMessage("re-creating adapter");
    mArticleAdapter = new CursorListAdapter(Home.this, R.layout.list_item,
    mCursor, FROM, TO, 0, mCurFontSize);
    mArticlesListView.setAdapter(mArticleAdapter);
    }
}   

Step two, on the adapter constructor, set the font size values to the instance variables.

public CursorListAdapter(Context context, int layout, Cursor c,
        String[] from, int[] to, int flags, int textSize) {
    super(context, layout, c, from, to, flags);
    mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mRootLayout = layout;
    mResources = context.getResources();
    setAdjustTextSize(textSize);
}   

Where setAdjustTextSize does this:

public void setAdjustTextSize(int size) {       
    mTextSize = size;
    switch (size) {
    case ArticleViewFragment.FONT_SIZE_SMALL:
        mTitleTextSizeRes = R.dimen.title_size_small;
        mCategoryTextSizeRes = R.dimen.description_size_small;
        mDescripTextSizeRes = R.dimen.description_size_small;
        break;
    case ArticleViewFragment.FONT_SIZE_MEDIUM:
        mTitleTextSizeRes = R.dimen.title_size_medium;
        mCategoryTextSizeRes = R.dimen.description_size_medium;
        mDescripTextSizeRes = R.dimen.description_size_medium;
        break;
    case ArticleViewFragment.FONT_SIZE_LARGE:           
        mTitleTextSizeRes = R.dimen.title_size_large;
        mCategoryTextSizeRes = R.dimen.description_size_large;
        mDescripTextSizeRes = R.dimen.description_size_large;
        break;
    case ArticleViewFragment.FONT_SIZE_EXTRA_LARGE:
        mTitleTextSizeRes = R.dimen.title_size_extra_large;
        mCategoryTextSizeRes = R.dimen.description_size_extra_large;
        mDescripTextSizeRes = R.dimen.description_size_extra_large;
        break;
    default:
        break;
    }
}   

Step Three: During onNewView() set the text size.

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
    View container = mInflater.inflate(mRootLayout, null);

    ViewHolder mHolder = new ViewHolder();

    mHolder.category = (TextView) container.findViewById(R.id.article_category);
    mHolder.title = (TextView) container.findViewById(R.id.article_title);
    mHolder.description = (TextView) container.findViewById(R.id.article_descrp);
    mHolder.image = (ImageView) container.findViewById(R.id.article_image);

    mHolder.category.setTextSize(TypedValue.COMPLEX_UNIT_PX, mResources.getDimension(mCategoryTextSizeRes));
    mHolder.title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mResources.getDimension(mTitleTextSizeRes));
    mHolder.description.setTextSize(TypedValue.COMPLEX_UNIT_PX, mResources.getDimension(mDescripTextSizeRes));

    container.setTag(mHolder);

    return container;       
}

And that is about it. It works, it doesn't call setTextSize many times during the adapter's life, I only recreate the adapter when the font size did change, and we are all happy.

OTHER TIPS

Try altering your custom adapter's getView() to something like this:

public View getView(int position, View convertView, ViewGroup parent)
{
    View view = super.getView(position, convertView, parent);
    TextView tv = (TextView)view;

    // NOTE: textSize is set in the custom adapter's constructor
    // int textSize

    tv.setTextSize(textSize);

    return view;
}

creating your own adapter is very easy, especially if all you use are just textViews for each row.

you just override the getView() method and re-use the convertView if it's not null, or inflate a new view for the row in case it is null (and create its viewholder to be put as a tag) .

then, you use the view's viewHolder to update the textViews to the new size.

when the user has changed the preference of the size of the font, just call notifyDataSetChanged.

for being more familiar with listView and adapters, watch "the world of listView" .

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top