- 投稿日:2020-02-17T23:25:09+09:00
DialogFragmentの利用方法
プロダクトの既存コードで使われている
DialogFragment
を改善したので、その修正内容をメモしておきます前提
実装予定のダイアログは以下のようなごく一般的なものとする
修正前
UpdateConfirmDialogFragment.ktclass UpdateConfirmDialogFragment : DialogFragment() { var mTitle: String? = null private var listener: OnFragmentInteractionListener? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return AlertDialog.Builder(activity!!) .setTitle(mTitle) .setPositiveButton("ok") { _, _ -> listener?.onClickConfirmOk(confirmType) } .setNegativeButton("cancel") { _, _ -> listener?.onClickConfirmCancel(confirmType) } .create() } override fun onDetach() { super.onDetach() listener = null } fun setListener(listener: OnFragmentInteractionListener){ this.listener = listener } interface OnFragmentInteractionListener { fun onClickConfirmOk() fun onClickConfirmCancel() } }※プロダクトコードを思い出しながら修正後のコードから作ったコードなので、誤りがあるかもしれません
以下、修正内容です
1. interfaceの設定方法変更
androidのactivity/fragmentは、インスタンス破棄後の復帰を考慮してライフサイクルメソッドで値を設定するべきです。今回のコードでは
ダイアログタイトルがpublic変数、イベントコールバック用のinterfaceがpublicメソッドを使って設定されていましたが、それぞれargument及びcontextから取得するようにしました。UpdateConfirmDialogFragment.ktprivate val mTitle by lazy { arguments?.getString(ARG_Title, "") ?: "" } override fun onAttach(context: Context) { super.onAttach(context) listener = context as? OnFragmentInteractionListener } companion object { private const val ARG_TITLE = "ARG_TITLE" fun newInstance(title: String) = ConfirmDialogFragment().apply { arguments = Bundle().apply { putString(ARG_TITLE, title) } } }2. 類似要件のダイアログも生成できるようにする
修正を進めていたところ、ダイアログ自体の要件がほぼ変わらないDelete用のダイアログがあることがわかりました。そのため、インスタンス作成時にタイトルではなくTypeを渡すようにし、タイトルはその変数として保持させるようにしました。また、それをコールバックの引数として設定し、利用側で判別できるようにしました(※同一Activity内で2種類のダイアログ表示があった際の考慮です)。Fragment名は、より汎用的にするために
ConfirmDialogFragment
にリネームしています。ConfirmDialogFragment.ktoverride fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return AlertDialog.Builder(activity!!) .setTitle(confirmType?.title) .setPositiveButton("ok") { _, _ -> listener?.onClickConfirmOk(confirmType) } .setNegativeButton("cancel") { _, _ -> listener?.onClickConfirmCancel(confirmType) } .create() } interface OnFragmentInteractionListener { fun onClickConfirmOk(confirmType: ConfirmType?) fun onClickConfirmCancel(confirmType: ConfirmType?) } sealed class ConfirmType : Serializable { abstract val title: String object Update : ConfirmType() { override val title = "更新しますか?" } object Delete : ConfirmType() { override val title = "削除しますか?" } } companion object { private const val ARG_CONFIRM_TYPE = "ARG_CONFIRM_TYPE" fun newInstance(targetFragment: Fragment? = null, confirmType: ConfirmType) = ConfirmDialogFragment().apply { arguments = Bundle().apply { putSerializable(ARG_CONFIRM_TYPE, confirmType) } } }3. コールバック先を選択できるようにする
更に実装を進めていると、このダイアログはActivityとFragment、どちらからも呼ばれる可能性があることがわかりました。activityはこれまで通り
context
から、fragmentはargument
に設定することでtargetとして取得できるようになります。fragmentの設定をオプションとして用意し、DialogFragment内では意識しないで使えるように拡張関数を用意して利用するようにしました。// 拡張関数 フラグメントが設定されていた場合はそちらを利用する fun <T> DialogFragment.getTarget() = (targetFragment ?: context) as? T override fun onAttach(context: Context) { super.onAttach(context) listener = getTarget() } companion object { fun newInstance(targetFragment: Fragment? = null, confirmType: ConfirmType) = ConfirmDialogFragment().apply { arguments = Bundle().apply { putSerializable(ARG_CONFIRM_TYPE, confirmType) } setTargetFragment(targetFragment, 0) } }修正後
ごちゃごちゃ言いながらリファクタしてきましたが、最終的には以下のようになりました。変数名やConfirmTypeの役割など修正の余地はまだあるかと思いますが、ひとまず最低限の拡張性と安定性は確保できたと思います。
DialogFragment.ktclass ConfirmDialogFragment : DialogFragment() { private val confirmType by lazy { arguments?.getSerializable(ARG_CONFIRM_TYPE) as? ConfirmType } private var listener: OnFragmentInteractionListener? = null override fun onAttach(context: Context) { super.onAttach(context) listener = getTarget() } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return AlertDialog.Builder(activity!!) .setTitle(confirmType?.title) .setPositiveButton("ok") { _, _ -> listener?.onClickConfirmOk(confirmType) } .setNegativeButton("cancel") { _, _ -> listener?.onClickConfirmCancel(confirmType) } .create() } override fun onDetach() { super.onDetach() listener = null } interface OnFragmentInteractionListener { fun onClickConfirmOk(confirmType: ConfirmType?) fun onClickConfirmCancel(confirmType: ConfirmType?) } sealed class ConfirmType : Serializable { abstract val title: String object Update : ConfirmType() { override val title = "更新しますか?" } object Delete : ConfirmType() { override val title = "削除しますか?" } } companion object { private const val ARG_CONFIRM_TYPE = "ARG_CONFIRM_TYPE" fun newInstance(targetFragment: Fragment? = null, confirmType: ConfirmType) = ConfirmDialogFragment().apply { arguments = Bundle().apply { putSerializable(ARG_CONFIRM_TYPE, confirmType) } setTargetFragment(targetFragment, 0) } } } fun <T> DialogFragment.getTarget() = (targetFragment ?: context) as? Tまとめ
- publicでsetしている変数を見つけたら警戒する
- 拡張関数を用意したものの、そのあたりの抽象化はもっといいやり方がありそう
- 投稿日:2020-02-17T19:09:44+09:00
【Android】Adaptive Launcher Icon の作り方と注意点
Androidでは、GooglePlayストアアイコンとは別に、デバイス側で使うアプリアイコンも作成する必要があります。
Android 8.0(API レベル 26)でアダプティブランチャーアイコンが導入されました。これがまたやっかいで、過去OSにも対応したい場合、新旧の2パターンのアダプティブアイコンを作成しなければなりません。
新旧で仕様が全く異なるのでその作成方法をメモしておきます。アイコンを作成する際の参考になれば嬉しいです。【2/17追記】
コメントを頂いて初めて知ったのですが、アダプティブランチャーアイコンを作成するための機能がなんとAndroidStudioに元からあるとの情報を頂きました。
なんてこったい、次はこのツールで是非作成してみたいと思います(感謝!)
https://developer.android.com/studio/write/image-asset-studio.html?hl=ja#accessアダプティブランチャーアイコンの仕組み
Adaptive Launcher iconは、foreground、background画像と、その上にMaskがかかった3枚構成となっています。
backgroundはエンジニア側でカラーを設定し、マスクは自動でかかるので、デザイナーは基本真ん中のForegroundの画像を作成すればOKです。アダプティブランチャーアイコンの作り方
1:108pxの正方形透明レイヤーを作成する
2:72pxの正方形オブジェクトを1のレイヤーの上に重ねる
3:直径66pxの正円オブジェクトを作成し2の上に重ねる
4:アイコンを正円内に配置する(丸内が表示保証されているエリア)
5:2と3のレイヤーを非表示にし、アイコンが表示された状態で1の正方形を書き出し設定する・foregroundの画像は直径66dpの範囲外はアニメーションで隠れる可能性がある
・マスクはランチャー次第で正円形から四角まで色々ある【注意点】
Risky zoneはマスクによっては範囲に含まれるので、外周にデザインがある場合は72dpまでデザインを入れること。そうしないとやり直すことになるので要注意!
用意する画像サイズ
Adaptive Launcher Iconを作る時に必要な画像サイズは下記の通り。
「背面」も「前面」も画像の場合は各サイズ2つずつ作成する。
「背面」はbackground-colorで指定することも可能なので、背景がベタ塗りの場合は、Foregroundの画像を各サイズ1つずつ用意すればOK。
解像度 比率 Adaptive Launcher icon(px) mdpi 1 108×108 hdpi 1.5 162×162 xhdpi 2 216×216 xxhdpi 3 324×324 xxxhdpi 4 432×432 ファイル名:ic_launcher_foreground.png
レガシーランチャーアイコンの作り方(おまけ)
・書き出し画像サイズ:48dp×48dp
・inner size:44dp (上下左右に2dpずつの余白をとる)用意する画像サイズ
解像度 比率 Adaptive icon(px) mdpi 1 48×48 hdpi 1.5 72×72 xhdpi 2 96×96 xxhdpi 3 144×144 xxxhdpi 4 192×192 ファイル名:ic_launcher.png
参考サイト
Google Developers▼
https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptiveソースコードなどのご参照▼
https://qiita.com/takahirom/items/696fb5ecaa230fa8f755GooglePlayStoreのアイコンの仕様はこちら▼
https://developer.android.com/google-play/resources/icon-design-specificationsおわりに
Adaptive Launcher Iconが追加されて表現は自由になったものの、まだ旧アイコンにも対応しないといけない場合、期間限定のアイコンなどを作成するのが意外と大変だということがわかりました。
今後ここらへんが統一されていけば、もう少し楽になるのではと期待しています。
以上、Adaptive iconで苦戦したkannaでした!
- 投稿日:2020-02-17T18:44:49+09:00
【Android】アプリバーの文字・メニュー・背景色変更方法まとめ
概要
Androidアプリの画面上部のアプリバー(AppBar, ActionBar, Toolbarとも)に、メニューアイコンや「保存」のようなメニューボタンを設置したり、アイコン・文字や背景の色を変えたいことがある。方法を調べてまとめた。
アプリ・アクティビティ全体のスタイルを設定する
AndroidManifestの
<application>
でアプリ全体のテーマを設定できる。
<activity>
で設定した場合はアクティビティ全体のテーマになる。AndroidManifest.xml<application ...中略... android:theme="@style/AppTheme" > ...中略... </application>styles.xml<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="actionMenuTextColor">@color/menuText</item> </style> </resources>colors.xml<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#FF0000</color> <color name="colorPrimaryDark">#00FF00</color> <color name="colorAccent">#0000FF</color> <color name="menuText">#00FFFF</color> </resources>
Fragmentごとにスタイルを変更する
上記のスタイル設定では、Fragmentごとの設定はできない。
各Fragmentでアプリバーの背景色・メニューなどのスタイルを変更する方法を紹介する。アプリバーの背景色を変更する
activity?.supportActionBar?.setBackgroundDrawable()
で色(ColorDrawable
)をセットする。
一度色を変えるとFragment遷移してもそのままであるため、色を戻したい場合は、よしななタイミング(別画面に遷移する時など)で元の色をセットする必要がある。Fragment.ktactivity?.supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.WHITE))) // アプリバーを白にするアプリバーのタイトルの文字色を変更する
ベストプラクティスが分からないが、任意の色を付けた
SpannableString
をセットするのが良さそうだろうか。HTMLでゴリ押す
HTMLタグで文字に色を付けてセットする。
Fragment.ktval titleHtml = "<font color=\"#FF0000\">HTMLタイトル</font>" activity?.supportActionBar?.apply { setTitle(Html.fromHtml(titleHtml)) setBackgroundDrawable(ColorDrawable(Color.WHITE)) }SpannableStringで色を付ける
ForegroundColorSpan
というSpanで文字に色を付けてセットする。val title = SpannableString("SpannableStringタイトル") title.setSpan(ForegroundColorSpan(Color.RED), 0, title.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) activity?.supportActionBar?.apply { setTitle(title) setBackgroundDrawable(ColorDrawable(Color.WHITE)) }アプリバーにメニューを表示する
onCreateOptionsMenu()
内で任意のメニューをセットし、アプリバーにメニューアイコンや文字(「保存」「閉じる」など)を表示する。Fragment.ktoverride fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) this.setHasOptionsMenu(true) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.hoge_menu, menu) }hoge_menu.xml<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_hoge_icon" android:icon="@drawable/hoge_icon" app:showAsAction="always" /> <item android:id="@+id/action_hoge_text" android:title="hoge" app:showAsAction="always" /> </menu>アプリバーのメニューの文字色を変更する
onCreateOptionsMenu()
内で該当のメニューアイテムを取得し、ForegroundColorSpan()
をセットする。
SpannableString
作成の際に文字列を変更することもできる。Fragment.ktoverride fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.hoge_menu, menu) val item = menu.findItem(R.id.action_hoge_text) // メニューアイテム取得 val s = SpannableString("fuga") s.setSpan(ForegroundColorSpan(Color.YELLOW), 0, s.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) item.setTitle(s) // メニューアイテムにSpannableをセット }アプリバーのメニューアイコンの色を変更する
onCreateOptionsMenu()
内で該当のメニューアイテムのicon
(Drawable
)を取得し、setColorFilter()
で色を変更する。
フィルターの種類についてはPorterDuff.Mode
を参照されたい。Fragment.ktoverride fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.hoge_menu, menu) val item = menu.findItem(R.id.action_hoge_icon) item.icon?.apply { mutate() // Drawableを変更可能にする setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP) // アイコンを白くする } }まとめ
アプリバーの文字色・メニューアイテムの色・背景色変更について、アプリ全体・個別Fragmentでの方法を紹介した。
- 投稿日:2020-02-17T15:25:57+09:00
僕はただAndroidで自Serviceから他Activityにタッチイベントを発生させたいだけだったのに(ラノベ調
※最初に
この仕組みは使用者(ユーザ)にも実装者にもリスクがあるものと思ってます
自身まだ完全に内容や動きを理解しているわけではなく、あくまで参考記事として受け取ってもらえると助かります。目的
わたくしめは趣味でソシャゲを嗜んでおります。
ゲームといえども効率化って大事だよね!みんなも昔連打コンとか使ってたよね!
以上
実際に成功した実装内容について
たどり着く前に紆余曲折あったのですが結論から先に
ユーザ補助機能を使いましょう
ただし!この機能は非常に強力でありながら大変リスキーな機能となっております。
使用の際(特にアプリを配布したりする場合)はご自身でもよく調査してから使用する事を限りなくおすすめします。ユーザ補助機能とは
目の見えない人とか一部の操作(スワイプとかダブルタップ)が身体的理由等で使えない人に向けた補助サービス用機能とういう解釈です。
ユーザ補助機能一部でどういった事ができるようになるのかは下記の記事がわかりやすいと思います。
https://www.slideshare.net/r-ralph/accessibilityserviceようはなんでもできます(たぶん)
実装
①AndroidManifest修正
②Manifest用のXML作成
③Activity(Fragment)で権限許可リクエストをなげる
④AccessibilityService
の立ち上げ
⑤GestureDescription.Builder().addStroke でタップイベントを発生させる①AndroidManifest修正
<service android:name=“xxx.xxxx.xxx.xxxService” android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService"/> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/service_config" /> </service>AndroidManifestのapplication部に記述してください
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" tools:ignore="ProtectedPermissions" />場合によっては
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>が必要かも
FOREGROUND_SERVICEはServiceを使おうとおもったらついてまわってくる権限です@xml/service_configは次の項ででるやつです
②Manifest用のXML作成
res/xml内に下記ファイルを追加
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeAllMask" android:accessibilityFeedbackType="feedbackAllMask" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:canPerformGestures="true" android:description="@string/accessibility_desciription " />各設定の詳細はここ見てください
https://developer.android.com/reference/android/accessibilityservice/AccessibilityServiceInfoタッチ挙動させる場合特に重要なのは
canPerformGestures
こやつです③Activity(Fragment)で権限許可リクエストをなげる
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // OVERLAY許可 if (!Settings.canDrawOverlays(this)) { val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())) this.startActivityForResult(intent, REQUEST_SYSTEM_OVERLAY) } val intent2 = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) intent2.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) this.startActivity(intent2) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { REQUEST_SYSTEM_OVERLAY -> { if (resultCode == Activity.RESULT_OK) { } else { finish() } } else -> { finish() } } }いろいろやっつけですまんの
いっこ注意点としてはACTION_ACCESSIBILITY_SETTINGSはstartActivityForResult
で投げても意味がありません(たぶん
なので設定がONかOFFか調べるのも結構苦労する④AccessibilityService
の立ち上げ
val settingValue = Settings.Secure.getString( this.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES ) if (settingValue.isNotEmpty()) { val simpleStringSplitter = TextUtils.SimpleStringSplitter(':') simpleStringSplitter.setString(settingValue) val name : String = packageName + "/" + xxxService::class.java.canonicalName while (simpleStringSplitter.hasNext()) { if (simpleStringSplitter.next().equals(name)) { startService(Intent(this, xxxService::class.java)) return } } }ユーザ補助の設定がONがどうか調査しつつServiceを立ち上げ
⑤GestureDescription.Builder().addStroke でタップイベントを発生させる
class xxxService :AccessibilityService() {継承はAccessibilityServiceを継承する事。
var point : Point = Point() mWindowManager.getDefaultDisplay().getSize(point) val gestureBuilder = GestureDescription.Builder() val path = Path() path.moveTo(tapPosX,tapPosY) gestureBuilder.addStroke(GestureDescription.StrokeDescription(path,0L,10L)) val gestureDescription = gestureBuilder.build() val flg : Boolean = mAccessibilityService.dispatchGesture(gestureDescription,object : AccessibilityService.GestureResultCallback(){ override fun onCompleted(gestureDescription:GestureDescription) { super.onCompleted(gestureDescription); } override fun onCancelled(gestureDescription: GestureDescription?) { super.onCancelled(gestureDescription) } },null)実際のタップしたときのイベントはこんな感じです。
これとは別にVerによってはnotificationを設定しないとそもそもServiceが立ち上がらないのでご注意を
課題
・AccessibilityServiceの場合Activityから終了させる事ができない。(Service自身が自身を終了させる必要がある)
・Serviceは今表示されているActivityが何かを保証していない
・Serviceはたまにシステムから消される
・重い処理を書きづらい
・AccessibilityEventの全容がわからないいやー!理解せず使って連打処理とか仕込んで意図せず課金しちゃったり電話しちゃったりしたら大変だね!
なので個人用には使えるけどアプリ配布まではまだまだ勉強不足かなといった感じです
ここに至るまで
超大変だった。
ぐぐって出てきたものも色々試してみたんですけど中々やりたい事に辿りつけなかったですねただ調査開始から試した事、最後にこれに至ったまでを話すと長くなるので割愛します。
最後に
「Serviceは今表示されているActivityが何かを保証していない」に関してはいくつか解決策を見つけたので時間があればまた記事にするかも
ご意見等ありましたらお手柔らかにお願いします
- 投稿日:2020-02-17T14:17:31+09:00
Android アプリ公開時に必要な画像リソースをサックっと用意する手順まとめ
みなさん、Androidアプリ公開してますか?
自分はアプリを作るのは好きなのですが、それを公開するのにあれこれ手間をかけるのは正直好きではありません、、、
簡単な自作アプリはだいたい自分が使うためにつくっているのでそれでも問題ないといえばないのですが、ビルド環境やライブラリの依存関係その他最新の開発環境全般につねにキャッチアップしておく、というのがモチベーションの一つでもあるので、いつでも更新し、リリース出来るような環境を整えたいと思うことがままあります。
その昔、Androidがリリースされたばかりの頃は、ストアには審査なんてないし、特にリソースを追加しなくてもapkをサクッとアップロードすればOKだったのですが、世知辛い世の中になったものよのう。
と、言っていてもしょうがないので、アプリをつくったらサクッと公開するために必要なリソースを取得する手順をまとめておきます。
必要なリソース
AndroidアプリをGooogle Play Storeで公開する上で必須の画像リソースは以下3種類です。
- アイコン
- スクリーンショット
- feature graphic
ストア用のアイコンを用意する
Google Play Storeに公開するときに、アプリ内のリソースに含まれるアイコン以外に、大きいサイズ(512*512)のアイコンをアップロードする必要があります。
こちらを作るには、アプリ内のアイコンリソースと合わせて「Android Asset Studio」を使いましょう。
Android Asset Studio - Launcher icon generator
ここでは使い方の詳細は省きますが、画像や用意されているクリップアートをもとにアイコン画像が作れるツールです。
背景色や形などを選び、ダウンロードボタンでzipファイルをダウンロードすると、アプリの'res'以下に含める画像とともに"web_hi_res_512.png"という名前のファイルが含まれていて、こちらがPlay Storeにアップロードするアイコンとして使えます。
スクリーンショットを取る
次に、アプリのスクリーンショットが最低2枚必要となります。こちらはアプリを実機もしくはエミュレータで起動し、スクリーンショットを取ればいいので一見難しいことはありません。
ところが、このスクリーンショットにはステータスバーも含まれるため、起動中の他のアプリの通知アイコンなどが写ってしまいます。ストアに公開するスクリーンショットとなると、他のアプリのアイコンが写っていていいのか、時刻が夜中だったりすると良くないんじゃないか、などいろいろと考えることが出てきてしまいます。
そんなときのために、Androidには"Demo Mode"という、ステータスバーの表示内容を自由に書き換えられる機能が用意されています。
Developer Optionを有効にした端末で、Settingsの"System -> Developer options -> System UI demo mode"でDemo ModeのON/OFFが切り替えられます。
ただし、UI上では細かな設定が出来ないことと、毎回手で操作するのも手間なので、adbを使ってCLIで実行するのがいいでしょう。
adbコマンドは以下が詳しいです。
Demo Mode for the Android System UI
さて、ここではアプリの通知アイコンを消し指定のみを行って、スクリーンショットを取ることを考えましょう。
Demo Modeの設定とともに、スクリーンショットの取得までadbで行得るので、以下をtake_release_screenshot.shなどスクリプトとして保存しておくと取り直しも楽になります。
#!/bin/sh adb shell settings put global sysui_demo_allowed 1 adb shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false adb exec-out screencap -p > screen.png adb shell am broadcast -a com.android.systemui.demo -e command exitFeature Graphicを作成する
最後にFeature Graphicを作成します。Feature Graphicは1024 w x 500 hの画像で、Play Storeでオススメなどに掲載された場合に使われます。また、ストアのリンクを貼った場合にogp画像としても使われます。
そのため、アプリのユーザー獲得のためには非常に重要な画像となるのですが、そんなことよりまずは手間を最小限にして公開したくなりますよね^^
そんな方に最適なツールがこちらです。
Android Feature Graphic Generator
文字と背景を選ぶだけで、サクッと作れます。
必須ではないリソースの"promo graphic"や"TV Banner"向けのサイズも用意されています。
まとめ
以上で、AndroidアプリをGoogle Play Storeで公開する際に必須となっている画像リソースを手軽に用意できます。
画像以外にも"Title", "Short Description", "Full description"が必要になりますが、こちらはhogeでもfugaでもOKなので、困ることはないでしょう。
こうしてリリースしたのが、こちらの超シンプルは時報アプリです。しっかりFeature GraphicがOGPに使われていますね!
個人アプリを作る理由として、オリジナリティのある名前をつけたり、ビジュアル面やプロモーションまでこだわることがモチベーションとなっている方もいると思いますので、そういう場合はもちろんこだわって作るのがいいでしょう。
一方で、動くものをサクッとつくって公開したい、というようなモチベーションの方はこのような方法でリリース作業にかかる負荷を下げるのがいいんじゃないかと思ってます。
アプリつくったけど公開めんどくさくて諦めた、、、って方はぜひ参考にしてみてください!
- 投稿日:2020-02-17T00:59:21+09:00
Flutterでコンパスアプリを作ってリリースした話
はじめに
個人開発でFlutterを使用して、コンパスアプリを作り、リリースしてみましたので、その実装方法や使用したライブラリなどを紹介してみようと思います。
作ったアプリ
作ったアプリはコンパスですが、ただのコンパスでなく、恵方巻コンパスです。
普通のコンパスとの違いは、「北を差すのでなく、恵方を差す」というだけです!恵方巻コンパスアプリ自体は2年前にiOSアプリをリリースしていて、去年はAndroid版もリリースしていたのですが、今回はAndroid版のみFlutterで作り直してリリースしました。
恵方巻コンパス2020Flutterで作った理由
自分は普段仕事でiOSアプリの開発を行っているので、iOSは慣れているですが、Androidについてはあまり詳しくないです。
詳しくないながらも、去年kotlinで作ってリリースしたのですが、今年、恵方の方向を2020年版にアップデートしようと思ったところ、APIレベルの更新やAndroidX対応など案外とやることがありそうだったので、どうせならFlutterで作り直した方が簡単に出来るのではと思ったことが、今回、Android版のみをFlutterで作った理由です。(Flutterを触ったことがあったのも理由の一つです。)Flutterでコンパスの実装方法
まず、Flutterでコンパスアプリを作るにはどうしたらよいか検索したところ、flutter_compassというライブラリを使用してコンパスアプリを作る記事があり、それを参考に実装しました。
サンプルコード
恵方を指すコンパス自体は、50行くらいで実装できます。
参考にした記事のコードをproviderを使用して、シンプルに書き換えてみました。class CompassPage extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider<Compass>( create: (_) => Compass(), child: Consumer<Compass>( builder: (context, compass, child) { return Scaffold( appBar: AppBar( title: Text('タイトル'), ), body: Center( child: Transform.rotate( angle: compass.angle, child: Image.asset( 'images/arrow.png', width: 100, height: 100, ), ), ), ); }, ), ); } } class Compass with ChangeNotifier { static const ehoAngle = 255.0; double _angle = 0; double get angle => _angle; Compass() { FlutterCompass.events.listen((value) { _angle = -1 * pi * ((value - ehoAngle) / 180); notifyListeners(); }); } }githubに公開しておきました。
flutter_compass
FlutterCompassのイベントをlistenしておくと、端末の方向が変わる度に、呼び出されます。
角度が取得できるので、それをラジアンに変換し、notifyListenersで画面に通知しています。ラジアンに変換する際に、実際の角度から恵方の角度を引いた値を指定しています。(2020年の恵方は255度なので、255を引いています。)FlutterCompass.events.listen((value) { _angle = -1 * pi * ((value - ehoAngle) / 180); notifyListeners(); });Transform.rotate
Transform.rotateを使用して、画像を傾けて表示しています。
Transform.rotate( angle: compass.angle, child: Image.asset( 'images/arrow.png', width: 100, height: 100, ), ),その他、使用したライブラリ
flutter_launcher_icons
アイコンを簡単に作成できるライブラリです。
https://pub.dev/packages/flutter_launcher_iconssimple_animations
アニメーションを実装するために使用しました。
https://pub.dev/packages/simple_animationsvibration
バイブレーションを実装するために使用しました。
https://pub.dev/packages/vibrationfirebase_admob
admobの広告を出すためのライブラリです。
https://pub.dev/packages/firebase_admob最後に
今回紹介したAndorid版のアプリは2000ダウンロード程度でした。
iOS版は1日で40000ダウンロードされたので、恵方巻という完全に日本人向けのアプリでは、iOSの方がまだまだ有利なのかなと感じました。Flutterはマルチプラットフォームのframeworkですが、iOS、Androidのどちらかのみをリリースしているアプリの場合、リリースしていない方をFlutterで開発してみるのは、学習コストや将来性の面でもありかなと思いました。(良い感じに出来たら、その後は両OSをFlutterに移行する事も可能ですし!)