- 投稿日:2020-03-05T23:35:57+09:00
Android x86 9.0-r1 をHP Pavilion 11-k128TU x360にインストールしてみた
最初、Chromium OSをインストールしようとしたんだけど、CloudReadyはUSBメモリから起動はするけどインストールできないし、ChromiumOSのイメージをUSBメモリに入れても起動しないし、もう、ChromiumOSあきらめた!
ということで、Android x86を入れてみた。
ちなみに、HP Pavilion 11-k128TU x360はノートPCの状態から、キーボードを360度回転させることでタブレットとしても使える2015年発売のPCです。入れ方とかはググれば出てくるので、インストールしてみてどうなのかを書き残しておく。
書いててスクショ取ろうとしたら、許可されてなくてスクショが撮れなかったのでスマホのカメラで撮影することになるとは・・・
タブレットのようにつかえるのか?
このPC、冒頭にも触れましたが、タブレットとしても使えるPCです。
ある程度、キーボードを回転させると、キーボードが無効になって、タブレットモードになります。
買った時のようにWindows10が入っていれば。
Android x86を入れると、タブレットモードにしても、キーボードが有効なままになります。
おそらく、出荷時のWindows10では専用ドライバが入っていて対応していたのではないかと思います。
そんな状況なので、タブレットのように使うのはあまり向いていません。
常に物理キーボードが接続されている状態になるので、折りたたんでも、仮想キーボードが出ないので、設定で物理キーボードが接続されていても仮想キーボードが出るようにしないといけません。
キーボードを回転させて、自立させることもでき、その場合に入力が困るので、使い方次第ではこの設定は必須となります。
アプリは動くのか?
ゲームはMinecraftは動きます。
他にいくつか入れてみましたが、ほぼ起動せずに落ちます。ランチャーはNova Launcharは問題なく動きます。
Win X Launcher、Win 10 Launcherをれてみましたが、なんか動作が怪しいところがありました。
この辺入れるとPCっぽくなって面白いんですけどね^^;お絵かき関係でメディパンペイント、アイビスペイントXを入れてみました。
メディパンペイントは起動時に落ちましたが、アイビスペイントXは起動しました。お絵かきもできました。音楽関係はMusic StudioとKORG Kaossilatorを入れてみました。
Music Studioは起動時に落ちました。
KORG Kaossilatorはちゃんと動きました。その他Amazon Prime Video、Amazon Kindleなどを入れましたが、動くもの動かないものがありました。
という感じで、動かないアプリが割とあります。
とりあえず、Youtube、Twitterは動くので、仕事しながら動画見たりTwitter見ることはできます。
あ、Twitterはアプリより、PWA版を使ったほうが使いやすいです。他のPCにAndroid x86を入れた場合にどうなるかわかりませんが・・・
PWAを試してみた
なぜかインストールしてもアイコンが出なかったりしました。
twitterは出るようになったけど、自作PWAのLT Timerはなぜかアイコンが表示される。
ちゃんとしたAndroid端末やiPadでは動くので、PWAの開発や動作確認ではあまり使わないほうが良いのかも。開発環境として使えるのか
AIDEがインストール可能で動きます。
課金すればUIもデザイナで編集可能です。
ただ、Android Studioとは違った操作性なので慣れがかなり必要そう。
とりあえず、開発は可能・・・と思ったんですがなにか謎のエラーが出て、動かない・・・
エラーも文字化けしててなんだかわからない・・・
これ、AIDEの設定ファイルか何かなのかな?ちなみに、AIDEはJavaコンソールアプリやC/C++のコンソールアプリ、PhoneGapなどの開発も可能です。
Javaコンソールは実行までできたので、Javaの勉強は出来そうです。
PhoneGapやHTML,CSS,JavaScriptは追加でインストールが必要となります。終わりに
Android x86はYoutubeを見たり、twitterを見たりする用途には使用できます。
ゲームはPCによる可能性がありますが、期待はしないほうがいいです。
開発は、エラーが何とかなればAndroidアプリの開発ができるんじゃないかと思います。
ただ、CPUがarmではないので、NDK周りがどうなのか気になります。
また、動作確認で利用することにも向いていないと思います。Android x86でAIDEでなんとかAndroidアプリをビルドしてみたい!
- 投稿日:2020-03-05T19:24:44+09:00
【Android開発】MotionLayoutを触ってみる
MotionLayoutとは
Viewのアニメーションを管理できるViewGroupです。
ConstraintLayoutのサブクラスで、ConstraintLayoutを利用しているレイアウトなら、まるっとMotionLayoutに置き換えることができます。
※MotionLayoutはAPIレベル14との下位互換性があります。
導入
gradleに依存関係を追加します。
MotionLayoutはConstraintLayoutのライブラリの2.0.0から導入
されたものですので、ConstraintLayoutのバージョンを上げる必要があります。
※2020/3/5の時点での最新バージョンは2.0.0-beta4でした。build.gradle(app)// AndroidXを導入している場合はこちら implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4' // それ以外はこちら implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta4'MotionLayoutで利用するアニメーションファイル※1を入れるためのXMLフォルダ※2を作成します。
※1 厳密にはアニメーション開始時と終了時のViewの状態を記述したファイルです。
※2 AndroidStudioのメニューから、File > New > Folder > XML Resource Folderで作成。触ってみる
凝ったレイアウトを作る時間がないので、デフォルトの
activity_main.xml
にMotionLayoutを適応してみます。activity_main.xml<!-- ConstraintLayoutからMotionLayoutに置き換えました。 --> <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <!-- アニメーションさせたいViewにidをつけます。 --> <TextView android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.motion.widget.MotionLayout>次に
res/xml/
にアニメーションファイルを作成します。res/xml/motion_scene_main.xml<!-- MotionScene※3タグで大枠を作ります。xmlns:motion="~"を忘れずに。 --> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto" > <!-- 開始、終了アニメーションの指定と実行時間を記述します。 --> <Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@+id/start" motion:duration="1000" > <!-- トリガーとなるアクションとViewのidを紐付けます。 --> <OnClick motion:targetId="@id/main_text_view"/> </Transition> <!-- アニメーション開始時のViewの状態を記述します。 --> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <!-- アニメーション終了時のViewの状態を記述します。 --> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintStart_toStartOf="parent" /> </ConstraintSet> </MotionScene>※3 xmlns:motion="~"を含めれば、MotionSceneという命名でなく、どのような名前を付けても認識してくれます。
最後に、MotionLayoutとアニメーションファイルを紐づけてやります。
activity_main.xml<androidx.constraintlayout.motion.widget.MotionLayout 〜 省略 〜 app:layoutDescription="@xml/motion_scene_main" >端末で確認するとしてみます。
テキストをタップするとしっかりアニメーションしていますね。
MotionLayoutはView自体をアニメーションさせるだけでなく、Viewのパラメータを動的に変更することもできます。
アニメーションファイルに少し手を加えてみます。res/xml/motion_scene_main.xml<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto" > <Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@+id/start" motion:duration="1000" > <OnClick motion:targetId="@id/main_text_view" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" > <!-- アニメーション開始時のテキストの色を指定します。 --> <CustomAttribute motion:attributeName="textColor" motion:customColorValue="#ff0000" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" > <!-- アニメーション終了時のテキストの色を指定します。 --> <CustomAttribute motion:attributeName="textColor" motion:customColorValue="#0000ff" /> </Constraint> </ConstraintSet> </MotionScene>
CustomAttribute
というタグは、attributeNameに指定したViewのパラメータをアニメーションに合わせて変更してくれます。
色を変える他に、アニメーションに合わせてViewを変形させたり、画像をクロスフェードさせたりできます。
CustomAttributeを複数指定することももちろん可能です。ここでは特に触れないので気になった方は調べてみてください。
追記1
上記ではアニメーションファイルにトリガーとなるViewを紐づけていましたが、実際のプロジェクトでは、コードで指定する方が一般的かと思います。
res/xml/motion_scene_main.xml<!-- ここは削除して構いません。 --> <OnClick motion:targetId="@id/main_text_view" />MainActivity.ktclass MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val motionLayout = findViewById<MotionLayout>(R.id.main_motion_layout) // トリガーとなるViewのクリック時に、指定したid※4のアニメーションを開始します。 findViewById<TextView>(R.id.main_text_view).setOnClickListener { // 現在のアニメーションの状態を判定して、開始するアニメーションを決めます。 if (motionLayout.currentState == R.id.start) { motionLayout.transitionToEnd() } else { motionLayout.transitionToStart() } } } }※4 アニメーションファイルで定義したConstraintSetのidです。
追記2
要件によってはアニメーションの進行状況を検知したい場合があると思います。
そんな時は、MotionLayout.TransitionListener
をセットすればコールバックが得られます。MainActivity.ktmotionLayout.setTransitionListener(object : MotionLayout.TransitionListener { override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) { // トリガーした時に呼ばれます。 } override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) { // アニメーション開始時に呼ばれます。 } override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) { // アニメーション中に呼ばれます。 } override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) { // アニメーション終了時に呼ばれます。 } })感想
MotionLayoutの魅力は、ほぼ既存の実装に影響せず、線形補完の滑らかなアニメーションで手軽にリッチな表現ができるところにあると思いました。また、学習コストが比較的低いのも◎。
参考
https://developer.android.com/training/constraint-layout/motionlayout
- 投稿日:2020-03-05T19:24:44+09:00
【Android】MotionLayoutを触ってみる
MotionLayoutとは
Viewのアニメーションを管理できるViewGroupです。
ConstraintLayoutのサブクラスで、ConstraintLayoutを利用しているレイアウトなら、まるっとMotionLayoutに置き換えることができます。
※MotionLayoutはAPIレベル14との下位互換性があります。
導入
gradleに依存関係を追加します。
MotionLayoutはConstraintLayoutのライブラリの2.0.0から導入
されたものですので、ConstraintLayoutのバージョンを上げる必要があります。
※2020/3/5の時点での最新バージョンは2.0.0-beta4でした。build.gradle(app)// AndroidXを導入している場合はこちら implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4' // それ以外はこちら implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta4'MotionLayoutで利用するアニメーションファイル※1を入れるためのXMLフォルダ※2を作成します。
※1 厳密にはアニメーション開始時と終了時のViewの状態を記述したファイルです。
※2 AndroidStudioのメニューから、File > New > Folder > XML Resource Folderで作成。触ってみる
凝ったレイアウトを作る時間がないので、デフォルトの
activity_main.xml
にMotionLayoutを適応してみます。activity_main.xml<!-- ConstraintLayoutからMotionLayoutに置き換えました。 --> <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <!-- アニメーションさせたいViewにidをつけます。 --> <TextView android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.motion.widget.MotionLayout>次に
res/xml/
にアニメーションファイルを作成します。res/xml/motion_scene_main.xml<!-- MotionScene※3タグで大枠を作ります。xmlns:motion="~"を忘れずに。 --> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto" > <!-- 開始、終了アニメーションの指定と実行時間を記述します。 --> <Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@+id/start" motion:duration="1000" > <!-- トリガーとなるアクションとViewのidを紐付けます。 --> <OnClick motion:targetId="@id/main_text_view"/> </Transition> <!-- アニメーション開始時のViewの状態を記述します。 --> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <!-- アニメーション終了時のViewの状態を記述します。 --> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintStart_toStartOf="parent" /> </ConstraintSet> </MotionScene>※3 xmlns:motion="~"を含めれば、MotionSceneという命名でなく、どのような名前を付けても認識してくれます。
最後に、MotionLayoutとアニメーションファイルを紐づけてやります。
activity_main.xml<androidx.constraintlayout.motion.widget.MotionLayout 〜 省略 〜 app:layoutDescription="@xml/motion_scene_main" >端末で確認するとしてみます。
テキストをタップするとしっかりアニメーションしていますね。
MotionLayoutはView自体をアニメーションさせるだけでなく、Viewのパラメータを動的に変更することもできます。
アニメーションファイルに少し手を加えてみます。res/xml/motion_scene_main.xml<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto" > <Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@+id/start" motion:duration="1000" > <OnClick motion:targetId="@id/main_text_view" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" > <!-- アニメーション開始時のテキストの色を指定します。 --> <CustomAttribute motion:attributeName="textColor" motion:customColorValue="#ff0000" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/main_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" > <!-- アニメーション終了時のテキストの色を指定します。 --> <CustomAttribute motion:attributeName="textColor" motion:customColorValue="#0000ff" /> </Constraint> </ConstraintSet> </MotionScene>
CustomAttribute
というタグは、attributeNameに指定したViewのパラメータをアニメーションに合わせて変更してくれます。
色を変える他に、アニメーションに合わせてViewを変形させたり、画像をクロスフェードさせたりできます。
CustomAttributeを複数指定することももちろん可能です。ここでは特に触れないので気になった方は調べてみてください。
追記1
上記ではアニメーションファイルにトリガーとなるViewを紐づけていましたが、実際のプロジェクトでは、コードで指定する方が一般的かと思います。
res/xml/motion_scene_main.xml<!-- ここは削除して構いません。 --> <OnClick motion:targetId="@id/main_text_view" />MainActivity.ktclass MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val motionLayout = findViewById<MotionLayout>(R.id.main_motion_layout) // トリガーとなるViewのクリック時に、指定したid※4のアニメーションを開始します。 findViewById<TextView>(R.id.main_text_view).setOnClickListener { // 現在のアニメーションの状態を判定して、開始するアニメーションを決めます。 if (motionLayout.currentState == R.id.start) { motionLayout.transitionToEnd() } else { motionLayout.transitionToStart() } } } }※4 アニメーションファイルで定義したConstraintSetのidです。
追記2
要件によってはアニメーションの進行状況を検知したい場合があると思います。
そんな時は、MotionLayout.TransitionListener
をセットすればコールバックが得られます。MainActivity.ktmotionLayout.setTransitionListener(object : MotionLayout.TransitionListener { override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) { // トリガーした時に呼ばれます。 } override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) { // アニメーション開始時に呼ばれます。 } override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) { // アニメーション中に呼ばれます。 } override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) { // アニメーション終了時に呼ばれます。 } })感想
MotionLayoutの魅力は、ほぼ既存の実装に影響せず、線形補完の滑らかなアニメーションで手軽にリッチな表現ができるところにあると思いました。また、学習コストが比較的低いのも◎。
参考
https://developer.android.com/training/constraint-layout/motionlayout
- 投稿日:2020-03-05T14:55:10+09:00
【Android】Glideを使って画像表示後Blur�をかける
どういうときに使う?
- スクロールしたらヘッダーのイメージをぼかしたい時とか。
- 表示後に動的Blurにする必要が無いのであれば必要ない。
使うもの
コード
val url = "https://hogehoge" // 初回読み込み Glide.with(imageView.context) .load(url) // Blurをかける時 Glide.with(imageView.context) .load(url) .transform( GrayscaleTransformation(), BlurTransformation(int) ) .placeholder(imageView.drawable) .into(imageView)ポイントは
placeholder
で 既に表示されている ImageView の Drawable を指定する所。
上記のことを忘れるとチカチカする。Blur の他にも表示後に何かの値を変更するみたいなときとかも同じようにやればいいと思う。
CoilとかPiccasoでも同じ事ができると思う。Seekbarとかと組み合わせて試してみて
- 投稿日:2020-03-05T13:28:08+09:00
アンドロイドアプリでスマートニュース風のタブを実装する
これだけでした。
tabs.setTabMode(TabLayout.MODE_SCROLLABLE);スマートニュース風タブメニュー pic.twitter.com/8qfpI1qFLY
— Tetsuji Okuno (@tetsujiokuno) March 5, 2020
- 投稿日:2020-03-05T11:13:03+09:00
Androidで、ktor + coroutines で通信をする。
Android で、通信処理を実装しようとすると、retrofit が1番最初に思いつきます。
ただ、選択肢は多い方がいいと思いますので、マルチプラットフォームで実装出来る ktor を紹介したいと思います。gradle
- ktor
ktor は、マルチプラットフォーム対応なので、通信ライブラリーも選択可能です。
Androidの場合、OkHttp, Android があります。
今回は、OkHttp にします。
また、Json も同様に Gson, Jackson, Kotlinx.Serialization が選択が出来ます。
今回は、Kotlinx.Serialization にします。dependencies { implementation "io.ktor:ktor-client-core:1.3.1" implementation "io.ktor:ktor-client-android:1.3.1" implementation "io.ktor:ktor-client-okhttp:1.3.1" implementation "io.ktor:ktor-client-serialization-jvm:1.3.1" }
- coroutines
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3" }
- serialization
dependencies { classpath 'com.android.tools.build:gradle:3.6.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" }apply plugin: 'kotlinx-serialization' dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0" }
OpenWeather から天気の情報を取得してみたいと思います。
ログインして、Keyは取得済みとします。ViewModel
まず、ktorを初期化する。
OkHttp と KotlinxSerializer を使用します。private val httpClient = HttpClient(OkHttp) { install(JsonFeature) { serializer = KotlinxSerializer( Json( JsonConfiguration.Stable.copy() ) ) } }上記を使って、天気情報を取得する処理を実装します。
viewModelScope.launch が、coroutines の実装になります。
これだけで、非同期で通信する処理が実装出来ます。
また、取得できたら、LiveData を使って、UI側に通知します。val openWeatherMapLiveData = MutableLiveData<OpenWeatherMapJson>() fun getOpenWeatherMapJson() { viewModelScope.launch { try { val openWeatherMapJson = httpClient.post<OpenWeatherMapJson>("https://api.openweathermap.org/data/2.5/weather?q=yokohama&appid={KEY}") { contentType(ContentType.Application.Json) } openWeatherMapLiveData.postValue(openWeatherMapJson) } catch (e:Exception) { Log.e("w", e.message) } } }
- OpenWeatherMapJson
Json部分の実装は、省略しますが、下記のような data class を用意します。
@Serializable data class OpenWeatherMapJson( val coord: Coord, val weather: List<Weather>, val base: String, val main: Main, val visibility: Int, val wind: Wind, val clouds: Clouds, val dt: Int, val sys: Sys, val timezone: Int, val id: Int, val name: String, val cod: Int )Activity
実際に取得する処理を実装します。
ViewModel を使って、非同期で通信し、レスポンスを受け取ったら、UIに表示する処理になります。class MainActivity : AppCompatActivity() { private val viewModel by viewModels<MainViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) fab.setOnClickListener { view -> viewModel.getOpenWeatherMapJson() } viewModel.openWeatherMapLiveData.observe(this, Observer { textView.text = it.name }) } }まとめ
retrofit でも同様なことが出来ます。
Android で、通信処理を実装する場合の選択肢として、ktor も使えますので是非使ってみてください。
- 投稿日:2020-03-05T06:39:03+09:00
ターゲットを狙うようなカスタムViewを作成してみた。
カスタムView(カスタムViewだとなんのことか分からなくなりそうなので、TargetViewと呼びます。) の作り方とアニメーションのやり方を学ぶための備忘録になります。
とりあえず見た目重視の勉強用で作成したため、画面回転時などは何も考えていおりません。今回作ったもの
QRコードを読み込む際に使えそうな感じですね。(といいつつも用途不明です)
TargetView
今回作成したカスタムViewはxmlに定義したFrameLayoutの中に、
- 鍵かっこを表示するView
- グレーの矩形を表示するView
- 真ん中の十字を表示するView
を重ねて表示しています。
Viewそれぞれにはxml内でマージンを設定し、特に鍵かっこViewとグレーの矩形がアニメーションした際に重ならないように調整しています。
3つの
View
はそれぞれのクラス内のonDraw
でCanvas
のメソッドを使用して横線、縦線、矩形を描画しています。鍵かっことグレーの矩形のアニメーションは縮小・拡大を交互に行うようにしています。
鍵かっこを表示するView
鍵かっこViewの作成手順
1.View
を継承したクラスを作成
2.Canvas
クラスとPaint
クラスを使用して縦線と横線を描画
3. 拡大→縮小のアニメーションを作成Viewを継承する
class TargetFrameView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { constructor(context: Context): this(context, null, 0, 0) // xmlに定義するため、このコンストラクタは必須となります。 constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): this(context, attrs, defStyleAttr, 0) // 省略... }
View
を継承する際はxmlから呼び出す場合とコードから呼び出す場合とで必要となるコンストラクタが違うため、用途に合わせて継承する必要があります。
今回はxmlから呼び出すことを想定しているため、最低限Context
とAttributeSet
のコンストラクタを追加する必要があります。鍵かっこ描画
class TargetFrameView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { // 省略... private val c: Context by lazy { context } // 線の長さ private val margin: Float = 50f override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) drawLeftTopLine(canvas, setupLinePaint()) drawLeftBottomLine(canvas, setupLinePaint()) drawRightTopLine(canvas, setupLinePaint()) drawRightBottomLine(canvas, setupLinePaint()) // 省略... } private fun drawLeftTopLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(0.0f, 0.0f, margin, 0.0f, paint) // 縦線 canvas?.drawLine(0.0f, 0.0f, 0.0f, margin, paint) } private fun drawLeftBottomLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(0.0f, height.toFloat(), margin, height.toFloat(), paint) // 縦線 canvas?.drawLine(0.0f, height.toFloat() - margin, 0.0f, height.toFloat(), paint) } private fun drawRightTopLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(width.toFloat() - margin, 0.0f, width.toFloat(), 0.0f, paint) // 縦線 canvas?.drawLine(width.toFloat(), 0.0f, width.toFloat(), margin, paint) } private fun drawRightBottomLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(width.toFloat() - margin, height.toFloat(), width.toFloat(), height.toFloat(), paint) // 縦線 canvas?.drawLine(width.toFloat(), height.toFloat() - margin, width.toFloat(), height.toFloat(), paint) } private fun setupPaint(): Paint { return Paint().apply { isAntiAlias = true // アンチエイリアスを有効にするかの設定。基本的にはtrueしておくと良いと思います。 color = ContextCompat.getColor(c, android.R.color.holo_green_dark) strokeWidth = 10f } } // 省略... }鍵かっこ描画での登場人物は、
Canvas
とPaint
です。
Canvas
とは簡単に言うとView上に図形や画像を描画する際に使われるAPIです。4つの基本的なコンポーネントを使用して、渡された情報を元に描画を行います。
Paint
とはCanvas
に線を作成したりする際などに、色や太さなどの情報を渡すために使用します。実際の線の描画については、
Canvas#drawLine
を使用しています。
Canvas#drawLine
の使い方は始点の座標と終点の座標、さらに線を描画する際の設定を渡すだけです。描画される線の長さは、50.0固定で設定しています。(marginという変数に格納しています。)
拡大→縮小アニメーション作成
class TargetFrameView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { // 省略... override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) // 省略... expandedAnimation() } private fun expandedAnimation() { val expandedScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.05f, 0.98f) val expandedScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.05f, 0.98f) ObjectAnimator.ofPropertyValuesHolder(this, expandedScaleX, expandedScaleY).apply { duration = 1100 repeatCount = ValueAnimator.INFINITE // アニメーションはループさせる repeatMode = ValueAnimator.REVERSE // 拡大後は縮小させる }.start() } }拡大→縮小アニメーションは、
ObjectAnimator
を使用して行っています。
ObjectAnimator
は渡したObjectに対してアニメーションさせるためのAPIです。
PropertyValuesHolder
というアニメーション情報を保持するAPIを使用して、複数のアニメーション情報を作成しています。
アニメーション自体はX軸方向、Y軸方向に大きくなるというアニメーションです。鍵かっこ完成
以上で鍵かっこは完成です。
コード全文
import android.animation.ObjectAnimator import android.animation.PropertyValuesHolder import android.animation.ValueAnimator import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.util.AttributeSet import android.view.View import androidx.core.content.ContextCompat class TargetFrameView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { constructor(context: Context): this(context, null, 0, 0) constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): this(context, attrs, defStyleAttr, 0) private val c: Context by lazy { context } private val margin: Float = 50f override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) drawLeftTopLine(canvas, setupLinePaint()) drawLeftBottomLine(canvas, setupLinePaint()) drawRightTopLine(canvas, setupLinePaint()) drawRightBottomLine(canvas, setupLinePaint()) expandedAnimation() } private fun drawLeftTopLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(0.0f, 0.0f, margin, 0.0f, paint) // 縦線 canvas?.drawLine(0.0f, 0.0f, 0.0f, margin, paint) } private fun drawLeftBottomLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(0.0f, height.toFloat(), margin, height.toFloat(), paint) // 縦線 canvas?.drawLine(0.0f, height.toFloat() - margin, 0.0f, height.toFloat(), paint) } private fun drawRightTopLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(width.toFloat() - margin, 0.0f, width.toFloat(), 0.0f, paint) // 縦線 canvas?.drawLine(width.toFloat(), 0.0f, width.toFloat(), margin, paint) } private fun drawRightBottomLine(canvas: Canvas?, paint: Paint) { // 横線 canvas?.drawLine(width.toFloat() - margin, height.toFloat(), width.toFloat(), height.toFloat(), paint) // 縦線 canvas?.drawLine(width.toFloat(), height.toFloat() - margin, width.toFloat(), height.toFloat(), paint) } private fun setupPaint(): Paint { return Paint().apply { isAntiAlias = true // アンチエイリアスを有効にするかの設定。基本的にはtrueしておくと良いと思います。 color = ContextCompat.getColor(c, android.R.color.holo_green_dark) strokeWidth = 10f } } private fun expandedAnimation() { val expandedScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.05f, 0.98f) val expandedScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.05f, 0.98f) ObjectAnimator.ofPropertyValuesHolder(this, expandedScaleX, expandedScaleY).apply { duration = 1100 repeatCount = ValueAnimator.INFINITE // アニメーションはループさせる repeatMode = ValueAnimator.REVERSE // 拡大後は縮小させる }.start() } }グレーの矩形を表示するView
グレー矩形作成手順
1. Viewを継承したクラスを作成
2. CanvasクラスとPaintクラスを使用して矩形を描画
3. 縮小→拡大のアニメーションを作成Viewを継承する
class TargetRectangleView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { constructor(context: Context): this(context, null, 0, 0) // xmlに定義するため、このコンストラクタは必須となります。 constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): this(context, attrs, defStyleAttr, 0) // 省略... }鍵かっこと同じなので解説は飛ばします。
グレー矩形描画
class TargetRectangleView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { // 省略... private val c: Context by lazy { context } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) drawCenterRectangle(canvas, setupRectPaint()) // 省略... } private fun drawCenterRectangle(canvas: Canvas?, paint: Paint) { val rect = Rect().apply { set(0, 0, width, height) } canvas?.drawRect(rect, paint) } private fun setupRectPaint(): Paint { return Paint().apply { isAntiAlias = true color = ContextCompat.getColor(c, R.color.rectangle_color) } } // 省略... }今回は矩形を表示するため、
Canvas#drawRect
を使用しています。
Rect
を使用して矩形の大きさをCanvas
に渡しています。
Paint
に関しては鍵かっこで説明したので省かせていただきます。縮小→拡大アニメーション作成
class TargetRectangleView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { // 省略... override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) // 省略... shrinkAnimation() } // 省略... private fun shrinkAnimation() { val shrunkScaleX = PropertyValuesHolder.ofFloat("scaleX", 0.94f, 1.03f) val shrunkScaleY = PropertyValuesHolder.ofFloat("scaleY", 0.94f, 1.03f) ObjectAnimator.ofPropertyValuesHolder(this, shrunkScaleX, shrunkScaleY).apply { duration = 1100 // 拡大→縮小アニメーションと同じ秒数 repeatCount = ValueAnimator.INFINITE //拡大→縮小アニメーションと同じ repeatMode = ValueAnimator.REVERSE //拡大→縮小アニメーションと同じ }.start() } }こちらも鍵かっことほとんど同じです。
矩形に適応するアニメーションは縮小→拡大なので、PropertyValuesHolder#ofFloat
の第二引数と第三引数の大小関係が違っています。グレー矩形完成
わかりづらいですが、縮小→拡大アニメーションがついています。
コード全文
import android.animation.ObjectAnimator import android.animation.PropertyValuesHolder import android.animation.ValueAnimator import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.Rect import android.util.AttributeSet import android.view.View import androidx.core.content.ContextCompat import com.example.targetviewsample.R class TargetRectangleView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { constructor(context: Context): this(context, null, 0, 0) constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): this(context, attrs, defStyleAttr, 0) private val c: Context by lazy { context } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) drawCenterRectangle(canvas, setupRectPaint()) shrinkAnimation() } private fun drawCenterRectangle(canvas: Canvas?, paint: Paint) { val rect = Rect().apply { set(0, 0, width, height) } canvas?.drawRect(rect, paint) } private fun setupRectPaint(): Paint { return Paint().apply { isAntiAlias = true color = ContextCompat.getColor(c, R.color.rectangle_color) } } private fun shrinkAnimation() { val shrunkScaleX = PropertyValuesHolder.ofFloat("scaleX", 0.94f, 1.03f) val shrunkScaleY = PropertyValuesHolder.ofFloat("scaleY", 0.94f, 1.03f) ObjectAnimator.ofPropertyValuesHolder(this, shrunkScaleX, shrunkScaleY).apply { duration = 1100 repeatCount = ValueAnimator.INFINITE repeatMode = ValueAnimator.REVERSE }.start() } }真ん中の十字を表示するView
真ん中の十字作成手順
1. Viewを継承したクラスを作成
2. CanvasクラスとPaintクラスを使用して線を描画十字に関しては鍵かっことかなり似ているため、全文を載せます。
基本的には、幅と高さを半分にして十字Viewの中心を出し、そこから計算を始点と終点を計算し縦線横線を描画しています。
import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.util.AttributeSet import android.view.View import androidx.core.content.ContextCompat class TargetCrossView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : View(context, attrs, defStyleAttr, defStyleRes) { constructor(context: Context): this(context, null, 0, 0) constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): this(context, attrs, defStyleAttr, 0) private val c: Context by lazy { context } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) drawCrossLine(canvas, setupCrossLinePaint()) } private fun drawCrossLine(canvas: Canvas?, paint: Paint) { val halfWidth = (width / 2).toFloat() val halfHeight= (height / 2).toFloat() val margin = 25f // 横線 canvas?.drawLine(halfWidth - margin, halfHeight, halfWidth + margin, halfHeight, paint) // 縦線 canvas?.drawLine(halfWidth, halfHeight - margin, halfWidth, halfHeight + margin, paint) } private fun setupCrossLinePaint(): Paint { return Paint().apply { isAntiAlias = true color = ContextCompat.getColor(c, android.R.color.white) strokeWidth = 5f } } }十字View完成
コード上では十字の色は白色だったのですが、完成画像を見せる際に見えないため黒くしています。
TargetView作り
レイアウトファイルに定義
最後に今まで作成してきた3つのViewをFrameLayoutを親として定義したxmlレイアウトに配置し、FrameLayoutを継承したカスタムViewGourpのクラスで全てのViewを組み合わせます。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <view class="com.example.targetviewsample.targetview.TargetFrameView" android:id="@+id/indicatorView" android:layout_margin="16dp" android:layout_width="match_parent" android:layout_height="match_parent" /> <view android:id="@+id/rectangleView" class="com.example.targetviewsample.targetview.TargetRectangleView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="24dp" /> <view class="com.example.targetviewsample.targetview.TargetCrossView" android:id="@+id/crossView" android:layout_margin="24dp" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent"/> </FrameLayout>Viewを配置していく順番ですが、 最前面から十字→矩形→鍵かっこという順番になるように配置していきます。
FrameLayoutを継承したクラスを作成
import android.content.Context import android.graphics.Point import android.util.AttributeSet import android.util.Size import android.view.ViewGroup import android.widget.FrameLayout import com.example.targetviewsample.R class TargetView( context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int ) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) { constructor(context: Context): this(context, null, 0, 0) constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): this(context, attrs, defStyleAttr, 0) private val c: Context by lazy { context } init { inflate(c, R.layout.item_target_view, this) } }作成したカスタムViewGroup内で先ほど3つのViewを定義したレイアウトファイルをinflateし、Activityなどで使用します。
TargetViewの完成
使用感
GIFが用意できずわかりづらいのですが、こんな感じとういうのが伝われば幸いです。
最後に
過去に何回かカスタムViewを作成する機会はあったのですが、アニメーションこみでは作ったことがなかったので勉強になりました。
今回作成したものはまだまだ改良の余地がありますので、さらに勉強していこうと思います。朝まで記事を書いてしまったため、誤字脱字や見づらい箇所があると思います。。。
見ていただきありがとうございます。
参考にさせていただいた記事
Custom Viewsで複数のviewを1つにまとめて部品化する
AndroidでもiPhoneに負けないようなアニメーションを実装してみよう
公式リファレンス
カスタムViewの作り方
View
ObjectAnimator
PropertyValuesHolder
Canvas
Canvas#drawLineのリファレンス
Paint
Rect
Canvas#drawRect)