Gelişmiş widget oluşturma

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, 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üncelleme optimizasyonları

Widget içeriğini güncellemek bilgi işlem açısından maliyetli 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ı güncellemenin üç yolu vardır: tam güncelleme, kısmi güncelleme ve koleksiyon widget'ı söz konusu olduğunda 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 verilmektedir.

  • Tam güncelleme: Widget'ı tamamen güncellemek için AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) numaralı telefonu arayın. Bu işlem, daha önce sağlanan RemoteViews yerine yeni bir RemoteViews yerleştirir. Bu, hesaplama açısından en maliyetli 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 belirli 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'yı birleştirir. Bir widget, updateAppWidget(int[], RemoteViews) aracılığıyla 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: AppWidgetManager.notifyAppWidgetViewDataChanged widget'ınızdaki bir koleksiyon görünümünün verilerini geçersiz kılmak için çağrı yapın. Bu işlem, RemoteViewsFactory.onDataSetChanged tetikleyicisini çalıştırır. Bu süre zarfında eski veriler widget'ta gösterilir. Bu yöntemle maliyetli görevleri güvenli bir şekilde senkron 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);

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üncellemeleri yayınlayarak veya her ikisini de yaparak güncellenebilir.

Düzenli olarak güncelleme

appwidget-provider XML'sinde 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üncelleme 10 saniyeden uzun sürüyorsa yayın alıcı güncellemeleriyle ilgili alternatifleri göz önünde bulundurun. Çünkü 10 saniye sonra sistem, BroadcastReceiver öğesini yanıt vermiyor olarak kabul eder.

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

Kullanıcıların bir yapılandırmadaki güncellemelerin sıklığını ayarlamasına izin verebilirsiniz. Örneğin, 15 dakikada bir veya günde yalnızca dört kez güncellenen bir borsa sembolü 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 göre widget'ı güncellemek için önerilen yöntemlerden bazıları şunlardır:

  • Uygulama etkinliğinden: Kullanıcı etkileşimine (ör. kullanıcının dokunması) yanıt olarak doğrudan AppWidgetManager.updateAppWidget işlevini çağırın.

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

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

Güncellenmesi için widget gerektiren yayınlanan etkinliklere ö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.

Yayın için bir BroadcastReceiver de kaydedebilirsiniz. Örneğin, ACTION_LOCALE_CHANGED için dinleme. Ancak bu işlem cihaz kaynaklarını tükettiği için dikkatli bir şekilde kullanın ve yalnızca belirli 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ının kullanıma sunulmasıyla birlikte, uygulamalar belirli istisnalar dışında manifestlerinde örtülü yayınları kaydedemez.

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

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

Güncellemenin süresi

Genel olarak sistem, genellikle uygulamanın ana iş parçacığında çalışan yayın alıcıların yanıt vermediğini düşünmeden ve Uygulama Yanıt Vermiyor (ANR) hatasını tetiklemeden önce 10 saniyeye kadar çalışmasına izin verir. Yayın işlenirken ana iş parçacığının engellenmesini önlemek için goAsync yöntemini kullanın. Widget'ın güncellenmesi daha uzun sürüyorsa WorkManager kullanarak bir görev planlayabilirsiniz.

Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.

Daha fazla bilgi için Güvenlikle ilgili hususlar 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ının çağrılmasında gecikmeye neden olabileceği anlamına gelir. Yayın önceliği 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'e iletilen Intent öğesine Intent.FLAG_RECEIVER_FOREGROUND işaretini ekleyin.

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

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

Bu bölümde, koleksiyon görünümü olan bir widget'ın widget önizlemesinde birden fazla öğeyi görüntülemek için önerilen yaklaşım açıklanmaktadır. Koleksiyon görünümü, ListView, GridView veya StackView kullanan bir widget'tı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 öğe gösterilmediğinde deneyimi olumsuz etkiler. Bunun nedeni, koleksiyon görünümü verilerinin çalışma zamanında dinamik olarak ayarlanması ve Şekil 1'de gösterilen resme benzemesidir.

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 ListView öğesini taklit edebilirsiniz.

ListView için bir örnek göstermek üzere 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 devam edersiniz ve çalışma zamanında 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ğundan sahte liste öğeleri sağlanmaktadır. Öğeler karmaşık düzenlere sahipse 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 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 durumda her liste öğesi aynı 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>