20200115のAndroidに関する記事は10件です。

React NativeでError: Activity class {APP_NAME/APP_NAME.MainAtivity} does not exist となる

react-nativeでandroidのアプリを作っている際に、普段は通る

react-native run-android

が通らなくなったのでその対処法メモです.

現象

普段から実機のandroid端末を使ってテストしているのですが、
実機上でビルドされたアプリをアンインストールした後にリビルドしたところ、
以下のエラーを吐くようになりました.

Error: Activity class {APP_NAME/APP_NAME.MainAtivity} does not exist.

解決法

あくまで自分の場合はですが、以下のコマンドで完全に実機上からアプリをアンインストールすることで解決しました.

adb uninstall com.APP_NAME

想定原因

今回、アプリのアンインストールがトリガーになったのですが、androidのシステム上でアンインストールを行っても、どうやら何かしらのデータは実機上に残るっぽいです(少なくとも今回の端末ではそのようです).

詳細は追っていないので正しいかどうかはわかりませんが、該当コマンドでは必要な部分のみのリビルドを行うために、中途半端に削除されたデータのせいで当エラーが発生したのではないかと思っています.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]Room を忘れないためのサンプル

セットアップ

次の依存関係を記述し Room をセットアップする。

build.gradle(app)
dependencies {
      def room_version = "2.2.2"
      implementation "androidx.room:room-runtime:$room_version"
      implementation "androidx.room:room-ktx:$room_version"
      kapt "androidx.room:room-compiler:$room_version"
}
KTX や Coroutines を利用する場合には "androidx.room:room-ktx"を入れる。
Kotlin を利用するときは annotationProcessor ではなく kapt を使うようにする。

Room で作成するクラス

クラス名  役割
User データに含まれる情報を定義するクラス
UserDao 永続化したデータをオブジェクトに変換するクラス
UserRepository Daoを利用して情報を取得するクラス
AppDatabase RoomDatabaseを継承したクラス
RoomDatabase データベースを作成する際に継承する抽象クラス

User

  • Entity に テーブル名を記載する
  • @PrimaryKey(autoGenerate = true) でプライマリキーを指定する
  • @ColumnInfo(name = "" ) でカラム名を指定する
  • @Ignore でフィールドを無視する
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

UserDao

  • @Insert で指定したデータを挿入
  • @Delete で指定したデータを削除
  • @Update で指定したデータを更新
  • 上記でできないことは @Query で記述する
@Dao
interface UserDao {
    @Query("select * from users")
    fun getAll(): List<User>

    @Query("select * from users where id = :id")
    fun getUser(id: Int): User

    @Insert
    fun insert(user : User)

    @Update
    fun update(user : User)

    @Delete
    fun delete(user : User)

    @Query("delete from users")
    fun deleteAll()
}

UserRepository

  • Repository は Room の実装を隠蔽するクラス
  • ここで Entity と関連付けを切り分けたいならば変換する処理を入れる
class UserRepository(private val dao : UserDao) {
    fun getAll() : List<User> {
        return dao.getAll()
    }

    fun getUser(id : Int ) : User {
        return dao.getUser(id)
    }

    fun insert(user : User) {
        return dao.insert(user)
    }

    fun delete(user: User) {
        dao.delete(user)
    }

    fun deleteAll() {
        dao.deleteAll()
    }
}

RoomDatabase・AppDatabase

  • RoomDatabase の抽象クラスである、 AppDatabase を作成する
  • AppDabase から DAO を取得して、 データの書き込み、読み込みを実施する。
  • RoomDatabase の抽象クラスを作成するときには、 @Database アノテーションをつける
  • @Database アノテーションにはデータベースで扱うエンティティ、データベースのバージョンを記述する
@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

使い方

上記のクラスを利用すると、次のような感じになる。

val database = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "database-name").fallbackToDestructiveMigration().build()
val repository = UserRepository(database.userDao())
val newUser = User(0, date.time.toString(), date.time.toString())
repository.insert(newUser)
repository.deleteAll()

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android NDK r21 C++ コンパイルオプションのメモ

背景

  • Gradle 経由で CMake で C++ ビルドしている.

arm64-v8a ターゲット + デフォルトのビルドタイプ(debug?) + libc++_shared で .so ビルドでは以下の構成になりました.

/home/syoyo/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=aarch64-none-linux-android28 --gcc-toolchain=/home/syoyo/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/syoyo/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot  -Dnative_submodule_EXPORTS  -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -fno-addrsig -Wa,--noexecstack -Wformat -Werror=format-security   -fPIC -MD -MT CMakeFiles/native_submodule.dir/home/syoyo/work/flutter_native_vulkan_experiment/ios/Classes/native_submodule.cpp.o -MF CMakeFiles/native_submodule.dir/home/syoyo/work/flutter_native_vulkan_experiment/ios/Classes/native_submodule.cpp.o.d -o CMakeFiles/native_submodule.dir/home/syoyo/work/flutter_native_vulkan_experiment/ios/Classes/native_submodule.cpp.o -c /home/syoyo/work/flutter_native_vulkan_experiment/ios/Classes/native_submodule.cpp

参考情報

https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html

オプション

気になるオプションをリストアップしてみます.

-fdata-sections, -ffunction-sections

コンパイル単位(.o)ごとに data section, function sections を作るっぽい

Query on -ffunction-section & -fdata-sections options of gcc
https://stackoverflow.com/questions/4274804/query-on-ffunction-section-fdata-sections-options-of-gcc

https://boringssl.googlesource.com/boringssl/+/2987/third_party/android-cmake/README.md

ANDROID_FUNCTION_LEVEL_LINKING cmake option で制御できる.

-fnunwind-tables

stack trace 用っぽい

https://stackoverflow.com/questions/53102185/what-exactly-happens-when-compiling-with-funwind-tables

-fstack-protector-strong

gcc 4.9 から. スタックのチェックをより強く行う?

https://outflux.net/blog/archives/2014/01/27/fstack-protector-strong/

-no-canonical-prefixes

https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html

ヘッダのファイル探索パス用など.
シンボリックリンクを展開しない, ファイルパスを絶対パスにする.

-fno-addrsig

clang 限定.

https://releases.llvm.org/7.0.0/tools/clang/docs/ReleaseNotes.html

By default, Clang emits an address-significance table into every ELF object file when using the integrated assemble

リンカ周りで安全に処理する仕組みっぽい.

https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36912.pdf

その他一般

android.toolchain.cmake にいくつか記述があります.

https://android.googlesource.com/platform/ndk/+/master/build/cmake/android.toolchain.cmake

以前は ANDROID_DISABLE_RELRO とかありましたが, r20 or r21 からなくなっていますね.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者に優しいAndroidアプリの公開の手順、初めてのリリースに大苦戦⁈

初めてのAndroidアプリ公開

著作権の壁を乗り越え(前回のAndroidに関する記事参照)、やっとのことでAndroidアプリの制作が終わりました。
さっそくアプリを公開する準備を進めることに...
しかし初めて公開したので、様々な困難が待ち受けていた:fearful:

公開の手順と初心者が引っかかりやすい部分、また、どうやって解決したかを紹介していきます。私が公開する際に参考にした分かりやすい記事も紹介しています。
しっかり準備し、手順をしっかりわかっていれば問題なくできるはずです:thumbsup_tone2:
これからアプリの公開を考えてる方、特に初めての方ぜひ参考にしてください。

AndroidアプリをGoole Play Storeに公開する手順

アプリ公開には以下の3つのステップが必要です。

  1. Googleデベロッパー登録
  2. aabファイル(Android App Bundle)の作成
  3. Google Play Consoleへログイン

しかし、このステップに入る前にアプリをリリース用に設定しておくとよいでしょう。

リリース用アプリの設定

アプリ公開ステップに入る前にリリース用アプリに設定します。

Androidデベロッパー(リリースの準備 | Android デベロッパー | Android Developers)を読み進めてやっていくのがベストですが...
初心者の私にはたくさん色々なことが書いてあり、結局何をすればいいのかわからない状態でした:cold_sweat:
ですので、私がリリース前にしたことをここでは紹介します。

  • ロギングとデバッグをオフ
    image.png
    Logメソッドというのは、開発者が調査目的に記録するためのメソッドなので、ユーザー向けアプリでは必要がないので、削除します。

  • アプリのバージョン情報を設定する
    コメント 2020-01-15 144318.png

1.Googleデベロッパー登録

ここでやっと公開するためのステップ1.Googleへのデベロッパー登録です。
デベロッパー登録自体は難しくないです

私は以下の記事を参考に登録しました:point_down_tone2:
Android開発者登録方法 2019年最新版 かんたんな方法

デベロッパー登録に必要なもの
  • Googleのアカウント(ない方は作る必要があります。 普段使っているアカウントでも大丈夫ですが、私は、開発用の新しいアカウントを作成しました。)
  • クレジットカード(登録料として25ドル支払うため)

2.aabファイル(Android App Bundle)の作成

登録が完了の後は、aabファイルの作成です。

Qiitaの記事に作成手順が書かれていたので、参考にしてくだい:point_down_tone2:
Google Play Storeにアプリを公開する

ここのaabファイルを作るところで1番苦労しました。

どこの部分で引っかかったのか以下にまとめました↓

  • Android App Bundleではなく、APKを選んでしまった

コメント 2020-01-14 145008.png

APKの方でもファイルは作れますが、Goole Play Consoleでリリースする際に以下の警告が出ます。
コメント 2020-01-14 165132.png

Android App Bundleの方を選ぶようにしましょう。

  • Key store pathって何? image.png

進めていくとこのような画面が出てきます。私はここでなにを設定しているかよくわからなかったので、説明をつけておきます。
image.png

Alias=アップロード鍵:Google Play アプリ署名用にアップロードする前に、App Bundle または APK への署名に使用する鍵です。

  • キーストアに関しての警告が出てくる

必要事項を記入し、キーを作成しました。OKを押し、完了させると、以下のような警告が出てきました。
コメント 2020-01-10 161102.png

警告を消してそのままつくることができましたが、解決方法が知りたい方は以下の記事を参考にしてください:point_down_tone2:
https://qiita.com/koichi-ozaki/items/c515ba6711811aac44a2

  • Buildができない

ついにFinishボタンを押せばaabファイル完成のところをまできたのですが…最後の最後にBuildができない...
コメント 2020-01-14 144809.png

原因はGitHubで共同作業していたため、SDKのpathが他人のものになっていました。Android Studioの右上の像のマークを押して、解決しました。
image.png

1人でアプリを作成していれば、問題ないと思いますが、GitHubで共同作業してる方は注意が必要です。

3. Google Play Consoleへログイン

aabファイルが完成した方...おめでとうございます:clap_tone2:
あと1歩です。最後のステップはGoogle Play Consoleへログインです。
ここで、ストアに公開します。

先ほど、aabファイルを作った際に参考にした記事に続きが載っています:point_down_tone2:
Google Play Storeにアプリを公開する

アプリ公開にに必要なもの

  • 先程作成したaabファイル
  • スクリーンショット(4枚~8枚)
  • フィーチャーグラフィック(横1024×縦500の画像)

コメント 2020-01-14 160518.png

フィーチャーグラフィック(ユーザーがアプリのストアの掲載情報を開くと最初に目に留まるもの)という横長の画像が必要でした。事前に用意しておくと、スムーズに公開ができると思います。

ここで最後の苦労が…
私の場合最初にAPKファイルで作ってしまったので、アップロードする際に警告が出てしまいました。(aabファイルで作れば問題ないです。)
コメント 2020-01-14 165132.png
ですので、aabファイルで作り直し、アップロードすると新たな問題が…
コメント 2020-01-14 172137.png
APKでバージョン1を作ってしまっているので、バージョンを変えなければいけませんでした。
image.png

build.gradle(Moudule:app)を選択し、
image.png
VersionCodeを上げることで解決しました。

Google Play Consoleに基本情報の登録が終わると、公開完了です:bangbang:
後は、アプリの公開を待つだけです。わくわくしますね:relaxed:

記念すべき初アプリ公開されました!!

私は、アプリの公開に3日ほどかかりました…
アプリ公開までいくと一人前になった気がします:laughing:
色々改善点はあるのですが...アップデートもしていけたらいいなと思います。
指1本:point_up_tone2:キーボード打ちから半年で制作、公開まで行けました。

公開したアプリ:point_down_tone2:

:star:アプリ名:Time is money:star:
無駄な時間をお金に換算するアプリです。
image.png

今日1日の自分の無駄な行動を振り返るために作りました。
私自身とても時間を無駄に過ごしていることが多かったので...:sweat_drops:
お金に換算することでより現実的になり、時間に対しての考え方を改めることができるという思いから、お金に換算というアイディアが浮かびました:money_mouth:

時間をもっと有効に使えるといいですね!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android] DBデータ取得・追加(MySQL)

前回

前回の記事https://qiita.com/QiitaD/items/d605b07e849e3bec0722
で発生していたエラーだが、警告であるらしい。DBとの接続に成功していても出るようだ。

接続の解決法

MySQLにAndroidからアクセスする権限を追加することでうまくいった。

//Androidからアクセスするユーザ名とパスワード、AndroidのIPアドレスを指定。
create user 'username'@'localhost' identified by 'password';

//アクセス権限を追加
grant all privileges on *.* to 'username'@'localhost' with grant option;

flush privileges;

以下の記事が参考になる。
・ユーザ権限の追加
https://code.i-harness.com/ja-jp/q/17cd93
https://proengineer.internous.co.jp/content/columnfeature/6638

注意点

・Androidからアクセスするとき、ユーザ名をrootにしているとアクセスできない
・MySQLとAndroidは異なるアドレスなので、ループバックアドレスだとうまくいかない。(よくあるっぽい)

これらに気づかずかなり時間を取られたので、気を付けて頂きたい。

感想

解決は思ったより簡単だったが、かなり時間を取られた。記事を読まれた方々は気を付けて頂きたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]現在日時を取得する

Android端末の現在日時を取得するコード

 //現在日時をyyyy/MM/dd HH:mm:ss形式で取得する

public static String getDate(){

    //取得する日時のフォーマットを指定
    final DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    //時刻をミリ秒で取得
    final Date date = new Date(System.currentTimeMillis());

    //日時を指定したフォーマットで取得
    return df.format(date);
}

感想

めちゃくちゃ簡単なので、ぜひ試していただきたい。

参考URL

・Androidでの日時取得
https://qiita.com/zuccyi/items/d9c185588a5628837137

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AndroidでBLEデバイスとの通信ログを見る。

Jan 14 2020 更新


未知のBLE端末を、自前のオリジナルアプリから使う。っていうことをしたかったので、
BLEの仕様をハックするためにやったことの備忘録。

簡単にいうと、Androidでアプリの通信ログを取って、頑張って、どういう通信しているかを分析する。という感じになる。

準備

AndroidとMacを用意。

Androidの設定

Androidの開発者向けオプションを開く。
開発者向けオプションの出し方は、ググればすぐに出るので、それを参照。

例えば↓
[Android 4.2以降] 開発者向けオプションを表示させる方法@colorrabbit

開発者向けオプションから、スヌープログを有効化、また、USBデバッグを有効化する。

スヌープログを有効化した瞬間から、ログが吐き出される。
一度オフって、もう一度オンにすると、新しいログが吐き出されているっぽい。

Macの設定

まず、Terminalからadbコマンドを使えるようにする。
この記事を見て入れた。
adbをMacのターミナルで使えるようにする@furusin_oriver

※homebrewとかでも入れられるっぽい。

ちなみにADBは、PCに接続されたAndroidにTerminalからアクセスしちゃうぅ〜みたいなコマンド。

詳しい説明は、こことかにありそう。
忙しい人用に、冒頭だけ引用↓

Android Debug Bridge は adb と省略されます。 これは Android SDK の platform-tools に含まれるツールです。
このツールを用いると、現在利用可能なデバイス・エミュレータの列挙、シェルコマンドの発行、ファイルの転送などが行えます。
Eclipse から Android アプリをビルドしたときも、裏側で adb を用いてエミュレータまたは実際のデバイスに接続します。

次にWiresharkというログの解析アプリを入れる。

$ brew cask install wireshark

とすれば、Wireshark.appが自動でアプリケーションディレクトリに入っている。

ログを解析する

まず、AndroidをMacに繋ぐ。

$ adb devices

にデバイスが出れば接続できてることが確認できる。
接続できたら、早速ログファイルを抽出する。

$ adb bugreport bugreport

でzipをダウンロードする。

bugreport.zipというフォルダが落ちてくるので解凍する。

その中のbugreport-bluexxxx-XXXX.000000.000-2020-01-01-00-00-00.txt的な奴を探す。

これにログがあるけど、今回はいらない余計なことが書いてあるし読みにくいので、クレンジングする必要がある。

Googleがいい感じのスクリプトを配布しているので、それを使う。

btsnooz.py

btsnooz.pyをログの.txtと同じディレクトリに作って、Python2で実行する。

自分は、何も考えずPythonで実行したら、環境がPython3だったので、エラー出て5秒ほどハマった。

実行コマンドはこんな感じでよしなに。

python btsnooz.py bugreport-blueline-QP1A.191005.007-2020-01-14-21-59-11.txt > BTSNOOP.log

BTSNOOP.logの部分は任意で改名して良い。

ようやく。

Wiresharkを起動し、先ほどのBTSNOOP.logを開くと記述されているログを見ることができる。

一旦ここまで。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AndroidでBLEデバイスとの通信ログを見る(1)

Jan 14 2020 更新


未知のBLE端末を、自前のオリジナルアプリから使う。っていうことをしたかったので、
BLEの仕様をハックするためにやったことの備忘録。

簡単にいうと、Androidでアプリの通信ログを取って、頑張って、どういう通信しているかを分析する。という感じになる。

準備

AndroidとMacを用意。

Androidの設定

Androidの開発者向けオプションを開く。
開発者向けオプションの出し方は、ググればすぐに出るので、それを参照。

例えば↓
[Android 4.2以降] 開発者向けオプションを表示させる方法@colorrabbit

開発者向けオプションから、スヌープログを有効化、また、USBデバッグを有効化する。

スヌープログを有効化した瞬間から、ログが吐き出される。
一度オフって、もう一度オンにすると、新しいログが吐き出されているっぽい。

Macの設定

まず、Terminalからadbコマンドを使えるようにする。
この記事を見て入れた。
adbをMacのターミナルで使えるようにする@furusin_oriver

※homebrewとかでも入れられるっぽい。

ちなみにADBは、PCに接続されたAndroidにTerminalからアクセスしちゃうぅ〜みたいなコマンド。

詳しい説明は、こことかにありそう。
忙しい人用に、冒頭だけ引用↓

Android Debug Bridge は adb と省略されます。 これは Android SDK の platform-tools に含まれるツールです。
このツールを用いると、現在利用可能なデバイス・エミュレータの列挙、シェルコマンドの発行、ファイルの転送などが行えます。
Eclipse から Android アプリをビルドしたときも、裏側で adb を用いてエミュレータまたは実際のデバイスに接続します。

次にWiresharkというログの解析アプリを入れる。

$ brew cask install wireshark

とすれば、Wireshark.appが自動でアプリケーションディレクトリに入っている。

ログを解析する

まず、AndroidをMacに繋ぐ。

$ adb devices

にデバイスが出れば接続できてることが確認できる。
接続できたら、早速ログファイルを抽出する。

$ adb bugreport report

でzipをダウンロードする。

report.zipというフォルダが落ちてくるので解凍する。
reportの部分はフォルダの名前指定なので、なんでも良い。

その中のbugreport-bluexxxx-XXXX.000000.000-2020-01-01-00-00-00.txt的な奴を探す。

これにログがあるけど、Wiresharkでは読めない形式なので、変換する必要がある。

Googleがいい感じのスクリプトを配布しているので、それを使う。

btsnooz.py

btsnooz.pyをログの.txtと同じディレクトリに作って、Python2で実行する。

自分は、何も考えずPythonで実行したら、環境がPython3だったので、エラー出て5秒ほどハマった。

実行コマンドはこんな感じでよしなに。

$ python btsnooz.py bugreport-bluexxxx-XXXX.000000.000-2020-01-01-00-00-00.txt > BTSNOOP.log

BTSNOOP.logの部分は任意で改名して良い。

ようやく。

Wiresharkを起動し、先ほどのBTSNOOP.logを開くと記述されているログを見ることができる。

一旦ここまで。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kotlin, Git初心者がDroidKaigi 2020カンファレンスアプリにContributeするまで

TL;DR

Git、GitHub、Kotlinに触れたことが無くても、DroidKaigi公式アプリにコントリビュートできる!
ぜひ私と一緒に挑戦しましょう😎

DroidKaigiとは

DroidKaigiはエンジニアが主役のAndroidカンファレンスです。
Android技術情報の共有とコミュニケーションを目的に、2020年2月20日(木)、21日(金)の2日間開催します。

DroidKaigi 2020 (https://droidkaigi.jp/2020/)

公開されたばかりの、DroidKaigi2020カンファレンスアプリプロジェクトはこちら
(https://github.com/DroidKaigi/conference-app-2020)

経歴

新卒2年目のAndroidエンジニアです。
普段は主に、以下のような環境で業務用Androidアプリの開発を行っています。
使用言語:Java (職場全体でのKotlin採用率は0%です)
バージョン管理:Subversion

DroidKaigi2020のプロジェクトが公開されるまでにやったこと

お正月休み中の約5日間を利用して、KotlinとGitの学習を並行して行いました。
実際には時間が足りず、Android Kotlin Fundamentals Courseは学習途中です💦

Kotlin

Android Kotlin codelab courseで、今から最新のAndroidアプリ開発を始めよう
上記の記事を参考に、以下のCodelab 2コースを使用しました。

Kotlin Bootcamp Course
Kotlinの文法について学ぶことができます。
Kotlinが初めての場合は、下のAndroidコースを始める前に一通り目を通しておいた方が良いと思います。

Android Kotlin Fundamentals Course
Kotlinを使用したAndroidアプリの開発について学ぶことができます。
Gitを並行して学習する場合は、本コースの内容に合わせてGitの操作を実際に行ってみると良いと思います。

Git, GitHub

UdemyのGit入門コースを受講しました。
1~2日程度で終えることができます。
https://www.udemy.com/course/unscared_git/

実際にコントリビュートした内容

Issue

https://github.com/DroidKaigi/conference-app-2020/issues/140

PullRequest

https://github.com/DroidKaigi/conference-app-2020/pull/238

内容を見ていただければわかりますが、Viewのサイズ調整を行っただけの非常に簡単な修正です。
Kotlinでコードを書くこともありませんでした😂

私のようにGitやKotlin,Androidを始めたばかりの人でも挑戦できそうなIssueは、"easy"や"welcome contribute"として挙がっていることが多いようです。
一緒に参加して、DroidKaigiやAndroid開発を盛り上げましょう🤗

後書き

(学習過程を除く)人生初のプルリクを終えたままの勢いで、Qiitaの記事まで書いてしまいました😳
まだまだ学習中の身ですが、今後もDoirdKaigi2020公式アプリに積極的に参加していきます!
ご指摘やご感想あれば、コメントいただけると嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutterで有名なアプリのUIを再現する(Twitter編①)

この記事を見るとここまでできます

Screenshot_1579017962.png

今回は初回なので、まだまだ形になっていません。

今回の記事のポイント(要点だけ知りたい方はここだけ読んでください)

画像を円形にして表示する

Twitterでは、左上に表示されている自身の画像や、投稿に表示される投稿者の画像が円形に加工されて表示されています。
この部分の実装は下記のようにしています。

Container(
 margin: EdgeInsets.all(8.0),
 decoration: BoxDecoration(
  shape: BoxShape.circle,
  image: DecorationImage(
   fit: BoxFit.fill,
   image: NetworkImage(iconImgUrl),
  ),
 ),
)

ContainerのdecorationプロパティにBoxDecorationを指定しています。
BoxDecorationのshapeプロパティでBoxShape.circleを指定することで円形に加工されます。
また、画像もDecorationImageのfitプロパティでBoxFit.fillを指定することで円形全体に画像が表示されます。

BottomNavigationBarに4つ以上のボタンを配置する

表示環境にもよるかもしれませんが、私の環境の場合だとBottomNavigationBarに4つ以上のボタンを配置すると表示位置が均等にならず、背景色の指定が無効化されてしまいましたが、最終的に下記のようにすることで解決しました。

BottomNavigationBar(
  type: BottomNavigationBarType.fixed,
  backgroundColor: Color.fromRGBO(30, 40, 54, 1.0),
  showSelectedLabels: false,
  showUnselectedLabels: false,
  items: <BottomNavigationBarItem>[
    _menuButton(Icons.home),
    _menuButton(Icons.search),
    _menuButton(Icons.notifications_none),
    _menuButton(Icons.mail_outline),
  ],
)

上記のようにtypeプロパティにBottomNavigationBarType.fixedを指定する事で解決しました。
typeはデフォルトではBottomNavigationBarType.shiftingが指定されており、固定化されていないようでした。

ここから本題です

このシリーズのゴール

UI周りの実装方法を学習するには有名なアプリを真似て作ればゴールも明確だし、完成した時の満足感も高いだろう、という事で今回はFlutterでTwitterアプリを再現しようと思います。

あくまでUI周りの実装を学習する過程を纏めた記事なので、Twitterクライアントを作るとか、機能を作りこむといったところまではせず、見た目と動作がそっくりになるところまで進めようと思います。

今のところ、5,6回くらいに分けて投稿する予定ですが、実装を進めながらの投稿になりますので、ハマったポイントがあった場合は番外記事も投稿するかもしれません。

続きが気になる方はフォローをお願いします。

今回のゴール

今回は初回なので、上下のメニュー部分を実装します。

コードと解説

アプリのメインは下記のようにしました。

main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Twitter',
      home: MainPage(),
    );
  }
}

ルートページとしてMainPageをインスタンス化していますので、次はMainPageのコードです。

少し長いのでメソッドごとに4つに分けて解説します。
実際のコードはMainPageクラス内に全て実装しています。

main.dart
class MainPage extends StatelessWidget {
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _twitterAppBar(
          'https://d1f5hsy4d47upe.cloudfront.net/79/791ba2a1c3245ae92712c393fe2b6408_w.jpg'),
      body: Container(
        color: Color.fromRGBO(30, 40, 54, 1.0),
      ),
      bottomNavigationBar: _twitterBottomBar(),
    );
  }
}

Twitterアプリのメイン画面は大きく分けると3つのセクションで構成されています。

  • アプリケーションバー:画面上部のアカウントアイコン、Twitterのアイコン、星アイコンが表示されている部分
  • 本文:画面中央のTwitterの投稿が表示される部分
  • メニュー:画面下部の各画面への遷移するためのメニュー部分

上記コードのbuildメソッドではScaffoldを返しています。
Scaffoldは、マテリアルデザイン用のウィジェットで、マテリアルデザインの各要素がプロパティとして定義されています。
その中で今回は、

  • appBarプロパティ:アプリケーションバーにあたる部分です。_twitterAppBarメソッドで中身を実装しています。
  • bodyプロパティ:本文にあたる部分です。今回は背景色を付けているだけですので、説明は割愛します。
  • bottomNavigationBarプロパティ:メニューにあたる部分です。_twitterBottomBarメソッドで中身を実装しています。

続いて、_twitterAppBarメソッドのコードです。

main.dart
  Widget _twitterAppBar(String iconImgUrl) {
    return AppBar(
      backgroundColor: Color.fromRGBO(30, 40, 54, 1.0),
      leading: Container(
        margin: EdgeInsets.all(8.0),
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          image: DecorationImage(
            fit: BoxFit.fill,
            image: NetworkImage(iconImgUrl),
          ),
        ),
      ),
      centerTitle: true,
      title: Text('Trial'),
      actions: <Widget>[
        Container(
          margin: EdgeInsets.all(8.0),
          child: Icon(
            Icons.star_border,
            color: Color.fromRGBO(76, 158, 235, 1.0),
            size: 45,
          ),
        ),
      ],
    );
  }

このメソッドではAppBarを返しています。
ポイントは下記の3点です。

  • leadingプロパティ

アプリケーションバーの左側に表示されるウィジェットを指定する部分です。
Twitterでは、ログインユーザーのアイコン画像が円状に表示されているので、BoxDecorationを使って画像を円状に変形させています。

  • titleプロパティ

アプリケーションバーの中央に表示されるウィジェットを指定する部分です。
Twitterでは、Twitterのアイコンが表示されますが勝手に拝借するわけにもいかないので、この部分はテキストを表示してあります。
centerTitleプロパティをtrueにしておかないと、Androidでは左寄せで表示されます。

  • actionsプロパティ

アプリケーションバーの右側に表示されるウィジェットを指定する部分です。
Twitterでは、表示順ルール変更用の星マークが表示されているので、代替となるアイコンを表示しています。

次は、_twitterBottomBarメソッドです。

main.dart
  Widget _twitterBottomBar() {
    return Container(
      margin: EdgeInsets.only(top: 0.1),
      child: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        backgroundColor: Color.fromRGBO(30, 40, 54, 1.0),
        showSelectedLabels: false,
        showUnselectedLabels: false,
        items: <BottomNavigationBarItem>[
          _menuButton(Icons.home),
          _menuButton(Icons.search),
          _menuButton(Icons.notifications_none),
          _menuButton(Icons.mail_outline),
        ],
      ),
    );
  }

このメソッドでは、BottomNavigationBarでメニューを実装しています。
(Containerで囲われていますが、これはbody部分との境界をつけるためのマージンを指定するためです)

ポイントは、typeプロパティに指定したBottomNavigationBarType.fixedの部分で、これを指定することでボタンの位置が固定化されます。
また、showSelectedLabelsプロパティとshowUnselectedLabelsプロパティでボタンのラベルを非表示にしています。

ボタンの実装は_menuButtonメソッドに実装しました。

main.dart
  BottomNavigationBarItem _menuButton(IconData icon) {
    return BottomNavigationBarItem(
      icon: Icon(
        icon,
        color: Color.fromRGBO(139, 152, 164, 1.0),
      ),
      activeIcon: Icon(
        icon,
        color: Color.fromRGBO(76, 158, 235, 1.0),
      ),
      title: Text(''),
    );
  }

非選択状態のアイコンをiconプロパティに、選択状態にアイコンをactiveIconプロパティに指定したBottomNavigationBarItemを返しています。

次回

自身とフォロワーの投稿が表示される本文を実装予定です。

あとがき

文字色や背景色を各箇所で指定していますが、themeを使ってまとめた方がよさそうですね。
ただ、themeで各プロパティにColor.fromRGBOを使うとエラーになってしまう部分が解決できず・・・。
このあたりも解決次第投稿予定です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む