6

I've programmed Android for a while, but this really puzzles me.

So I have a simple appwidget with a textview. I simply want the text font size inside that textview to scale up/down if the user resizes the widget so as to make the text larger if the app is resized. I have tried several solutions but everything fails:

  1. Simply using the "sp" (scaled pixels) unit for the font size - this does not scale the text when widget size changes (I did not expect it too as well!).

  2. Extending TextView with my own and the overriding onSizeChanged method. But you cannot have descendents of basic UI classes in a widget (documentation also states this).

Since there are no methods to even get the current appWidget size I simply am stuck here.

Yes, I know one solution is to create different versions of the widget like 1x4 and 2x4 etc, but surely there is another way?

Any ideas are welcome?

zaifrun
  • 931
  • 9
  • 21
  • As a side note to later readers, I can say that it is possible to do from API 16+. There is a callback method you can override in you widget: onAppWidgetOptionsChanged and from this method you can get the max width and height (as some point out below). But below API 16 it is not really easy to do the resizing. – zaifrun Mar 01 '17 at 16:38

4 Answers4

4

It's not possible to automatically scale the text of a TextView based on parent size. A workaround would be to override the onDraw method of the TextView and implement the scaling manually.

As for the appwidgets: You can't retrieve a resizable appwidget's current size because they are intended for use with ListView and GridView containers. These containers will manage the scaling and spacing of content automatically and will make themselves scrollable if the content can't fit in the appwidget's current size.

If you're not using a ListView or a GridView it seems to me the best way to "scale" the components of a appwidget is the method you already mentioned: Use different non-resizeable appwidgets.

Leon Lucardie
  • 9,541
  • 4
  • 50
  • 70
  • Like I said, the workaround with extending TextView and overriding onDraw does not work for appwidgets as the documentation also states. Maybe I could put the TextView inside a GridView and it would work? – zaifrun Sep 02 '12 at 14:49
  • A 'GridView' with one row and one column with the TextView set to fill_parent could work. If it doesn't you should probably resort to different sized non-resizeable appwidgets. – Leon Lucardie Sep 02 '12 at 15:06
  • I could not get the approach with gridview to work as well. I guess the only way is to have several appwidgets with different non-resizeable sizes then (this seems to be approach others do as well). Well, hope at least others can benefit from my question and save time! – zaifrun Sep 02 '12 at 16:01
3

You may call AppWidgetManager.getAppWidgetOptions(OPTION_APPWIDGET_MAX_WIDTH) to get the max width (same way to widget's height) and adjust text size accordingly. Limitation: this method is available from API Level 16

Bao Le
  • 16,643
  • 9
  • 65
  • 68
  • 1
    This works alright, although different launchers will pass slightly different values (dp vs. px), etc. so getting it right is somewhat messy. – Nikolay Elenkov May 20 '13 at 08:58
3

Combining the answers above and a lovely code snip-it from https://stackoverflow.com/a/7875656/6364860, I got it working like this:

*Note I had a circular button with text, so I only needed the smallest dimension.

@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions){

    super.onAppWidgetOptionsChanged(context,appWidgetManager,appWidgetId,newOptions);

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        int maxWidth = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
        int maxHeight = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);

        int size = Math.min(maxHeight,maxWidth);
        int pxSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,size,context.getResources().getDisplayMetrics());

        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_button);
        float newSize = refitText(context.getString(R.string.button_text),pxSize);

        remoteViews.setTextViewTextSize(R.id.widget_text, TypedValue.COMPLEX_UNIT_PX,newSize);

        appWidgetManager.updateAppWidget(appWidgetId,remoteViews);
    }

}

private float refitText(String text, int textWidth)
{
    if (textWidth <= 0)
        return 0;

    float hi = 100;
    float lo = 2;
    final float threshold = 0.5f; // How close we have to be

    Paint testPaint = new Paint();

    while((hi - lo) > threshold) {
        float size = (hi+lo)/2;
        testPaint.setTextSize(size);
        if(testPaint.measureText(text) >= textWidth)
            hi = size; // too big
        else
            lo = size; // too small
    }
    // Use lo so that we undershoot rather than overshoot
    return lo;
}
Community
  • 1
  • 1
donny.rewq
  • 621
  • 1
  • 7
  • 21
1

This is primitive way but it works:

AppWidgetManager manager = AppWidgetManager.getInstance(this);
ComponentName thisWidget = new ComponentName(this, AppWidget.class);
int[] widgetId = manager.getAppWidgetIds(thisWidget);
Bundle options;
options = manager.getAppWidgetOptions(widgetIds[1]);
int minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
int maxWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int minHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
int maxHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
int maxtextLen = (maxWidth / currentTextSize) * (maxHeight / currentTextSize);

Boolean isFit = maxtextLen > textInWidgetTextView.length();
Debuger
  • 23
  • 4