Androidアプリ「投資名言」リリースしました!
こんにちは。
2019年11月、個人開発のAndroidアプリ「投資名言」をリリースしました。
投資に関する名言ですが、人生に応用できるようなよい言葉がたくさん入っているアプリです。
ネットワークアクセスもほとんど無い電池に優しいアプリですので是非お試しください(^^)
それにしても私らしく地味なアプリです(笑)
画面も黒っぽいです。
機能はシンプルです。毎日通知が届くのでそこから名言の解説画面に遷移するような使い方になると想定しています。
アプリを作る前はこんな感じの画面遷移図を作って大体のイメージを作るようにしています。
この図はSketchというMacのアプリで作成しました。
こういうのを作る度にもっとデザイン方面に力を入れていきたい、とずっと思っています。
Android開発の技術面では、全体的に新しいコンポーネントを使っています。
などなど試行錯誤で作りました。
最近のコンポーネントの習得はほとんどGoogle Developers Codelabsで学習しました。
しかしながら進化が早いため、教材と最新版がずれてしまうことがザラであり進めるのはコツがいる感じでした。
今後はiOS版も作りたいと思います。
読んでくれてありがとうございました。
【AndroidX】Jetpack のNavigation で戻るボタンの処理をカスタマイズする(Backキーのイベントを拾う, OnBackPressedDispatcher, OnBackPressedCallback, ToolBarの戻るボタン)
- 公式サイト
- 実行した環境
- 参考サイト様
- 何がやりたいのか
- 注意事項
- 必要なライブラリ
- 依存ライブラリのバージョンの確認
- 戻るボタンの処理を記述する
- Navigationで一つ前の画面に戻る
- ActionBarのUpボタンの処理(おまけ)
AndroidのJetpackのNavigationコンポーネントを使って画面遷移をハンドリングしているアプリで、戻るボタンが押された場合の処理をカスタマイズする必要が出た時に、最近出た新しい方法で戻るボタンの処理を書きました。
その時のメモです。
Navigationコンポーネント内の機能というわけではないので、Navigationを使っていないアプリでもonBackPressed
メソッドをoverrideする代わりにその方法を使えます。
おまけでActionBarのToolBarでも戻る矢印のボタンを使っている場合のハンドリングについても書いています。
公式サイト
- Provide custom back navigation | Android Developers
- Activity | Android Developers
- Get started with the Navigation component | Android Developers
- NavController | Android Developers
実行した環境
- Android Studio 3.5
- com.android.tools.build:gradle:3.5.0
- Kotlin 1.3.50
- androidx.appcompat:appcompat:1.1.0-rc01
- androidx.navigation:navigation-fragment-ktx:2.2.0-alpha02
- androidx.navigation:navigation-ui-ktx:2.2.0-alpha02
参考サイト様
何がやりたいのか
JetpackのNavigation componentを使って戻るボタンの処理をNavigationに任せている場合でも、カスタマイズしたい時があります。
例えば何らかの処理を行わないでユーザーが画面を離脱しようとした時に、警告ダイアログを表示するなどが考えられます。
古くからある戻るボタンのハンドリング方法は、Activityクラスの中でonBackPressed
メソッドをoverrideして、その中に処理を記述していましたが、個々のFragmentから見ると親のActivityに処理を任せなくてはならず使い勝手がよくありませんでした。
Jetpackで登場したComponentActivity
のOnBackPressedDispatcher
を使うと、戻るボタンのイベントを受け取って処理を記述できるようになります。
Activityにコードを書かずに済むし、有効と無効の切り替えも簡単に出来ます。
onBackPressed
メソッドのオーバーライドの代わりにこれを使って処理を書いてみます。
注意事項
- AndroidX 移行済みの前提です。
- Jetpackは進化が早いので、この記事も古くなっているかも知れません。参考にされる場合はご了承ください。
- この記事では実装はKotlinのみを使用しています。Javaでの実装は扱っておりません。
必要なライブラリ
AndroidXのActivity が必要となります。
2019年4月25日にリリースされたandroidx.activity:activity:1.0.0-alpha07
で大幅にアップデートされたバックボタンのハンドリングの機能を使います。
その後も変更やメソッド削除等があるので、androidx.activity:activity:1.0.0
、androidx.activity:activity-ktx:1.0.0
の安定版以降を使うのがよいと思います。
公式サイトのAndroidXのActivityの導入ガイドはこちらです。
Activity | Android Developers
依存ライブラリのバージョンの確認
少し話がずれますが、androidx.activity:activity
のライブラリは、
androidx.appcompat:appcompat
やandroidx.navigation:navigation
からも依存されています。
そのためわざわざbuild.gradleのdependenciesに記述しなくてもandroidx.activity:activity
のパッケージを使用出来ます。
ですが、少し古いリリースのものだとまだ実装されていないかもしれないので、一応バージョンの確認をしておくといいでしょう。
依存関係の確認方法の一例として、下記のコマンドをプロジェクトのルート配下で実行すると、depends.txt
というテキストファイルが出来ます。
これを開いてバージョンを確認します。
$ ./gradlew app:dependencies > depends.txt
次の画像は手元で実行した時のスクリーンショットです。
androidx.activity
で検索すると、この例ではandroidx.activity:activity:1.1.0-alpha03 (*)
となっています。
1.0.0以降ですのでバージョンはOKです。
色々なライブラリがandroidx.activity
を参照していてバージョンが異なったとしても、Gradleの依存関係のルールでは参照されてる中で最も最新のものが全てに強制的に適用されます。(カスタマイズも可能)
Gradleの依存関係のルールについては公式サイトをご覧ください。
戻るボタンの処理を記述する
OnBackPressedDispatcherにコールバックを追加する
公式サイトより引用します。
class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // This callback will only be called when MyFragment is at least Started. val callback = requireActivity().onBackPressedDispatcher.addCallback(this) { // Handle the back button event } // The callback can be enabled or disabled here or in the lambda } ... }
説明(ほぼ公式サイトより)
FragmentActivity
とAppCompatActivity
の継承元クラスであるComponentActivity
では、getOnBackPressedDispatcher()
を呼び出して取得できるOnBackPressedDispatcher
を使用して戻るボタンのハンドリングを行うことが出来ます。
OnBackPressedDispatcher
はaddCallback()
メソッドを呼び出されるとOnBackPressedCallback
オブジェクトを返してhandleOnBackPressed()
メソッド内で戻るボタンのイベントをキャッチした時の処理を書くことが出来ます。
コールバックの引数にはLifecycleOwner
を渡します。
OnBackPressedCallback
はLifecycleOwner
の状態がLifecycle.State.STARTED
になってから追加されます。
複数のコールバックを登録できます。
登録された順の逆の順序で有効なコールバックが呼び出されます。
コールバックの有効と無効を切り替える
isEnabled()
メソッドで切り替えます。デフォルトでtrue
になっています。
callback.isEnabled = false
コールバックはChain of Responsibility パターン
に従っています。
コールバックの有効と無効を切り替えることは、呼び出し順序を維持するためにも一時的な変更にすることがおすすめです。
特に複数のネストされたLifecycleOwner
にコールバックが追加されている場合は特に重要です。
OnBackPressedCallbackを削除する
OnBackPressedCallback
を完全に削除する場合は、remove()
メソッドを呼び出す必要があります。
ただし、コールバックは関連付けられているLifecycleOwner
が 破棄されると自動的に削除されるため、通常は必要ありません。
ActivityのonBackPressedメソッド
Activity
のonBackPressed
メソッドをoverrideする方法をとっている場合は、
OnBackPressedCallback
を代わりに使うのがおすすめです。
この変更を行うことができない場合は、次の規則が適用されます。
addCallback
で登録されたコールバックは、super.onBackPressed()を呼び出した時に評価される- onBackPressed()はOnBackPressedCallbackのインスタンスに関係なく常に呼び出される。
Navigationで一つ前の画面に戻る
Navigationのバックスタックを一つ遡るには、NavController.popBackStack()
メソッドを使います。
特定の画面に遷移したい場合は公式サイトをご覧ください。
requireActivity().onBackPressedDispatcher.addCallback(this@MyFragment) { // Handle the back button event ... findNavController().popBackStack() }
ActionBarのUpボタンの処理(おまけ)
NavigationUI.setupActionBarWithNavController()を使用してNavigationコンポーネントとアクションバーの挙動を結びつけると、NavigationがToolBarの領域に戻る矢印のUpボタンなど状況に応じて適切なナビゲーションを表示してくれるようになります。 (レイアウト内のfragmentタグで「app:defaultNavHost="true"」となっている前提です)
参考コード
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { ... // レイアウト内のfragmentタグで「app:defaultNavHost="true"」となっている前提 setContentView(main_activity) val host: NavHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return val navController = host.navController setSupportActionBar(binding.toolbar) val appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) } override fun onSupportNavigateUp(): Boolean = findNavController(R.id.nav_host_fragment).navigateUp() }
Upボタンのイベントを取得して処理をカスタマイズする場合は
Fragment
内でonOptionsItemSelectedメソッドをオーバーライドしてMenuItem
のidが android.R.id.home
の時にUpボタンが押された時の処理を記述します。
class MyFragment : Fragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { // Upボタンが押された時の処理 // falseを指定すると、Upボタンの処理が続行されて前の画面に戻る // trueを指定した時は自分でfindNavController().popBackStack()などを実行して前の画面に戻る false } R.id.action_share -> { // 何らかの処理 true } else -> super.onOptionsItemSelected(item) } } }
Sketchで背景をテキストでマスクする&背景の中のテキスト部分を透過させる方法
Sketchを使って画像やシェイプを文字の形に切り抜くやり方と、
画像やシェイプの中のテキストの部分を透明にするやり方のメモです。
次の画像のようなものを作ります。
公式サイト
参考サイト様
①シェイプの中のテキスト部分を透明にする
Rectangleのシェイプのレイヤーの上にテキストのレイヤーを重ねます。
テキストはそのままでもいいし、Convert to Outlines
でパスにしてもいいです。
テキストレイヤーとシェイプのレイヤーを同時に選択します。
メニューからLayer
→ Combine
→ Subtract
又は Difference
を選択します。
するとCombined Shape
というレイヤーになりテキスト部分が透過されます。
②画像の中のテキスト部分を透明にする
画像で行う場合は、画像レイヤーの上にテキストのレイヤーを重ねてCombined Shape
を作ってもうまくいきません。
シェイプの背景を画像にした後にCombined Shape
を作ります。
まずRectangleのシェイプを作ります。(説明のためRectangleのシェイプにしていますが、Ovalなどの形でも大丈夫です)
右側のスタイル設定の中の Fills
から Pattern Fill
をクリックし、
IMAGES
をクリックして画像を読み込み、Display
はFill
にします。
上の説明画像とは違う背景ですが、シェイプの背景が設定されました。
後は ①シェイプの中のテキスト部分を透明にすると同じように、
テキストレイヤーを作成し、 テキストレイヤーとシェイプのレイヤーを同時に選択した状態で
メニューからLayer
→ Combine
→ Subtract
又は Difference
を選択すれば Combined Shape
というレイヤーになりテキスト部分が透過されます。
③背景画像をテキストでマスクして画像を文字の形に切り抜く
参考サイト様がとても詳しく説明してくださっているので、そちらを読むのが一番よいと思いますが、
私の手元で試したものを軽く説明させていただきます。
Advanced Text Mask In Sketch - Design + Sketch - Medium
③-1 Combined Shapeによるマスク
まず、②画像の中のテキスト部分を透明にする と同様に
シェイプを作成し、背景画像を設定します。
テキストレイヤーも作成します。
テキストレイヤーとシェイプのレイヤーを同時に選択した状態で
メニューからLayer
→ Combine
→ Intersect
を選択すれば Combined Shape
というレイヤーになり背景がテキストの形にくり抜かれたようになります。
この方法だと、テキストレイヤーを背景の上の方や下の方に配置しても背景の中央にテキストがあるかのように
背景の中央部分が切り抜かれるようです。
切り抜きの位置を柔軟に変えたければ、次に説明するやり方のほうがよいと思います。
③-2 Combined Shapeレイヤーの上に画像を重ねてマスクする
今までのやり方と違い、テキストをマスク用のシェイプにして、その上に画像を重ねてマスクします。
例えば画像を丸くくり抜くような普通のマスク方法と似たような感じです。
テキスト部分と画像が結合していないので、マスク位置を柔軟に変更することが出来ます。
まずはテキストの形をした透明のシェイプレイヤーを作ります。
Rectangleのシェイプとテキストのレイヤーを作ります。
テキストの色はなんでもいいので、目立つ色にしてます。
Rectangleのシェイプを非表示にします。
テキストレイヤーとシェイプのレイヤーを同時に選択した状態で
メニューからLayer
→ Combine
→ Union
を選択します。
これでMaskもできるテキストの形のレイヤーになりました。
このレイヤーの上に画像を重ねて、テキストのレイヤーの右クリックメニューからMask
を選択すると
テキストの形に画像がマスクされます。
画像を動かすとマスクされる位置も変えられます。
以上です。