본문 바로가기
개발/안드로이드

안드로이드 위젯 크기 결정 로직의 문제들

by 장모 2020. 2. 2.

안드로이드는 파편화가 원래 심하지만, 위젯 크기는 특히 거지같다. 

 

 

 

우선 위젯을 만들기 위해서 추가해야 하는 두가지는 appwidget-provider.xml 파일과 위젯 설정을 위한 클래스이다. 

 

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="@dimen/widget_min_width"
    android:minHeight="@dimen/widget_min_height"
    android:minResizeWidth="@dimen/widget_min_resize_width"
    android:minResizeHeight="@dimen/widget_min_resize_height"
    android:updatePeriodMillis="0"
    android:previewImage="@drawable/appwidget_preview"
    android:resizeMode="vertical|horizontal"
    android:initialLayout="@layout/widget_presets"
    android:widgetCategory="home_screen" >

</appwidget-provider>

 

xml에서는

- minWidth, minHeight는 위젯을 홈화면에 추가할 때 사용하는 값이다. 이 값으로 홈화면에서 몇개의 셀이 할당될지 결정된다. 

- minResizeWidth, minResizeHeight는 사용자가 위젯의 크기를 변경할 때 사용되는 값이다. 변경할 때 이 크기보다 작아지진 않아야 한다는 것을 런처에게 알려준다. 

 

 

위젯 설정을 위한 클래스를 추가했다면, 위젯의 크기를 변경했을 때  AppWidgetProvider의 onAppWidgetOptionsChanged가 호출된다. 

 

public class WidgetProvider extends AppWidgetProvider {

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

        int minWidth = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
        int maxWidth = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
        int minHeight = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
        int maxHeight = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
    }
}

 

onAppWidgetOptionsChanged로 전달되는 newOptions에서 변경된 위젯 크기 정보를 얻을 수 있다. 여기서부터 조금 헷갈린다.

 

min값과 max값의 의미가 뭘까? 이것은 기기의 회전과 관련이 있다. 세로모드에서 4x2였던 위젯은 가로로 돌려도 4x2의 크기가 유지된다. 특정 기기에서 값이 어떻게 되는지로 예를 들어 보면, 세로모드에선 w291xh180dp이고 가로모드에선 454x133dp를 사용한다. 이 경우 minWidth는 291, maxWidth는 454, minHeight는 133, maxHeight는 180이 된다. 이 값은 위젯이 4x2 셀을 차지할 때 사용할 수 있는 dp로 기기가 세로/가로 상태에 상관 없이 같은 값을 넘겨준다. 

 

이 런처에서 4x3의 크기는 minHeight는 208, maxHeight는 278이 된다. 만약 xml에서 minResizeHeight 가 260dp라면 세로모드에선 278보다 작으니 3칸에 들어갈 수 있지만 가로모드에선 208보다 크기때문에 세로모드에서도 3칸이 아닌 4칸이 최소 크기가 된다. 즉, 가로모드를 사용하지 않더라도 둘다 만족해야 한다.

 

여기까지가 정석적인 내용이고 이 아래는 다양한 파편과 버그이다.

- 어떤 경우엔 newOptions으로 넘어오는 min값과 max값이 같다

- 어떤 경우엔 기기가 세로일 때 넘어오는 값과 가로일 때 넘어오는 값이 다르다

- 어떤 경우엔 화면의 방향에따라 위젯이 리사이즈 될 수 있는 셀 개수가 다르다

- minWidth, maxWidth 와 minResizeWidth, minResizeHeight의 관계를 이해할 수 없다 (위에서 설명한 로직과 다르게 동작)

- 위젯 주변에 패딩이 있거나 없고 

- 위젯의 최소 크기를 결정하는 minResizeWidth/Height를 무시하기도 하고 

- 위젯의 가로 크기만 변경했는데 세로값이 바뀐다거나 (1이 달라짐)

- 심지어 위젯 크기가 변경되면 onAppWidgetOptionsChanged가 여러번 호출되는 런처도 있다

 

 

결론적으로 런처마다 너무 다양해서 크기를 정확하게 계산해서 사용하기는 어렵다. 그래서 minResize값은 그냥 0dp로 하여 사용자가 알아서 설정할 수 있게 하는 것이 좋고(이 값을 정의하지 않으면 minWidth, minHeight를 사용한다), onAppWidgetOptionsChanged에서 넘어오는 값으로는 위젯의 정확한 사이즈를 얻을 수 없으니, 위젯 크기에 따라 보여지는 내용이 달라지는 위젯은 만들지 않는게 좋다. (폴더블 폰에서는 가로/세로/접힘/펼침의 상태가 있는데, 기기 상태와 상관 없이 항상 같은 값이 넘어온다. 그러니까, 현재 위젯이 화면에서 차지하는 크기를 알 수 없다.)

 

 

 

 

댓글