问题
I just want to know how big my current widget is. I found tons of questions to set the minimum size, but I don't want to set it. Instead I want to show that informations which simply fit on the widget.
If the widget is too small I need to hide some things, but I need to know the size for that. I read the source code of some classes like the AppWidgetProvider
, even the documentation. There are allways just references about the minimum or maximum size, but never the current size.
Please point me to the right resource.
回答1:
Unfortunately there still is no api to acquire this info from The AppWidget-Host.
You only get a current size info on resize events (Android 4.1+) and only from launchers which support this (Sony XPeria Launcher for example does not, as well as some third party launchers).
I followed this stackoverflow thread and the Android Developer Documentation:
Determine the homescreen's appwidgets space grid size
https://developer.android.com/reference/android/appwidget/AppWidgetProvider.html#onAppWidgetOptionsChanged%28android.content.Context,%20android.appwidget.AppWidgetManager,%20int,%20android.os.Bundle%29
Here is my code to determine the widget size, taken from Picture Calendar 2014 (Play Store):
/* Get Device and Widget orientation.
This is done by adding a boolean value to
a port resource directory like values-port/bools.xml */
mIsPortraitOrientation = getResources().getBoolean(R.bool.isPort);
// Get min dimensions from provider info
AppWidgetProviderInfo providerInfo = AppWidgetManager.getInstance(
getApplicationContext()).getAppWidgetInfo(appWidgetId);
// Since min and max is usually the same, just take min
mWidgetLandWidth = providerInfo.minWidth;
mWidgetPortHeight = providerInfo.minHeight;
mWidgetPortWidth = providerInfo.minWidth;
mWidgetLandHeight = providerInfo.minHeight;
// Get current dimensions (in DIP, scaled by DisplayMetrics) of this
// Widget, if API Level allows to
mAppWidgetOptions = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
mAppWidgetOptions = getAppWidgetoptions(mAppWidgetManager,
appWidgetId);
if (mAppWidgetOptions != null
&& mAppWidgetOptions
.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) > 0) {
if (D.DEBUG_SERVICE)
Log.d(TAG,
"appWidgetOptions not null, getting widget sizes...");
// Reduce width by a margin of 8dp (automatically added by
// Android, can vary with third party launchers)
/* Actually Min and Max is a bit irritating,
because it depends on the homescreen orientation
whether Min or Max should be used: */
mWidgetPortWidth = mAppWidgetOptions
.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
mWidgetLandWidth = mAppWidgetOptions
.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
mWidgetLandHeight = mAppWidgetOptions
.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
mWidgetPortHeight = mAppWidgetOptions
.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
// Get the value of OPTION_APPWIDGET_HOST_CATEGORY
int category = mAppWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
// If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen
// widget (dumped with Android-L preview :-( ).
mIsKeyguard = category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;
} else {
if (D.DEBUG_SERVICE)
Log.d(TAG,
"No AppWidgetOptions for this widget, using minimal dimensions from provider info!");
// For some reason I had to set this again here, may be obsolete
mWidgetLandWidth = providerInfo.minWidth;
mWidgetPortHeight = providerInfo.minHeight;
mWidgetPortWidth = providerInfo.minWidth;
mWidgetLandHeight = providerInfo.minHeight;
}
if (D.DEBUG_SERVICE)
Log.d(TAG, "Dimensions of the Widget in DIP: portWidth = "
+ mWidgetPortWidth + ", landWidth = " + mWidgetLandWidth
+ "; landHeight = " + mWidgetLandHeight
+ ", portHeight = " + mWidgetPortHeight);
// If device is in port oriantation, use port sizes
mWidgetWidthPerOrientation = mWidgetPortWidth;
mWidgetHeightPerOrientation = mWidgetPortHeight;
if (!mIsPortraitOrientation)
{
// Not Portrait, so use landscape sizes
mWidgetWidthPerOrientation = mWidgetLandWidth;
mWidgetHeightPerOrientation = mWidgetLandHeight;
}
On Android 4.1+ you now have the current size of the Widget in DIP for the current device orientation. To know how many homescreen grid cells your widget is wide and high, you have to divide this by cell size. Default is 74dip, but this may vary by using different devices or launchers. Custom Launchers which allow to change the grid are a pain in the ass for Widget developers.
On Android < 4.1 there is no way to get the current widget size. So better set your API level to JellyBean to avoid trouble. Should be ok now... two years ago when I started with my Widget App that was not an option.
Getting notified on orientation changes to redraw the widget(s) is another topic.
Important: If you allow the user to have multiple Widgets of yours, you have to do this calculation for everyone of them, based on their Id!
回答2:
You can add an method to your widget provider and get your widget sizes when you resize it for all versions of Android started from API 16:
@Override
public void onAppWidgetOptionsChanged (Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle widgetInfo) {
int width = widgetInfo.getInt (AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int height = widgetInfo.getInt (AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
}
And as for new added widget you can get its wizes by getting its sizes from its layout and multiply it to its cols num:
AppWidgetProviderInfo widgetInfo = AppWidgetManager.getInstance (context).getAppWidgetInfo (widgetId);
private int getColsNum (int size) {
return (int) Math.floor ((size - 30) / 70);
}
public int getWidthColsNum () {
return getColsNum (widgetInfo.minWidth);
}
public int getHeightColsNum () {
return getColsNum (widgetInfo.minHeight);
}
int width = widgetInfo.minWidth * getWidthColsNum ();
int height = widgetInfo.minHeight * getHeightColsNum ();
Getting widget cols num was taken from official Android guidelines.
回答3:
Get cols/rows count (Kotlin):
val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
val minHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
val maxHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
val oneCellHeight = ViewExtensions.convertDpToPx(40f * 1, context).toInt() - (30 * 1)
val twoCellHeight = ViewExtensions.convertDpToPx(40f * 2, context).toInt() - (30 * 2)
val threeCellHeight = ViewExtensions.convertDpToPx(40f * 3, context).toInt() - (30 * 3)
val fourCellHeight = ViewExtensions.convertDpToPx(40f * 4, context).toInt() - (30 * 4)
when {
oneCellHeight in (minHeight..maxHeight) -> {
// 1 cell
}
twoCellHeight in (minHeight..maxHeight) -> {
// 2 cells
}
threeCellHeight in (minHeight..maxHeight) -> {
// 3 cells
}
fourCellHeight in (minHeight..maxHeight) -> {
// 4 cells
}
else -> {
// More
}
}
class ViewExtensions {
companion object {
fun convertDpToPx(sp: Float, context: Context): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, sp, context.resources.displayMetrics)
}
}
}
Based on, that 1 cell = 40dp
https://developer.android.com/guide/practices/ui_guidelines/widget_design.html#anatomy_determining_size
回答4:
Here is class helper to get widget size in pixels
Note that you should use Application context, otherwise orientation will be incorrect on widget rotation
class WidgetSizeProvider(
private val context: Context // Do not pass Application context
) {
private val appWidgetManager = AppWidgetManager.getInstance(context)
fun getWidgetsSize(widgetId: Int): Pair<Int, Int> {
val isPortrait = context.resources.configuration.orientation == ORIENTATION_PORTRAIT
val width = getWidgetWidth(isPortrait, widgetId)
val height = getWidgetHeight(isPortrait, widgetId)
val widthInPx = context.dip(width)
val heightInPx = context.dip(height)
return widthInPx to heightInPx
}
private fun getWidgetWidth(isPortrait: Boolean, widgetId: Int): Int =
if (isPortrait) {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
} else {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
}
private fun getWidgetHeight(isPortrait: Boolean, widgetId: Int): Int =
if (isPortrait) {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
} else {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
}
private fun getWidgetSizeInDp(widgetId: Int, key: String): Int =
appWidgetManager.getAppWidgetOptions(widgetId).getInt(key, 0)
private fun Context.dip(value: Int): Int = (value * resources.displayMetrics.density).toInt()
}
回答5:
For Android 4.1 (API 16) or high:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance( context );
int ids[] = appWidgetManager.getAppWidgetIds( componentName );
for( int id : ids ){
Bundle options = appWidgetManager.getAppWidgetOptions( id );
int width = options.getInt (AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int height = options.getInt (AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
}
回答6:
We can't get widget size from OPTION_APPWIDGET_MAX_WIDTH and OPTION_APPWIDGET_MAX_HEIGHT it will return wrong values.
This solution based on @Zanael answer. And I add delta value because on some device cellSize can be not in minSize and maxSize range.
private fun getWidgetSize(minSize: Int, maxSize: Int, context: Context): Int {
var delta = Int.MAX_VALUE
var lastDeltaRecord = -1
for (i in 1..30) {
val cellSize = convertDpToPx(40f * i, context).toInt() - (30 * i)
if (cellSize in minSize..maxSize) {
return i
}
val deltaMin = abs(minSize - cellSize)
val deltaMax = abs(cellSize - maxSize)
if (delta > deltaMin) {
delta = deltaMin
lastDeltaRecord = i
}
if (delta > deltaMax) {
delta = deltaMax
lastDeltaRecord = i
}
}
return lastDeltaRecord
}
private fun convertDpToPx(sp: Float, context: Context): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
sp,
context.resources.displayMetrics
)
}
来源:https://stackoverflow.com/questions/25153604/get-the-size-of-my-homescreen-widget