Esnek widget düzenleri sağlama

Yazma yöntemini deneyin
Jetpack Compose, Android için önerilen kullanıcı arayüzü araç setidir. Compose tarzı API'leri kullanarak widget oluşturmayı öğrenin.

Bu sayfada, Android 12'de (API düzeyi 31) kullanıma sunulan widget boyutlandırmasıyla ilgili iyileştirmeler ve daha fazla esneklik açıklanmaktadır. Ayrıca, widget'ınız için nasıl boyut belirleyeceğiniz de ayrıntılı olarak açıklanır.

Widget boyutları ve düzenleri için geliştirilmiş API'leri kullanma

Android 12 (API düzeyi 31) sürümünden itibaren, aşağıdaki işlemleri yaparak daha ayrıntılı boyut özellikleri ve esnek düzenler sağlayabilirsiniz. Bu işlemler, sonraki bölümlerde açıklanmıştır:

  1. Ek widget boyutlandırma kısıtlamaları belirtin.

  2. Duyarlı düzenler veya tam düzenler sağlama.

Android'in önceki sürümlerinde, OPTION_APPWIDGET_MIN_WIDTH, OPTION_APPWIDGET_MIN_HEIGHT, OPTION_APPWIDGET_MAX_WIDTH ve OPTION_APPWIDGET_MAX_HEIGHT ekstralarını kullanarak bir widget'ın boyut aralıklarını elde etmek ve ardından widget'ın boyutunu tahmin etmek mümkündü ancak bu mantık her durumda çalışmaz. Android 12 veya sonraki sürümleri hedefleyen widget'lar için duyarlı veya tam düzenler sağlamanızı öneririz.

Ek widget boyutlandırma kısıtlamaları belirtin

Android 12, farklı ekran boyutlarına sahip cihazlarda widget'ınızın daha güvenilir bir şekilde boyutlandırılmasını sağlayan API'ler ekler.

Mevcut minWidth, minHeight, minResizeWidth, ve minResizeHeight özelliklerine ek olarak aşağıdaki yeni appwidget-provider özelliklerini kullanın:

  • targetCellWidth ve targetCellHeight: widget'ın başlatıcı ızgara hücreleri açısından hedef boyutunu tanımlar. Tanımlanmışsa bu özellikler minWidth veya minHeight yerine kullanılır.

  • maxResizeWidth ve maxResizeHeight: Başlatıcının, kullanıcının widget'ı yeniden boyutlandırmasına izin verdiği maksimum boyutu tanımlar.

Aşağıdaki XML'de, boyutlandırma özelliklerinin nasıl kullanılacağı gösterilmektedir.

<appwidget-provider
  ...
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

Duyarlı düzenler sağlama

Düzenin widget'ın boyutuna göre değişmesi gerekiyorsa her biri belirli bir boyut aralığı için geçerli olan küçük bir düzen grubu oluşturmanızı öneririz. Bu mümkün değilse bu sayfada açıklandığı gibi çalışma zamanındaki tam widget boyutuna göre düzenler sağlamak da bir seçenektir.

Bu özellik, sistemin widget'ı farklı bir boyutta her gösterdiğinde uygulamayı uyandırması gerekmediğinden daha sorunsuz ölçeklendirme ve genel olarak daha iyi sistem sağlığı sağlar.

Aşağıdaki kod örneğinde, düzen listesinin nasıl sağlanacağı gösterilmektedir.

Kotlin

override fun onUpdate(...) {
    val smallView = ...
    val tallView = ...
    val wideView = ...

    val viewMapping: Map<SizeF, RemoteViews> = mapOf(
            SizeF(150f, 100f) to smallView,
            SizeF(150f, 200f) to tallView,
            SizeF(215f, 100f) to wideView
    )
    val remoteViews = RemoteViews(viewMapping)

    appWidgetManager.updateAppWidget(id, remoteViews)
}

Java

@Override
public void onUpdate(...) {
    RemoteViews smallView = ...;
    RemoteViews tallView = ...;
    RemoteViews wideView = ...;

    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    viewMapping.put(new SizeF(150f, 100f), smallView);
    viewMapping.put(new SizeF(150f, 200f), tallView);
    viewMapping.put(new SizeF(215f, 100f), wideView);
    RemoteViews remoteViews = new RemoteViews(viewMapping);

    appWidgetManager.updateAppWidget(id, remoteViews);
}

Widget'ın aşağıdaki özelliklere sahip olduğunu varsayalım:

<appwidget-provider
    android:minResizeWidth="160dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="200dp">
</appwidget-provider>

Yukarıdaki kod snippet'i şu anlama gelir:

  • smallView, 160 dp (minResizeWidth) × 110 dp (minResizeHeight) ile 160 dp × 199 dp (bir sonraki kesme noktası - 1 dp) arasındaki boyutları destekler.
  • tallView 160 dp × 200 dp ile 214 dp (bir sonraki kesme noktası - 1) × 200 dp arasındaki boyutları destekler.
  • wideView, 215 dp × 110 dp (minResizeHeight) ile 250 dp (maxResizeWidth) × 200 dp (maxResizeHeight) arasındaki boyutları destekler.

Widget'ınız minResizeWidth × minResizeHeight ile maxResizeWidth × maxResizeHeight arasındaki boyut aralığını desteklemelidir. Bu aralıkta, düzenleri değiştirmek için kesme noktasını belirleyebilirsiniz.

Duyarlı düzen örneği
1. şekil Duyarlı düzen örneği.

Tam düzenler sağlama

Küçük bir duyarlı düzen grubu oluşturmak mümkün değilse bunun yerine widget'ın gösterildiği boyutlara göre uyarlanmış farklı düzenler sağlayabilirsiniz. Bu genellikle telefonlar için iki boyut (dikey ve yatay mod), katlanabilir cihazlar için ise dört boyuttur.

Bu çözümü uygulamak için uygulamanızın aşağıdaki adımları gerçekleştirmesi gerekir:

  1. Boyut kümesi değiştiğinde çağrılan AppWidgetProvider.onAppWidgetOptionsChanged() aşırı yüklemesi.

  2. Boyutları içeren bir Bundle döndüren AppWidgetManager.getAppWidgetOptions() işlevini çağırın.

  3. Bundle bölümünden AppWidgetManager.OPTION_APPWIDGET_SIZES anahtarına erişin.

Aşağıdaki kod örneğinde, tam düzenlerin nasıl sağlanacağı gösterilmektedir.

Kotlin

override fun onAppWidgetOptionsChanged(
        context: Context,
        appWidgetManager: AppWidgetManager,
        id: Int,
        newOptions: Bundle?
) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, id, newOptions)
    // Get the new sizes.
    val sizes = newOptions?.getParcelableArrayList<SizeF>(
            AppWidgetManager.OPTION_APPWIDGET_SIZES
    )
    // Check that the list of sizes is provided by the launcher.
    if (sizes.isNullOrEmpty()) {
        return
    }
    // Map the sizes to the RemoteViews that you want.
    val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews))
    appWidgetManager.updateAppWidget(id, remoteViews)
}

// Create the RemoteViews for the given size.
private fun createRemoteViews(size: SizeF): RemoteViews { }

Java

@Override
public void onAppWidgetOptionsChanged(
    Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    // Get the new sizes.
    ArrayList<SizeF> sizes =
        newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES);
    // Check that the list of sizes is provided by the launcher.
    if (sizes == null || sizes.isEmpty()) {
      return;
    }
    // Map the sizes to the RemoteViews that you want.
    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    for (SizeF size : sizes) {
        viewMapping.put(size, createRemoteViews(size));
    }
    RemoteViews remoteViews = new RemoteViews(viewMapping);
    appWidgetManager.updateAppWidget(id, remoteViews);
}

// Create the RemoteViews for the given size.
private RemoteViews createRemoteViews(SizeF size) { }

Widget'ınızın boyutunu belirleme

Her widget, Android 12 veya sonraki sürümleri çalıştıran cihazlar için targetCellWidth ve targetCellHeight, Android'in tüm sürümleri için ise minWidth ve minHeight tanımlamalıdır. Bu tanımlamalar, widget'ın varsayılan olarak kullandığı minimum alanı gösterir. Ancak kullanıcılar ana ekranlarına widget eklediğinde bu widget'lar genellikle belirttiğiniz minimum genişlik ve yükseklikten daha fazla alan kaplar.

Android ana ekranları, kullanıcılara widget ve simge yerleştirebilecekleri bir ızgara sunar. Bu ızgara cihaza göre değişebilir. Örneğin, birçok mobil telefonda 5x4 ızgara sunulurken tabletlerde daha büyük bir ızgara sunulabilir. Widget'ınız eklendiğinde, Android 12 veya sonraki sürümleri çalıştıran cihazlarda targetCellWidth ve targetCellHeight kısıtlamalarını, Android 11 (API düzeyi 30) veya önceki sürümleri çalıştıran cihazlarda ise minWidth ve minHeight kısıtlamalarını karşılamak için gereken minimum hücre sayısını yatay ve dikey olarak kaplayacak şekilde genişletilir.

Hem hücrenin genişliği ve yüksekliği hem de widget'lara uygulanan otomatik kenar boşluklarının boyutu cihazlar arasında değişiklik gösterebilir. İstediğiniz dolu ızgara hücrelerinin sayısını göz önünde bulundurarak, tipik bir 5x4 ızgara düzenine sahip telefonda widget'ınızın minimum boyutlarını kabaca tahmin etmek için aşağıdaki tabloyu kullanın:

Hücre sayısı (genişlik x yükseklik) Dikey modda kullanılabilir boyut (dp) Yatay modda kullanılabilir boyut (dp)
1x1 57x102dp 127x51dp
2x1 130x102dp 269x51dp
3x1 203x102dp 412x51dp
4x1 276x102dp 554x51dp
5x1 349x102dp 697x51dp
5x2 349x220dp 697x117dp
5x3 349x337dp 697x184dp
5x4 349x455dp 697x250dp
... ... ...
n x m (73n - 16) x (118m - 16) (142n - 15) x (66m - 15)

minWidth, minResizeWidth ve maxResizeWidth özellikleri için sağladığınız değerleri belirlemek üzere dikey moddaki hücre boyutlarını kullanın. Benzer şekilde, minHeight, minResizeHeight ve maxResizeHeight özellikleri için sağladığınız değerleri belirlemek üzere yatay moddaki hücre boyutlarını kullanın.

Bunun nedeni, hücre genişliğinin genellikle dikey modda yatay moda göre daha küçük olmasıdır. Benzer şekilde, hücre yüksekliği de genellikle yatay modda dikey moda göre daha küçüktür.

Örneğin, Google Pixel 4'te widget genişliğinizin bir hücreye kadar yeniden boyutlandırılmasını istiyorsanız minResizeWidth özelliğinin değerinin 57 dp'den küçük olduğundan emin olmak için minResizeWidth özelliğini en fazla 56 dp olarak ayarlamanız gerekir. Bunun nedeni, bir hücrenin dikey modda en az 57 dp genişliğinde olmasıdır. Benzer şekilde, widget'ınızın yüksekliğinin aynı cihazda tek bir hücrede yeniden boyutlandırılabilmesini istiyorsanız minResizeHeight özelliğinin değerinin 51 dp'den küçük olduğundan emin olmak için minResizeHeight değerini en fazla 50 dp olarak ayarlamanız gerekir. Bunun nedeni, bir hücrenin yatay modda en az 51 dp yüksekliğinde olmasıdır.

Her widget, minResizeWidth/minResizeHeight ve maxResizeWidth/maxResizeHeight özellikleri arasındaki boyut aralıklarında yeniden boyutlandırılabilir. Bu nedenle, bu aralıklar arasındaki tüm boyut aralıklarına uyum sağlaması gerekir.

Örneğin, yerleşimdeki widget'ın varsayılan boyutunu ayarlamak için aşağıdaki özellikleri belirleyebilirsiniz:

<appwidget-provider
    android:targetCellWidth="3"
    android:targetCellHeight="2"
    android:minWidth="180dp"
    android:minHeight="110dp">
</appwidget-provider>

Bu, targetCellWidth ve targetCellHeight özellikleri tarafından belirtildiği gibi widget'ın varsayılan boyutunun 3x2 hücre olduğu anlamına gelir. Android 11 veya daha eski sürümleri çalıştıran cihazlarda ise minWidth ve minHeight özellikleri tarafından belirtildiği gibi 180×110 dp'dir. İkinci durumda, hücrelerdeki boyut cihaza bağlı olarak değişebilir.

Ayrıca, widget'ınızın desteklenen boyut aralıklarını ayarlamak için aşağıdaki özellikleri belirleyebilirsiniz:

<appwidget-provider
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>

Önceki özelliklerde belirtildiği gibi, widget'ın genişliği 180 dp ile 530 dp arasında, yüksekliği ise 110 dp ile 450 dp arasında yeniden boyutlandırılabilir. Widget, aşağıdaki koşullar karşılandığı sürece 3x2 hücreden 5x2 hücreye kadar yeniden boyutlandırılabilir:

  • Cihazda 5x4 ızgara olmalıdır.
  • Hücre sayısı ile kullanılabilir boyut arasındaki eşleme, bu sayfadaki minimum boyut tahminini gösteren tabloya göre yapılır.
  • Widget, bu boyut aralığına uyum sağlar.

Kotlin

val smallView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_small)
val mediumView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_medium)
val largeView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_large)

val viewMapping: Map<SizeF, RemoteViews> = mapOf(
        SizeF(180f, 110f) to smallView,
        SizeF(270f, 110f) to mediumView,
        SizeF(270f, 280f) to largeView
)

appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))

Java

RemoteViews smallView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_small);
RemoteViews mediumView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_medium);
RemoteViews largeView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_large);

Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
viewMapping.put(new SizeF(180f, 110f), smallView);
viewMapping.put(new SizeF(270f, 110f), mediumView);
viewMapping.put(new SizeF(270f, 280f), largeView);
RemoteViews remoteViews = new RemoteViews(viewMapping);

appWidgetManager.updateAppWidget(id, remoteViews);

Widget'ın, önceki kod snippet'lerinde tanımlanan duyarlı düzenleri kullandığını varsayın. Bu, 180 dp (minResizeWidth) x 110 dp (minResizeHeight) ile 269 x 279 dp (bir sonraki kesme noktaları - 1) arasında R.layout.widget_weather_forecast_small olarak belirtilen düzenin kullanıldığı anlamına gelir. Benzer şekilde, 270x110 dp ile 270x279 dp arasında R.layout.widget_weather_forecast_medium, 270x280 dp ile 530 dp (maxResizeWidth) x 450 dp (maxResizeHeight) arasında ise R.layout.widget_weather_forecast_large kullanılır.

Kullanıcı widget'ı yeniden boyutlandırdıkça görünümü, aşağıdaki örneklerde gösterildiği gibi hücrelerdeki her boyuta uyacak şekilde değişir.

En küçük 3x2 ızgara boyutundaki hava durumu widget&#39;ı örneği. Kullanıcı arayüzünde konum adı (Tokyo), sıcaklık (14°) ve parçalı bulutlu havayı gösteren simge yer alıyor.
Şekil 2. 3x2 R.layout.widget_weather_forecast_small.

4x2 &quot;orta&quot; boyutunda örnek hava durumu widget&#39;ı. Bu şekilde widget&#39;ı yeniden boyutlandırmak, önceki widget boyutundaki tüm kullanıcı arayüzünü temel alır ve &quot;Çoğunlukla bulutlu&quot; etiketini ve 16:00-19:00 arasındaki sıcaklık tahminini ekler.
Şekil 3. 4x2 R.layout.widget_weather_forecast_medium.

5x2 &quot;orta&quot; boyutunda örnek hava durumu widget&#39;ı. Widget&#39;ı bu şekilde yeniden boyutlandırmak, önceki boyuttaki kullanıcı arayüzüyle aynı sonucu verir. Ancak daha fazla yatay alan kaplamak için bir hücre uzunluğu kadar uzatılır.
4. şekil. 5x2 R.layout.widget_weather_forecast_medium.

5x3 &quot;büyük&quot; boyutunda örnek hava durumu widget&#39;ı. Widget&#39;ı bu şekilde yeniden boyutlandırmak, önceki widget boyutlarındaki tüm kullanıcı arayüzünü temel alır ve widget&#39;ın içine, Salı ve Çarşamba günleri için hava durumu tahminini içeren bir görünüm ekler. Güneşli veya yağmurlu havayı gösteren simgeler
            ve her gün için en yüksek ve en düşük sıcaklıklar.
5. şekil. 5x3 R.layout.widget_weather_forecast_large.

5x4 &quot;büyük&quot; boyutunda hava durumu widget&#39;ı örneği. Widget&#39;ı bu şekilde yeniden boyutlandırmak, önceki widget boyutlarındaki tüm kullanıcı arayüzünü temel alır ve Perşembe ile Cuma günlerini (ve her gün için hava türünü, yüksek ve düşük sıcaklığı gösteren ilgili sembolleri) ekler.
Şekil 6. 5x4 R.layout.widget_weather_forecast_large.