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üncellemeyle ilgili optimizasyonlar

Widget içeriğini güncellemek bilgi işlem açısından pahalı olabilir. Pil tüketimini azaltmak için güncelleme türünü, sıklığını ve zamanlamasını optimize edin.

Widget güncelleme türleri

Widget'ları üç şekilde güncelleyebilirsiniz: tam güncelleme, kısmi güncelleme ve koleksiyon widget'ı durumunda veri yenileme. Her birinin farklı hesaplama maliyetleri ve sonuçları vardır.

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

  • Tam güncelleme: Widget'ı tamamen güncellemek için AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) işlevini çağırın. Bu işlem, daha önce sağlanan RemoteViews değerini yeni bir RemoteViews değeriyle değiştirir. Bu, en pahalı hesaplama işlemi gerektiren 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 bölümlerini güncellemek için AppWidgetManager.partiallyUpdateAppWidget işlevini çağırın. Bu işlem, yeni RemoteViews ile daha önce sağlanan RemoteViews'ü birleştirir. Bir widget updateAppWidget(int[], RemoteViews) üzerinden en az bir tam güncelleme almazsa bu yöntem yok sayı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 işlevini çağırın. Bu işlem, RemoteViewsFactory.onDataSetChanged tetikleyicisini etkinleştirir. Bu süre zarfında widget'ta eski veriler gösterilir. Bu yöntemle pahalı görevleri senkronize bir şekilde 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, ilgili 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 yanıt olarak güncellenebilir, güncelleme yayınlayabilir veya her ikisini de yapabilir.

Düzenli olarak güncelleme

appwidget-provider XML dosyasında AppWidgetProviderInfo.updatePeriodMillis için bir değer belirterek düzenli 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 gerekiyorsa veya güncellemesinin 10 saniyeden uzun sürmesi gerekiyorsa aşağıdaki bölümde açıklanan yayın alıcı güncellemeleri için alternatifleri göz önünde bulundurun. Çünkü sistem, 10 saniye sonra BroadcastReceiver'yi yanıt vermiyor olarak kabul eder.

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

Kullanıcıların bir yapılandırmadaki güncelleme sıklığını ayarlamalarına izin verebilirsiniz. Örneğin, bir hisse senedi akışının 15 dakikada bir veya yalnızca günde 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

Widget'ı kullanıcı etkileşimine göre güncellemenin önerilen bazı yolları ş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 çağrılır.

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

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

Widget'ın güncellenmesini gerektiren bir yayın etkinliği örneği, kullanıcının fotoğraf çekmesidir. Bu durumda, yeni bir fotoğraf algılandığında widget'ı güncellemek istiyorsunuz.

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

Yayın için bir BroadcastReceiver de kaydedebilirsiniz. Örneğin, ACTION_LOCALE_CHANGED için dinleme. Ancak bu işlem cihaz kaynaklarını kullandığından bu özelliği dikkatli bir şekilde kullanın ve yalnızca belirli bir yayını dinleyin. Android 7.0 (API düzeyi 24) ve Android 8.0 (API düzeyi 26) sürümlerinde yayın sınırlamaları kullanıma sunulduğundan, uygulamalar belirli istisnalar dışında, manifest dosyalarında dolaylı yayın kaydedemez.

Bir widget'ı BroadcastReceiver'dan güncellerken dikkat edilmesi gerekenler

Widget, AppWidgetProvider dahil olmak üzere bir BroadcastReceiver'ten güncelleniyorsa widget güncellemesinin süresi ve önceliğiyle ilgili aşağıdaki hususları göz önünde bulundurun.

Güncellemenin süresi

Sistem, kural olarak genellikle uygulamanın ana iş akışında çalışan yayın alıcılarının yanıt vermediğini düşünmeden ve Uygulama Yanıt Vermiyor (ANR) hatası tetiklemeden önce 10 saniye boyunca çalışmasını sağlar. Widget'ın güncellenmesi daha uzun sürerse aşağıdaki alternatifleri değerlendirin:

  • WorkManager simgesini kullanarak bir görev planlayın.

  • goAsync yöntemiyle alıcıya daha fazla zaman verin. Bu, alıcılara 30 saniye boyunca yürütme olanağı tanır.

Daha fazla bilgi için Güvenlikle ilgili dikkat edilmesi gereken noktalar ve en iyi uygulamalar başlıklı makaleyi 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üklenmiş 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 Intent'e iletilen PendingIntent.getBroadcast'e Intent.FLAG_RECEIVER_FOREGROUND işaretini ekleyin.

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

Şekil 1: Liste öğesi içermeyen bir widget önizlemesi.

Bu bölümde, koleksiyon görünümü olan bir widget'ın (yani ListView, GridView veya StackView kullanan bir widget'ın) widget önizlemesinde birden fazla öğe göstermek 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 önizlemesinde hiçbir öğe gösterilmediğinde deneyimi olumsuz etkiler. Bunun nedeni, koleksiyon görünümü verilerinin çalışma zamanında dinamik olarak ayarlanmasıdır ve Şekil 1'de gösterilen resme benzer.

Koleksiyon görünümleri içeren widget'ların önizlemelerinin widget seçicide düzgün şekilde gösterilmesi için yalnızca önizleme için ayrılmış ayrı bir düzen dosyası kullanmanı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ünü içerir. Örneğin, birkaç sahte liste öğesi içeren bir yer tutucu LinearLayout sağlayarak bir ListView'ü taklit edebilirsiniz.

ListView örneğini açıklamak 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 belirtmeye ve çalışma zamanında RemoteViews oluştururken gerçek widget düzenini kullanmaya devam edersiniz.

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

Karmaşık liste öğeleri

Liste öğeleri TextView nesneleri olduğundan önceki bölümdeki örnekte sahte liste öğeleri sağlanmaktadır. Öğeler karmaşık düzenlerse sahte öğeler sağlamak daha karmaşık olabilir.

widget_list_item.xml içinde tanımlanan ve iki TextView nesnesinden oluşan bir liste öğesini 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 çok kez ekleyebilirsiniz ancak bu, her liste öğesinin aynı olmasına neden olur. Benzersiz liste öğeleri sağlamak için aşağıdaki 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 aşağıdaki ö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 stilleri 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>