Gelişmiş widget oluşturma

Bu sayfada, daha iyi bir kullanıcı deneyimi için daha gelişmiş bir widget oluşturmayla ilgili önerilen uygulamalar açıklanmaktadır.

Widget içeriğini güncellemek için optimizasyonlar

Widget içeriğini güncellemek işlem yükü açısından pahalı olabilir. Pil tüketiminden tasarruf etmek için güncelleme türünü, sıklığını ve zamanlamayı optimize edin.

Widget güncellemesi türleri

Widget'ları güncellemenin üç yolu vardır: tam güncelleme, kısmi güncelleme ve koleksiyon widget'ı kullanılıyorsa veri yenileme. Her birinin hesaplama maliyetleri ve sonuçları farklıdır.

Aşağıda, her güncelleme türü açıklanmakta ve her biri için kod snippet'leri sunulmaktadır.

  • Tam güncelleme: Widget'ı tam olarak güncellemek için AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) numaralı telefonu arayın. Bu işlem, daha önce sağlanan RemoteViews değerini yeni bir RemoteViews ile değiştirir. Bu, işlem açısından en pahalı güncellemedir.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout1, "Updated text1")
    setTextViewText(R.id.textview_widget_layout2, "Updated text2")
    }
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout1, "Updated text1");
    remoteViews.setTextViewText(R.id.textview_widget_layout2, "Updated text2");
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
    
  • Kısmi güncelleme: Widget'ın bazı bölümlerini güncellemek için AppWidgetManager.partiallyUpdateAppWidget numaralı telefonu arayın. Bu işlem, yeni RemoteViews öğesini daha önce sağlanan RemoteViews ile birleştirir. Bir widget updateAppWidget(int[], RemoteViews) aracılığıyla en az bir tam güncelleme almıyorsa bu yöntem yoksayılır.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout, "Updated text")
    }
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout, "Updated text");
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews);
    
  • Koleksiyon verilerini yenileme: Widget'ınızdaki bir koleksiyon görünümünün verilerini geçersiz kılmak için AppWidgetManager.notifyAppWidgetViewDataChanged çağrısı yapın. Bu, RemoteViewsFactory.onDataSetChanged öğesini tetikler. Bu sırada widget'ta eski veriler gösterilir. Bu yöntemle pahalı görevleri eş zamanlı olarak güvenli bir şekilde gerçekleştirebilirsiniz.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
    
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);
    

Uygulama, karşılık gelen AppWidgetProvider sınıfıyla aynı UID'ye sahip olduğu sürece bu yöntemleri uygulamanızın herhangi bir yerinde çağırabilirsiniz.

Bir widget'ın ne sıklıkta güncelleneceğini belirleme

Widget'lar, updatePeriodMillis özelliği için sağlanan değere bağlı olarak düzenli olarak güncellenir. Widget; kullanıcı etkileşimine, yayın güncellemelerine veya her ikisine göre güncellenebilir.

Düzenli aralıklarla güncelleme

appwidget-provider XML'de AppWidgetProviderInfo.updatePeriodMillis için bir değer belirterek periyodik güncellemenin sıklığını kontrol edebilirsiniz. Her güncelleme, AppWidgetProvider.onUpdate() yöntemini tetikler. Bu yöntemde widget'ı güncellemek için kodu yerleştirebilirsiniz. Bununla birlikte, widget'ınızın verileri eşzamansız olarak yüklemesi gerekiyorsa veya güncellenmesi 10 saniyeden uzun sürüyorsa bir sonraki bölümde açıklanan yayın alıcı güncellemeleri için alternatifleri göz önünde bulundurun. Bunun nedeni, 10 saniyeden sonra sistem BroadcastReceiver öğesinin yanıt vermediğini kabul etmesidir.

updatePeriodMillis, 30 dakikadan kısa değerleri desteklemiyor. Ancak düzenli güncellemeleri devre dışı bırakmak isterseniz 0 değerini belirtebilirsiniz.

Kullanıcıların bir yapılandırmada güncellemelerin sıklığını ayarlamasına izin verebilirsiniz. Örneğin, bir hisse senedi takip cihazının 15 dakikada bir veya günde yalnızca dört kez güncellenmesini isteyebilir. Bu durumda, updatePeriodMillis değerini 0 olarak ayarlayın ve bunun yerine WorkManager kullanın.

Kullanıcı etkileşimine yanıt olarak güncelleme

Widget'ı kullanıcı etkileşimine göre güncellemek için önerilen yöntemlerden bazıları şunlardır:

  • Uygulamanın bir etkinliğinden: Kullanıcının dokunması gibi bir kullanıcı etkileşimine yanıt olarak doğrudan AppWidgetManager.updateAppWidget numarasını arayın.

  • Bildirim veya uygulama widget'ı gibi uzaktan etkileşimlerden: Bir PendingIntent oluşturun, ardından widget'ı çağrılan Activity, Broadcast veya Service işlevlerinden güncelleyin. Kendi önceliğinizi seçebilirsiniz. Örneğin, PendingIntent için bir Broadcast seçerseniz BroadcastReceiver önceliğine vermek üzere bir ön plan yayını seçebilirsiniz.

Yayın etkinliğine yanıt olarak güncelleme

Widget'ın güncellenmesini gerektiren yayın etkinliklerine örnek olarak, kullanıcının fotoğraf çekmesi verilebilir. Bu durumda, yeni bir fotoğraf algılandığında widget'ı güncellemek istersiniz.

JobScheduler ile bir iş planlayabilir ve JobInfo.Builder.addTriggerContentUri yöntemini kullanarak tetikleyici olarak bir yayın belirtebilirsiniz.

Ayrıca yayın için bir BroadcastReceiver kaydedebilirsiniz (örneğin, ACTION_LOCALE_CHANGED dinlerken). Ancak bu işlem cihaz kaynaklarını tükettiği için bunu yaparken dikkatli olun ve yalnızca ilgili yayını dinleyin. Android 7.0 (API düzeyi 24) ve Android 8.0'a (API düzeyi 26) eklenen yayın sınırlamaları sayesinde uygulamalar, belirli istisnalar dışında örtülü yayınları manifestlerinde kaydedemez.

BroadcastRecipientr'dan widget güncellerken dikkat edilmesi gereken noktalar

Widget, AppWidgetProvider dahil olmak üzere bir BroadcastReceiver üzerinden güncellenirse widget güncellemesinin süresi ve önceliğiyle ilgili aşağıdaki noktalara dikkat edin.

Güncelleme süresi

Sistem, kural olarak genellikle uygulamanın ana iş parçacığında çalışan yayın alıcılarının yanıt vermiyor olarak kabul edilip Uygulama Yanıt Vermiyor (ANR) hatasını tetiklemeden önce 10 saniyeye kadar çalışmasına izin verir. Widget'ın güncellenmesi daha uzun sürerse aşağıdaki alternatifleri değerlendirin:

  • WorkManager kullanarak görev planlayın.

  • goAsync yöntemini kullanarak alıcıya daha fazla zaman tanıyın. Bu, alıcıların 30 saniye boyunca yürütmesini sağlar.

Daha fazla bilgi için Güvenlikle ilgili olarak göz önünde bulundurulması gereken noktalar ve en iyi uygulamalar bölümüne bakın.

Güncellemenin önceliği

Varsayılan olarak, AppWidgetProvider.onUpdate kullanılarak yapılan yayınlar da dahil olmak üzere yayınlar arka plan işlemleri olarak çalışır. Bu, aşırı yüklenen sistem kaynaklarının yayın alıcısının çağrılmasında gecikmeye neden olabileceği anlamına gelir. Yayına öncelik vermek için yayını ön plan işlemi yapın.

Örneğin, kullanıcı widget'ın belirli bir bölümüne dokunduğunda PendingIntent.getBroadcast öğesine iletilen Intent öğesine Intent.FLAG_RECEIVER_FOREGROUND işaretini ekleyin.

Dinamik öğeler içeren doğru önizlemeler oluşturun

Şekil 1: Liste öğesi göstermeyen bir widget önizlemesi.

Bu bölümde, koleksiyon görünümüne sahip bir widget'a (ListView, GridView veya StackView kullanan bir widget) ilişkin widget önizlemesinde birden fazla öğe görüntülemek için önerilen yaklaşım açıklanmaktadır.

Widget'ınız bu görünümlerden birini kullanıyorsa gerçek widget düzenini doğrudan sağlayarak ölçeklenebilir bir önizleme oluşturmak, widget önizlemesi hiçbir öğe görüntülemediğinde deneyimi olumsuz etkiler. Bu durum, koleksiyon görünümü verilerinin çalışma zamanında dinamik olarak ayarlanması ve Şekil 1'de gösterilen resme benzemesinden kaynaklanır.

Koleksiyon görünümlerine sahip widget'ların önizlemelerinin, widget seçicide düzgün bir şekilde görüntülenmesini sağlamak için yalnızca önizleme için atanmış ayrı bir düzen dosyası tutmanızı öneririz. Bu ayrı düzen dosyası, gerçek widget düzenini ve sahte öğelerin bulunduğu bir yer tutucu koleksiyon görünümünü içerir. Örneğin, çeşitli sahte liste öğeleri içeren bir LinearLayout yer tutucusu sağlayarak ListView öğesini taklit edebilirsiniz.

ListView örneğini göstermek için ayrı bir düzen dosyasıyla başlayın:

// res/layout/widget_preview.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="@drawable/widget_background"
   android:orientation="vertical">

    // Include the actual widget layout that contains ListView.
    <include
        layout="@layout/widget_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    // The number of fake items you include depends on the values you provide
    // for minHeight or targetCellHeight in the AppWidgetProviderInfo
    // definition.

    <TextView android:text="@string/fake_item1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

    <TextView android:text="@string/fake_item2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

</LinearLayout>

AppWidgetProviderInfo meta verilerinin previewLayout özelliğini sağlarken önizleme düzeni dosyasını belirtin. Yine de initialLayout özelliği için gerçek widget düzenini belirtir ve çalışma zamanında bir RemoteViews oluştururken gerçek widget düzenini kullanırsınız.

<appwidget-provider
    previewLayout="@layout/widget_previe"
    initialLayout="@layout/widget_view" />

Karmaşık liste öğeleri

Önceki bölümdeki örnekte, liste öğeleri TextView nesneleri olduğu için sahte liste öğeleri sağlanmaktadır. Öğeler karmaşık düzenlerse sahte öğelerin sağlanması daha karmaşık olabilir.

widget_list_item.xml içinde tanımlanmış ve iki TextView nesneden oluşan bir liste öğesini ele alalım:

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    <TextView android:id="@id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_title" />

    <TextView android:id="@id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_content" />
</LinearLayout>

Sahte liste öğeleri sağlamak için düzeni birden çok kez ekleyebilirsiniz, ancak bu durum her liste öğesinin aynı olmasına neden olur. Benzersiz liste öğeleri sağlamak için şu adımları izleyin:

  1. Metin değerleri için bir özellik grubu oluşturun:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. Metni ayarlamak için şu özellikleri kullanın:

    <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        <TextView android:id="@id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetTitle" />
    
        <TextView android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetContent" />
    </LinearLayout>
    
  3. Önizleme için gereken sayıda stil oluşturun. Her stildeki değerleri yeniden tanımlayın:

    <resources>
    
        <style name="Theme.Widget.ListItem">
            <item name="widgetTitle"></item>
            <item name="widgetContent"></item>
        </style>
        <style name="Theme.Widget.ListItem.Preview1">
            <item name="widgetTitle">Fake Title 1</item>
            <item name="widgetContent">Fake content 1</item>
        </style>
        <style name="Theme.Widget.ListItem.Preview2">
            <item name="widgetTitle">Fake title 2</item>
            <item name="widgetContent">Fake content 2</item>
        </style>
    
    </resources>
    
  4. Önizleme düzenindeki sahte öğelere stil uygulayın:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" ...>
    
        <include layout="@layout/widget_view" ... />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview1" />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview2" />
    
    </LinearLayout>