Gelişmiş widget oluşturma

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

Widget içeriğini güncelleme optimizasyonları

Widget içeriğini güncellemek hesaplama 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

Bir widget'ı üç şekilde güncelleyebilirsiniz: Tam güncelleme, kısmi güncelleme ve koleksiyon widget'ı kullanıyorsanız verileri yenileme. Her birinin farklı hesaplama maliyetleri ve sonuçları vardır.

Aşağıda her güncelleme türü açıklanmış ve her biri için kod snippet'leri sağlanmıştır.

  • Tam güncelleme: Widget'ı tamamen güncellemek için AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) numaralı telefonu arayın. Bu işlem, önceden sağlanan RemoteViews özelliğini yeni bir RemoteViews ile değiştirir. Bu, bilgi 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 çağrısını çağırın. Bu işlem, yeni RemoteViews öğesini önceden sağlanan RemoteViews ile birleştirir. Bir widget, updateAppWidget(int[], RemoteViews) üzerinden en az bir tam güncelleme almazsa 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ündeki verileri geçersiz kılmak için AppWidgetManager.notifyAppWidgetViewDataChanged çağrısı. Bu, RemoteViewsFactory.onDataSetChanged etiketini tetikler. Bu arada, eski veriler widget'ta görüntülenir. Pahalı görevleri bu yöntemle güvenli bir şekilde eşzamanlı olarak 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);
    

Uygulamanın 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 yerinden çağırabilirsiniz.

Widget'ları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 aralıklarla güncellenir. Widget, kullanıcı etkileşimine, yayın güncellemelerine veya her ikisine göre güncellenebilir.

Düzenli olarak 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, widget'ı güncellemek için kodu yerleştirebileceğiniz AppWidgetProvider.onUpdate() yöntemini tetikler. Ancak, widget'ınızın verileri eşzamansız olarak yüklemesi veya güncellenmesi 10 saniyeden uzun sürmesi gerekiyorsa veya sistem, 10 saniyeden sonra BroadcastReceiver değerini yanıt vermiyor olarak kabul ettiğinden, aşağıdaki bölümde açıklanan yayın alıcı güncellemeleri için alternatifleri göz önünde bulundurun.

updatePeriodMillis, 30 dakikadan kısa değerleri desteklemez. Ancak, periyodik güncellemeleri devre dışı bırakmak istiyorsanız 0 değerini belirtebilirsiniz.

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

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

Kullanıcı etkileşimine dayalı olarak widget'ı güncellemek için önerilen yöntemlerden bazıları aşağıda belirtilmiştir:

  • Uygulama etkinliğinden: Kullanıcının dokunması gibi bir kullanıcı etkileşimine yanıt olarak doğrudan AppWidgetManager.updateAppWidget çağrısı yapın.

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

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

Güncelleme için widget 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 için dinliyor olabilirsiniz). Ancak bu işlem cihaz kaynaklarını tükettiği için dikkatli kullanın ve yalnızca ilgili yayını dinleyin. Android 7.0 (API düzeyi 24) ve Android 8.0'da (API düzeyi 26) yayın sınırlamalarının kullanıma sunulmasıyla birlikte, uygulamalar belirli istisnalar dışında manifest dosyalarına örtülü yayınları kaydedemez.

BroadcastRecipientr'dan widget güncellerken dikkat edilmesi gereken noktalar

Widget, AppWidgetProvider dahil olmak üzere bir BroadcastReceiver sürümünden güncellendiyse widget güncellemesinin süresi ve önceliğiyle ilgili aşağıdaki noktaları göz önünde bulundurun.

Güncelleme süresi

Kural olarak sistem, genellikle uygulamanın ana iş parçacığında çalışan yayın alıcılarının yanıt vermediğini algılamadan ve Uygulama Yanıt Vermiyor (ANR) hatası 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 uygulamasını kullanarak görev planlayın.

  • goAsync yöntemiyle alıcıya daha fazla zaman tanıyın. Bu, alıcıların 30 saniye boyunca yürütülmesine izin verir.

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

Güncellemenin önceliği

Varsayılan olarak, AppWidgetProvider.onUpdate kullanılarak yapılanlar 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 haline getirin.

Ö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şturma

Şekil 1: Hiçbir liste öğesi göstermeyen widget önizlemesi.

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

Widget'ınız bu görünümlerden birini kullanıyorsa doğrudan gerçek widget düzenini sağlayarak ölçeklenebilir bir önizleme oluşturmak, widget önizlemesinde hiçbir öğe görüntülenmediğinde deneyimin bozulmasına neden olur. Bunun nedeni, koleksiyon görünümü verilerinin çalışma zamanında dinamik olarak ayarlanması ve Şekil 1'de gösterilene benzemesidir.

Koleksiyon görünümlerine sahip widget'ların önizlemelerinin widget seçicide düzgün bir şekilde görüntülenmesi için yalnızca önizleme için atanmış ayrı bir düzen dosyası bulundurmanızı öneririz. Bu ayrı düzen dosyası, gerçek widget düzenini ve sahte öğeler içeren bir yer tutucu koleksiyon görünümü içerir. Örneğin, birkaç sahte liste öğesi 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. initialLayout özelliği için gerçek widget düzenini belirlemeye ve çalışma zamanında bir RemoteViews oluştururken gerçek widget düzenini kullanmaya devam edebilirsiniz.

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

Karmaşık liste öğeleri

Önceki bölümde verilen örnek, liste öğeleri TextView nesneleri olduğu için sahte liste öğeleri sağlar. Öğeler karmaşık düzenlerden oluşursa sahte öğe sağlamak daha karmaşık olabilir.

widget_list_item.xml içinde tanımlanmış ve iki TextView nesneden oluşan bir liste öğesi düşünü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="@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 fazla kez ekleyebilirsiniz, ancak bu durum her liste öğesinin aynı olmasına neden olur. Benzersiz liste öğeleri sağlamak için şu adımları uygulayın:

  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 stilde 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. Stilleri, önizleme düzenindeki sahte öğelere 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>