6行書くだけで誰でもできるウィジェット開発
皆さん,はじめまして.
CheerAppsで主にAndroidアプリ開発をしているshiitaです.
Androidアプリ開発で役立つTIPSなどについて、
伝えていけたらいいなと思います!
しばらくの間は、ウィジェットカウンターの開発で学んだことについて,
記事をいくつか書いていきます!
今回は簡単なウィジェットの開発について話したいと思います.
具体的には付箋のようにメモができるウィジェットを作ります!
使用するプログラミング言語は,私の一番好きなKotlinで書いていきたいと思います.
AndroidStudioで自動生成されるサンプルのプログラムをうまく活用して,
数行のコード編集だけで作れるものを紹介します.
ウィジェットの追加
右クリックをして出てくるメニューから,Widgetを選択し,追加します.
下の画像のように,クラス名やウィジェットのサイズなどを設定していくのですが,
ここで1つポイントがあります.
Configuration Screenにチェック
これを行うことで,ウィジェットの設定画面もAndroidStudioが自動生成してくれます!
できるだけ簡単にウィジェットを作って行きたいため,
楽できるところは楽して行きましょうwww
コードの編集
付箋のようにメモができるウィジェットを作るために,
コードの編集をしていきます.
編集する箇所はたったの6行です!!!
エラーの除去
AndroidStudioで自動生成されるコードはJavaで書かれたもので,
その後にKotlinへのコンバートが行われます.
このときに入り込んでしまうエラーを解消していきます.
class NoteConfigureActivity : Activity() { internal var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID internal lateinit var mAppWidgetText: EditText // onCreateで初期化するのでlateinitをつける internal var mOnClickListener: View.OnClickListener = View.OnClickListener { val context = this@NoteConfigureActivity // When the button is clicked, store the string locally val widgetText = mAppWidgetText.text.toString() saveTitlePref(context, mAppWidgetId, widgetText) // It is the responsibility of the configuration activity to update the app widget val appWidgetManager = AppWidgetManager.getInstance(context) Note.updateAppWidget(context, appWidgetManager, mAppWidgetId) // Make sure we pass back the original appWidgetId val resultValue = Intent() resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId) setResult(Activity.RESULT_OK, resultValue) finish() } public override fun onCreate(icicle: Bundle?) { super.onCreate(icicle) // Set the result to CANCELED. This will cause the widget host to cancel // out of the widget placement if the user presses the back button. setResult(Activity.RESULT_CANCELED) setContentView(R.layout.note_configure) mAppWidgetText = findViewById<View>(R.id.appwidget_text) as EditText findViewById<View>(R.id.add_button).setOnClickListener(mOnClickListener) // Find the widget id from the intent. val intent = intent val extras = intent.extras if (extras != null) { mAppWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) } // If this activity was started with an intent without an app widget ID, finish with an error. if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { finish() return } mAppWidgetText.setText(loadTitlePref(this@NoteConfigureActivity, mAppWidgetId)) } companion object { private val PREFS_NAME = "jp.cheerapps.note.Note" private val PREF_PREFIX_KEY = "appwidget_" // Write the prefix to the SharedPreferences object for this widget internal fun saveTitlePref(context: Context, appWidgetId: Int, text: String) { val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit() prefs.putString(PREF_PREFIX_KEY + appWidgetId, text) prefs.apply() } // Read the prefix from the SharedPreferences object for this widget. // If there is no preference saved, get the default from a resource internal fun loadTitlePref(context: Context, appWidgetId: Int): String { val prefs = context.getSharedPreferences(PREFS_NAME, 0) val titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null) return titleValue ?: context.getString(R.string.appwidget_text) } internal fun deleteTitlePref(context: Context, appWidgetId: Int) { val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit() prefs.remove(PREF_PREFIX_KEY + appWidgetId) prefs.apply() } } }
コードを全て載せましたが,この部分で変更した箇所は
internal var mAppWidgetText: EditText
を
internal lateinit var mAppWidgetText: EditText
のようにlateinitを追加しただけです!
mAppWidgetTextの初期化はonCreate()で行うため,
後から初期化をすることを,明示的に示す必要があるのです.
設定画面の呼び出し
現在,設定画面はウィジェット追加時だけしか表示されません.
そこで,ウィジェットに記述したテキストを後から編集できるようにします.
具体的な処理は,ウィジェットをタップすると設定画面を呼び出すようにしたいと思います.
class Note : AppWidgetProvider() { override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { // There may be multiple widgets active, so update all of them for (appWidgetId in appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId) } } override fun onDeleted(context: Context, appWidgetIds: IntArray) { // When the user deletes the widget, delete the preference associated with it. for (appWidgetId in appWidgetIds) { NoteConfigureActivity.deleteTitlePref(context, appWidgetId) } } override fun onEnabled(context: Context) { // Enter relevant functionality for when the first widget is created } override fun onDisabled(context: Context) { // Enter relevant functionality for when the last widget is disabled } companion object { internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) { val widgetText = NoteConfigureActivity.loadTitlePref(context, appWidgetId) // Construct the RemoteViews object val views = RemoteViews(context.packageName, R.layout.note) views.setTextViewText(R.id.appwidget_text, widgetText) // クリックで設定画面を開く val intent = Intent(context, NoteConfigureActivity::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } val pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT) views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent) // Instruct the widget manager to update the widget appWidgetManager.updateAppWidget(appWidgetId, views) } } }
追加部分は以下の5行です.
val intent = Intent(context, NoteConfigureActivity::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } val pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT) views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent)
設定画面であるNoteConfigureActivityに対するPendingIntentを作成し,
ウィジェットのViewであるRemoteViewsに対してsetOnClickPendingIntent()
でクリック時の処理を追加します.
RemoteViewsはクリックイベントの処理は,通常のViewと異なることに注意が必要です.
実行結果
ウィジェット追加 | ウィジェット選択 |
---|---|
ウィジェット設定 | ウィジェット例 |
実行結果はこの表のようになっています.
ウィジェットの設定画面で書いたテキストが,
ウィジェットにちゃんと追加されていることが確認できます.
default activity not found エラー
default activity not foundのエラーが出て,以下の画像の様に実行できない場合があります.
これはConfigurationのLaunchをNothingにすることで解消することができます.
おわりに
ここまで読んでいただきありがとうございました!
いかがでしたか?記事の感想,コードへの指摘など頂けると嬉しいです.
CheerAppが開発した最初のアプリ,「ウィジェットカウンター」は
こちらからダウンロードできます!
ではでは.