Get the size of my homescreen widget

前端 未结 6 2029
没有蜡笔的小新
没有蜡笔的小新 2021-02-02 15:46

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

相关标签:
6条回答
  • 2021-02-02 15:59

    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

    0 讨论(0)
  • 2021-02-02 16:00

    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
        )
    }
    
    0 讨论(0)
  • 2021-02-02 16:05

    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()
    
    }
    
    0 讨论(0)
  • 2021-02-02 16:09

    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!

    0 讨论(0)
  • 2021-02-02 16:15

    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.

    0 讨论(0)
  • 2021-02-02 16:20

    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);
    }
    
    0 讨论(0)
提交回复
热议问题