Introduce about Widget Android

Widget, what is it?

Widget là gì? widget là một thành phần của android, gắn liền với một application, được hiện thị ở màn hình home, có thể hiện thị dữ liệu và xử lí thao tác của người dùng, thông thường chúng ta hay gọi widget là các tiện ích bởi khả năng tương tác nhanh gọn của chúng, đây là một component cơ bản của android do vậy không có gì phức tạp trong việc tạo ra một widget cho ứng dụng của mình, tuy nhiên đây là một thành phần khá đặc thù vì ít ứng dụng kèm theo widget, chủ yếu là các ứng dụng thời tiết, chứng khoán, đồng hồ ... nên trong một dự án bình thường chúng ta chả bao giờ phải đụng tới nó, nên hy vọng bài viết này sẽ giúp ích được cho những ai chưa biết về widget.

How to create a widget?

Widget là một thành phần được hiện thị độc lập, nó không phải là một activity hay một fragment, dù cũng có file layout riêng, nên bỏ qua những mô hình kiến trúc code, mình sẽ follow theo cách dế hiểu nhất nhé

Manifests

Để khởi tạo một widget, chúng ta cần khai báo ở menifest

<receiver android:name=".provider.WidgetProvider">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_resource" />
        </receiver>

một widget cơ bản được khai báo ở manifests với tất cả những thuộc tính ở thẻ </meta-data>, bao gồm một class Provider, là nơi để xử lý logic (tương tự như thành phần ViewModel), một file resource xml (tương tự như thành phần View), bạn cũng có thể dùng một service hay một receiver như một Presenter, để xử lý dữ liệu (call api, get data từ data base ...), tuy nhiên trong ứng dụng mình demo trong bài viết này, mình không dùng đến thành phần đó.

widget resource

trong phần khai báo ở manifest, chúng ta cung cấp một file xml : widget_resource, đó là nơi chúng ta sẽ cấu hình phần layout cho widget

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="tran.gianh.widgetandroidexample.settingpage.WidgetSettingActivity"
    android:initialLayout="@layout/layout_widget"
    android:minHeight="@dimen/dp_40"
    android:minWidth="@dimen/dp_40"
    android:previewImage="@drawable/ic_widget"
    android:resizeMode="none"
    android:updatePeriodMillis="0"
    android:widgetCategory="home_screen|keyguard" />

một file resource về cơ bản sẽ có những thuộc tính trên, chức năng của một thuộc tính được thể hiện qua tên của nó hết rồi nên mình cũng không đề cập đến nữa, chỉ lưu ý một số điểm, android:configure : để khai báo một activity làm nhiệm vụ khởi tạo trước khi một widget được khởi tạo, thông thường được dùng như một màn hình setting android:updatePeriodMillis thời gian update widget, mình set bằng 0 bởi vì mình sẽ tự handle việc update widget. Phần initialLayout chỉ là một layout xml bình thường, tuy nhiên có một số lưu ý là layout của widget chỉ sử dụng được một số view component như: TextView, EditText, ImageView ... thậm chí là việc sử dụng các custom view extend từ các view trên cũng không được chấp nhận, so sad

configure activity

Như mình đã nói, đây là một activity chúng ta khai báo có nhiệm vụ khởi tạo trước khi một widget được khởi tạo, tùy mục đích sử dụng mà chúng ta có hoặc không định nghĩa nó. Ở đây chúng ta làm quen thêm một định nghĩa mới là một RemoteView, tưởng tượng chúng ta có một layout view, rồi chúng ta có thể lấy nó ra và xử lý ở bất kì đâu trong dự án của mình. Một configure activity là một activity bình thường, tuy nhiên khi khai báo trong manifests chúng ta cần thêm thuộc tính “android.appwidget.action.APPWIDGET_CONFIGURE”

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Set the result to CANCELED.  This will cause the layout_widget host to cancel
        // out of the layout_widget placement if they press the back button.
        setResult(RESULT_CANCELED);
        if (getIntent().getExtras() != null) {
            mAppWidgetId = getIntent().getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
        }

        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish();
        }
     ...
    }

chúng ta khởi tạo activity này khi mở một widget kèm theo một giá trị widget ID, một widget ID tương ứng với một widget (tất nhiên @@) , chúng ta sẽ sử dụng giá trị này để tao tác với mỗi widget, cụ thể là lấy được layout của widget này thông qua một RemoteView, update view rồi kêt thúc activity (setResult())

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
        RemoteViews remoteViews =
                new RemoteViews(getPackageName(), R.layout.layout_widget);
    ...
remoteViews.setTextColor(R.id.text_view_time, ContextCompat.getColor(this, R.color.colorAccent));
appWidgetManager.updateAppWidget(mAppWidgetId, remoteViews);
....
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();

Widget provider

Đây là thành phần quan trọng nhất của widget, nôm na như nơi chúng ta xử lý toàn bộ logic của widget, đây là một class được kế thừa từ thành phần AppWidgetProvider, gồm các method chính:

@Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        for (int widgetId : appWidgetIds) {
        ...
        }
      ...
    }

đây là phương thức quan trọng nhất, được gọi khi khởi tạo một provider hoặc khi update widget

@Override
    public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
       ...
    }

nơi nhận và xử lý các sự kiện được gửi đến, như broad cast, các sự kiện được định nghĩa:

ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETE
ACTION_APPWIDGET_ENABLE
ACTION_APPWIDGET_DISABLE
ACTION_APPWIDGET_OPTION_CHANGED

ngoài ra còn có các phương thức khác onDelete() : Gọi khi instance của AppWidgetProvider được delete onDisable() : Gọi khi instance cuối cùng của AppWidgetProvider được delete onEnable() : Gọi khi một instance của AppwidgetProvider được tạo

tips

  • Có một sự khác nhau cơ bản khi gọi updateAppWidgetpartiallyUpdateAppWidget, bạn sẽ gặp trường hợp chỉ muốn update một đối tượng, như màu của một text view nhưng tất cả những thành phần khác bị reset về mặc định, đó là lúc bạn cần dúng để partiallyUpdateAppWidget, chỉ update những phần được thay đổi.
  • Sử dụng SharedPreference để pass data.
  • Chú ý khi thiết kế layout cho widget, xem thêm tại design guide

Kết

Widget là một thành phần đặc trưng của android, hy vọng bài viết sẽ giúp ích được mọi người khi muốn tích hợp một widget vào ứng dụng của mình !

All Rights Reserved