ویجت های اندروید

سه شنبه ۱۰ مهر ۹۷ توسط یوسف رضا مختاری

ما در  این مقاله یک آموزش کامل در مورد چگونگی اضافه کردن ویجت های اندروید به اپلیکشین تان  را برای شما آماده کرده ایم.

بعد از این خواندن این مقاله و با انجام چهار پروژه نمونه , شما یک درک منطقی و عمیق نسبت به ویجت های اندروید پیدا می کنید .

شما کد زدن ویجت از ابتدایی تا پیشرفته را خواهید آموخت .

 

قبل از اینکه شروع کنیم , بیایید ببینم ویجت اندروید چیست؟

ویجت ها یک جنبه اساسی از سفارشی سازی صفحه اصلی (home screen ) هستند .

شما می توانید آنها را به عنوان یک نگاه " مختصر و اجمالی"  از مهم ترین اطلاعات و قابلیت های برنامه که از صفحه اصلی کاربر قابل دسترسی می باشد فرض کنید .

شما همچنین می توانید این مقاله را به زبان چینی (中文) بخوانید.

 

پس ما می خواهیم به چه چیز دست پیدا کنیم؟

  • مراحل ایجاد ویجت با یک مثال ساده (ویجت ساده - باز کردن یک وبسایت با کلیک کردن روی ویجت )
  • مثال ویجت Broadcast – ( ویجت Broadcast- شمارش کلیک بر روی ویجت )
  • مثال ویجت قابل تنظیم – ( "ویجت قابل تنظیم " - دریافت اطلاعات قبل از ایجاد و استفاده از داده ها هنگام کلیک )
  •  به روز رسانی ویجت توسط سرویس ("بروز رسانی ویجت" - بروز رسانی هر دقیقه ارقام تصادفی در ویجت )

 

مراحل ایجاد یک ویجت : 

  1. ایجاد layout برای ویجت
  2. ایجاد XML برای تعریف ویژیگی های ویجت
  3. ایجاد کلاس برای رفتار ویجت
  4. همه اینها را به xml اضافه میکنیم

 با آگاهی از این موارد , مثال اول ما یک ویجت است که با کلیک روی آن وبسایت مورد نظر ما با خواهد شد.

 

مرحله 1 : یک یک layout خیلی ساده برای ویجت

simple_app_widget.xml 

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

    <TextView
        android:id="@+id/tvWidget"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="@dimen/widget_margin"
        android:background="#ff6200"
        android:contentDescription="@string/appwidget_text"
        android:gravity="center"
        android:text="@string/appwidget_text"
        android:textColor="#ffffff"
        android:textSize="24sp"
        android:textStyle="bold|italic"/>

</LinearLayout>

این layout به عنوان یک ویجت در صفحه اصلی (home-screen) کاربر نمایش داده می شود .

 

مرحله 2 : ایجاد XML که ویژیگی های (properties) ویجت را تعریف میکند .

widget properties

در پوشه res یک پوشه XML ایجاد کنید .

simple_app_widget_info.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialKeyguardLayout="@layout/simple_app_widget"
    android:initialLayout="@layout/simple_app_widget"
    android:minHeight="60dp"
    android:minWidth="60dp"
    android:previewImage="@android:drawable/ic_menu_add"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="0"
    android:widgetCategory="home_screen">
</appwidget-provider>

نگاهی سریع به این ویژیگی ها :

  • initialLayout : مرجع برای layout ویجت (که ما در حال حاضر ایجاد کرده ایم)
  • minHeight and minWidth : هر 60dp به معنی یک سلول در صفحه اصلی (home-screen) اندروید می باشد . برای این مثال ویجت در حالت min  سلول 1*1 میگیرد
  • previewImage : تصویری که بر روی صفحه انتخاب ویجت اندروید نشان داده خواهد شد . ما نمیتوانیم layout پیش نمایش را طراحی کنیم.  ما باید یک تصویر را تنظیم کنیم.
  • resizeMode : پیکربندی (configuration) برای تغییر اندازه ویجت
  • updatePeriodMillis : متد آپدیت ویجت بر اساس زمانی که ما بر حسب میلی ثانیه مشخص میکنیم فرخوانی می شود .
  • widgetCategory : صفحه اصلی یا صفحه قفل

 

 Android Studio دارای یک رابط شگفت انگیز برای ایجاد ویجت می باشد .

اندروید استودیو - ایجاد یک ویجت

مرحله 3 : ایجاد کلاس برای lifecycle ویجت

 

AppWidgetProvider از BroadcastReceiver ارث بری میکند . SimpleAppWidget به طور غیر مستقیم فرزند BroadcastReceiver می باشد .

بنابراین کلاس ویجت ما یک کلاس receiver می باشد .

مرحله 4  : اضافه کردن به عنوان یک receiver به AndroidManifest 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.erenutku.simplewidgetexample"
          xmlns:android="http://schemas.android.com/apk/res/android">

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

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

</manifest>

بله ! شما یک ویجت را برای اپلیکیشن تان پیاده سازی کردید .

بیایید به سطح بالاتری بریم !

 

RemoteView

با  یادگیری RemoteView  ادامه میدهیم .

یک کلاس که یک view سلسه مراتبی را توصیف میکند که میتواند در فرآیند دیگری نمایش داده شود .

سلسه مراتب از یک فایل منبع layout پر (inflate) می شود و این کلاس برخی از عملیات اساسی را برای اصلاح محتوای سلسه مراتب فراهم میکند.

 

RemoteView  فقط از layout های زیر پشتیبانی می کند .

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • GridLayout

RemoteView  فقط از view های زیر پشتیبانی می کند .

  • AnalogClock
  • Button
  • Chronometer
  • ImageButton
  • ImageView
  • ProgressBar
  • TextView
  • ViewFlipper
  • ListView
  • GridView
  • StackView
  • AdapterViewFlipper

 

اگر از view دیگری استفاده کنید RemoteView هیچ عملی برای نمایش ندارد .

بیایید برخی از کد ها را به کلاس ویجت اضافه کنیم .

public class SimpleAppWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    private void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                 int appWidgetId) {

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.simple_app_widget);
        // Construct an Intent object includes web adresss.
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://erenutku.com"));
        // In widget we are not allowing to use intents as usually. We have to use PendingIntent instead of 'startActivity'
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
        // Here the basic operations the remote view can do.
        views.setOnClickPendingIntent(R.id.tvWidget, pendingIntent);
        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

}

متد onUpdate همان طور که میبینم override شده که در هر  updatePeriodMillis فراخوانی می شود.

نتیجه را ببینید :

 

این یک مثال ابتدایی و سریع برای ویجت بود . برای مشاهده کامل کد ها میتوانید از این لینک استفاده کنید .

 

درک متد های Override شده

قبل از رفتن به مثال بعدی ما نیاز به درک کلاس های ویجت که متد های زیر را override میکند داریم .

 

  • ()onUpdate : این متد زمانی فرخوانی میشود برای بروزرسانی ویجت برنامه در فواصل تعریف شده توسط ویژیگی updatePeriodMillis .
  • () onAppWidgetOptionsChanged : این متد زمانی فراخوانی می شود که ویجت برای اولین بار قرار داده می شود یا هر زمانی که سایز ویجت تغییر می کند .
  • ( [] onDeleted(Context , int : این متد زمانی فراخوانی می شود که ویجت برنامه از میزکار حذف می شود .
  • (onEnabled(Context : این متد زمانی فراخوانی می شود که یک نمونه از ویجت برنامه برای اولین بار ایجاد شده است .
  • (onDisabled(Context : این متد زمانی فراخوانی می شود که آخرین نمونه از ویجت برنامه شما از میزکار حدف شود .
  • (onReceive(Context, Intent : این متد برای هر broadcast و قبل از فراخوانی هر کدام از متد های بالا فراخوانی می شود .

کمی سردرگم شدید؟مشکلی نیست .

در ویدیو زیر به شما نشان داده می شود که این متد ها چه زمانی فراخوانی می شوند .

 

این دانش به شما امکان کنترل کامل بر ویجت شما می دهد .

 

ویجت  Broadcast

ما تمام مراحل ایجاد ویجت را آموخته ایم و این مراحل را تکرار نخواهیم کرد .

هر کلیک روی ویجت یک broadcast ارسال می کند و متد onReceive شمارنده را افزایش می دهد .

در مثال قبلی ما از ()getActivity. استفاده کردیم , این بار میخواهیم از ()getBroadcast. استفاده کنیم .

BroadcastWidget کلاس ویجت ما می باشد , بیایید ببینیم داخلش چیست ؟

BroadcastWidget.java

public class BroadcastWidget extends AppWidgetProvider {
    private static final String ACTION_SIMPLEAPPWIDGET = "ACTION_BROADCASTWIDGETSAMPLE";
    private static int mCounter = 0;

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId) {

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.broadcast_widget);
        // Construct an Intent which is pointing this class.
        Intent intent = new Intent(context, BroadcastWidget.class);
        intent.setAction(ACTION_SIMPLEAPPWIDGET);
        // And this time we are sending a broadcast with getBroadcast
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        views.setOnClickPendingIntent(R.id.tvWidget, pendingIntent);
        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        if (ACTION_SIMPLEAPPWIDGET.equals(intent.getAction())) {
            mCounter++;
            // Construct the RemoteViews object
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.broadcast_widget);
            views.setTextViewText(R.id.tvWidget, Integer.toString(mCounter));
            // This time we dont have widgetId. Reaching our widget with that way.
            ComponentName appWidget = new ComponentName(context, BroadcastWidget.class);
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            // Instruct the widget manager to update the widget
            appWidgetManager.updateAppWidget(appWidget, views);
        }
    }

}

استفاده از فیلد های استاتیک با ویجت ها توصیه نمی شود . اما تلاش میکنم این مثال را به ساده ترین شکل ممکن بیان کنم .

شما میتوانید به جای استفاده از فیلد های استاتیک از SharedPreferences استفاده کنید .

نتیجه به این صورت می باشد.

ویجت به وسیله broadcast آپدیت می شود .

 

برای مشاهده کامل کد ها میتوانید از  این لینک استفاده کنید .

شما می توانید هر آنچه را که میخواهید با broadcast ارسال کنید و هم چنین میتوانید هر چیزی را بخواهید از broadcast  دریافت کنید  .

 

ویجت قابل تنظیم

برخی از ویجت ها در زمان ایجاد قابل تنظیم می باشند.

در این مثال، ما یک لینک از کاربر دریافت می کنیم و هر بار که کلیک می کنیم این لینک را در مرورگر باز می کنیم.

ConfigureActivity را برای تنظیمات کاربر ایجاد میکنیم .

Layout اولیه برای اکتیویتی :

activity_widget_configure.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical"
              android:padding="16dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="Configure your url"/>

    <EditText
        android:id="@+id/etUrl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="URL"
        android:inputType="text"/>

    <Button
        android:id="@+id/btAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="@string/add_widget"/>

</LinearLayout>

در متد onCreate اکتیویتی , اولین کاری که ما باید انجام دهیم تنظیم (setResult(RESULT_CANCELED می باشد

چرا؟ اندروید تنظیمات اکتیویتی که متعلق به ویجت شماست را راه اندازی میکند و منتظر نتیجه داده ها می ماند.

اگر کاربر تنظیمات رو همان طور که انتظار داشتیم انجام ندهد , میگوییم او دکمه برگشت را بدون وارد کردن داده فشار داده و ما نیازی به ایجاد ویجت نداریم.

ConfigurableWidgetConfigureActivity.java

public class ConfigurableWidgetConfigureActivity extends Activity {
    int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    private EditText etUrl;
    private Button btAdd;
    private AppWidgetManager widgetManager;
    private RemoteViews views;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setResult(RESULT_CANCELED);
        // activity stuffs
        setContentView(R.layout.activity_widget_configure);
        etUrl = (EditText) findViewById(R.id.etUrl);
        // These steps are seen in the previous examples
        widgetManager = AppWidgetManager.getInstance(this);
        views = new RemoteViews(this.getPackageName(), R.layout.configurable_widget);
        // Find the widget id from the intent.
        Intent intent = getIntent();
        Bundle extras = intent.getExtras();
        if (extras != null) {
            mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        }
        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish();
            return;
        }
        btAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Gets user input
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(etUrl.getText().toString()));
                PendingIntent pending = PendingIntent.getActivity(ConfigurableWidgetConfigureActivity.this, 0, intent, 0);
                views.setOnClickPendingIntent(R.id.ivWidget, pending);
                widgetManager.updateAppWidget(mAppWidgetId, views);
                Intent resultValue = new Intent();
                // Set the results as expected from a 'configure activity'.
                resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
                setResult(RESULT_OK, resultValue);
                finish();
            }
        });
    }
}

آخرین چیزی که ما باید اصلاح کنیم XML ویجت می باشد .

با این اصلاحیه سیستم عامل اندروید می داند این ویجت اکتیویتی تنطیمات دارد , بنابراین قبل از ایجاد ویجت این اکتیویتی را فعال خواهد کرد .

همانطور که متوجه شدید در مورد کلاس ویجت صحبت نکرده ایم. ما نیاز به اضافه کردن کدی برای کلاس ویجت نداریم زیرا تمام اقدامات توسط ConfigurableWidgetConfigureActivity انجام شده است . اما به هر حال ما باید ایجاد کنیم .

ConfigurableWidgetExample

نتیجه را مشاهده کنید .

برای مشاهده کامل کدها میتوانید از این لینک استفاده کنید .

 

بروزرسانی ویجت توسط سرویس

این پروژه اعداد تصادفی را در هر دقیقه تولید می کند و آن را بر روی ویجت نمایش می دهد.

اول از همه، ما به یک سرویس برای تولید اعداد تصادفی نیاز داریم .

UpdateService.java

public class UpdateService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // generates random number
        Random random = new Random();
        int randomInt = random.nextInt(100);
        String lastUpdate = "R: "+randomInt;
        // Reaches the view on widget and displays the number
        RemoteViews view = new RemoteViews(getPackageName(), R.layout.updating_widget);
        view.setTextViewText(R.id.tvWidget, lastUpdate);
        ComponentName theWidget = new ComponentName(this, UpdatingWidget.class);
        AppWidgetManager manager = AppWidgetManager.getInstance(this);
        manager.updateAppWidget(theWidget, view);
        
        return super.onStartCommand(intent, flags, startId);
    }
}

و ما باید این را به AndroidManifest اضافه کنیم.

AndroidManifest

سرویس ها به خودی خود آغاز نمی شود و ما باید سرویس را (در هر دقیقه برای این مثال) شروع کنیم.

اما چرا ما فقط از 'updatePeriodMillis' استفاده نمی کنیم؟

اگر در زمان بروز رسانی (زمان تعریف شده توسط  updatePeriodMillis ) دیوایس در حالت sleep  باشد , 

در آن هنگام دیوایس برای انجام بروزرسانی از حالت sleep  خارج می شود .

اگر بازه برورسانی کمتر از یک ساعت نباشد احتمالا مشکل قابل توجهی برای عمر باتری ایجاد نمی شود .

اگر با این حال شما نیاز به بروزرسانی مرتبی داشته باشید و یا نیاز به روزرسانی در زمانی که دیوایس در حالت sleep  میباشد ندارید , 

شما می توانید به جای آن از یک alarm برای بروزرسانی استفاده کنید که دیوایس رو از حالت Sleep خارج نمی کند .

برای انجام این کار , alarm را  تنظیم کنید  با یک Intent  که AppWidgetProvider  با استفاده AlarmManager دریافت می کند .

نوع زنگ را برای هر دو  ELAPSED_REALTIME  یا  RTC تنظیم کنید ,

که تنها زمانی دیوایس بیدار است alarm به صدا در میآید و سپس updatePeriodMillis را به مقدار ( "0" ) تنظیم کنید

 

در مثال های قبلی ما از متد های ()getActivity. و ()getBroadcast. استفاده کردیم .

این بار میخواهیم از متد ()getService. استفاده کنیم .

UpdatingWidget.java 

public class UpdatingWidget extends AppWidgetProvider {
    private PendingIntent service;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        final Intent i = new Intent(context, UpdateService.class);

        if (service == null) {
            service = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
        }
        manager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), 60000, service);
    }
}

حداقل بازه زمانی 60000 میلی ثانیه برای AlarmManager می باشد

اگر شما نیاز به فراخوانی سرویسی در کمتر از 60 ثانیه با alarm manager دارید  بعضی از راه حل هایی مثل این وجود دارد

اما من باید به شما هشدار می دهم ، این عمل باتری را تخلیه می کند و باعث می شود کاربران برنامه ی شما را حذف کنند .

به روز رسانی ویجت با مقادیر تصادفی تولید شده توسط سرویس .

نتیحه را دیدید (ویدیو 2 دقیقه برش داده شده تا از انتظار 2 دقیقه تی شما جلوگیری شود)

شما میتوانید کد کامل در این لینک مشاهده کنید .

 

اطلاعات بیشتر در مورد ویجت ها :

طراحی متریال ویجت چگونه می باشد؟

لینک های مفید برای مطالعه بیشتر :

بابت خواندن این مقاله از شما تشکر میکنم .

 

 

 


کلیدواژه: ایجاد Widget در اندروید آموزش ویجت های اندروید کار با ویجت ها آموزش ساختن ویجت اندروید ساخت ویجت در اندروید استودیو

منابع: medium.com

ارسال دیدگاه:
برای ارسال دیگاه باید به سیستم وارد شوید و یا ثبت نام کنید. ثبت نام چند لحظه بیشتر زمان شما را نمیگیرد.