- 投稿日:2020-09-16T18:05:38+09:00
5. 【Android/Kotlin】ダイアログ(Dialog)
はじめに
DreamHanksのMOONです。
前回は「リストビュー」というViewについて説明をしました。
4. 【Android/Kotlin】リストビュー(ListView)今回はリストビューのアイテムをクリックした場合、
ダイアログが表示されるし、リストのアイテムが削除することができることをしていきます。ダイアログとは
ダイアログは、ユーザーによる意思決定や追加情報の入力用に表示される小さなウィンドウです。ダイアログは全画面に表示されることはなく、通常はユーザーが処理を続ける前にアクションを起こす必要があるモデルイベントに使用されます。
種類は代表的に
AlertDialog
とDatePickerDialog
とTimePickerDialog
があります。
その中に今回はAlertDailog
を使用していきます。AlertDialog追加
前回に作成したコードを使用して修正します。
全体コードを確認したい場合は前回のリンクで確認してください。まず、Activityファイルを下記とように修正します。
ListViewActivity.ktpackage com.example.practiceapplication import android.app.AlertDialog import android.content.DialogInterface import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log.d import android.view.ContextThemeWrapper import android.widget.* import kotlinx.android.synthetic.main.activity_listview.* import javax.xml.validation.Validator class ListViewActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_listview) var userList = arrayListOf<User>( User("佐藤","sato@ggggg.com"), User("鈴木","suzuki@aaaaa.com"), User("高橋","takahasi@yyyyy.com"), User("伊藤","ito@fffff.com"), User("渡辺","watanabe@bbbbb.com"), User("山本","yamamoto@zzzzz.com"), User("中村","nakamura@ccccc.com"), User("小林","kobayasi@xxxxx.com"), User("加藤","gato@wwwww.com") ) var list_view = findViewById<ListView>(R.id.list_view) //リストビューのアイテムを変更 setListView(userList) //リストビューのアイテムのクリックイベントを設定 list_view.setOnItemClickListener { parent, view, position, id -> //クリックされたアイテム val element = parent.getItemAtPosition(position) //ダイアログを生成 val builder = AlertDialog.Builder(ContextThemeWrapper(this@ListViewActivity, R.style.Theme_AppCompat_Light_Dialog)) builder.setTitle("ユーザー名") //選択されたアイテムがユーザーオブジェクトの場合 if(element is User){ //ダイアログの内容にユーザー名を設定 builder.setMessage(element.name) } //確認ボタンを追加(確認ボタンは単純に確認機能) builder.setPositiveButton("確認", DialogInterface.OnClickListener { dialog, which -> }) //削除ボタンを追加 builder.setNegativeButton("削除", DialogInterface.OnClickListener { dialog, which -> //ユーザーリストで選択されたアイテムを削除 userList.remove(element) //リストビューのアイテムを変更 setListView(userList) }) builder.show() } } fun setListView(userList: ArrayList<User>){ //アダプターにリストを設定 val Adapter = ListAdapter(this, userList) //リストビューにアダプターを設定 list_view.adapter = Adapter } }追加分
・リストビューのアイテムクリックイベント設定
//リストビューのアイテムのクリックイベントを設定 list_view.setOnItemClickListener { parent, view, position, id -> //クリックされたアイテム val element = parent.getItemAtPosition(position) //ダイアログを生成 val builder = AlertDialog.Builder(ContextThemeWrapper(this@ListViewActivity, R.style.Theme_AppCompat_Light_Dialog)) builder.setTitle("ユーザー名") //選択されたアイテムがユーザーオブジェクトの場合 if(element is User){ //ダイアログの内容にユーザー名を設定 builder.setMessage(element.name) } //確認ボタンを追加(確認ボタンは単純に確認機能) builder.setPositiveButton("確認", DialogInterface.OnClickListener { dialog, which -> }) //削除ボタンを追加 builder.setNegativeButton("削除", DialogInterface.OnClickListener { dialog, which -> //ユーザーリストで選択されたアイテムを削除 userList.remove(element) //リストビューのアイテムを変更 setListView(userList) }) builder.show() }・リストビューにアダプターを設定
fun setListView(userList: ArrayList<User>){ //アダプターにリストを設定 val Adapter = ListAdapter(this, userList) //リストビューにアダプターを設定 list_view.adapter = Adapter }①リストビューにアイテムクリックイベントを設定します。
②イベント内にダイアログを生成します。
③ダイアログにタイトルを設定、メッセージには選択されたアイテムの名前を設定します。
④確認ボタンを設定し、確認ボタンは別の設定はしなくても大丈夫です。
⑤削除ボタンを設定し、削除ボタンにはユーザーリスト内のユーザー中に選択されたユーザーを削除します。
⑥最後にリストビューに変更されたユーザーにストのアダプターを設定します。アプリ起動
・初期画面
・佐藤さんをクリックした場合
・佐藤さんから伊藤まで削除した場合
終わりに
今回は「ダイアログ」について説明をしていきました。
次回は「スピナー」について説明をしていきます。
6. 【Android/Kotlin】スピナー(Spinner)
- 投稿日:2020-09-16T14:18:43+09:00
AABから単一APKを作成する
AABファイルは多デバイスの情報が含まれており、インストール時にいい感じの構成にしてくれるいかしたやつです。これはGooglePlayStoreで配布するときは問題ないですが、AppDistributionで配布する場合にAPKにしか対応していないので全てのデバイス情報を含んだAPKファイルを作成する必要が出てきます。
aabファイルをapkファイルに変更する
bundleToolsのダウンロード
ここ からダウンロード
bundleToolsから単一APKを作成
--mode=universal
でAPKのセットを生成するとデバイスごとではない単一APKが生成されるjava -jar bundletool-all-1.2.0.jar build-apks \ --bundle=app.aab \ --output=app.apks \ --ks=keystore \ --ks-pass=pass:'password' \ --ks-key-alias='alias' \ --key-pass=pass:'password' \ --mode=universalapksを解凍
出来上がったapksファイルはzipファイルなので回答することでapkファイルが出力される。
unzip app.apks終わり
ということをやれば、全デバイス対応したAPKが出来上がりますが、参考の注意書きに書いてあるように、含まれないモジュールが出てくる可能性があります。
注: bundletool は、マニフェストに
<dist:fusing dist:include="true"/>
の指定がある機能モジュールのみをユニバーサル APK に含めます。詳細については、機能モジュールのマニフェストをご覧ください。AABとAPKのビルドを2回流すのは面倒だからとAABからAPKを作るぐらいなら、APKのビルドを流して作るほうが無難ではあります。
参考
https://developer.android.com/studio/command-line/bundletool?hl=ja
- 投稿日:2020-09-16T05:28:07+09:00
FlutterでiOS、Android両方で動くカメラアプリを作る
Flutterを使ってマルチプラットフォームなカメラアプリを作っていく
「iOSでもAndroidでも動くアプリで、」
「スマホ内の画像にアクセスしてローカルのDBに保存可能」
「アプリ内でもカメラを起動できる」っていう要件をFlutterで満たせるかの検証をバイト先に任されたからなー
Flutter
iOS、Androidといった異なるプラットフォームのアプリを一つのコードで書くことができるGoogle製のオープンソースのクロスプラットフォームのフレームワーク
公式ドキュメントも充実・サードパーティー製のプラグインも日々増えているので、旬な技術だと思っている
公式Youtubeにはお洒落な感じデザインのやらWidgetやらの説明動画がたくさんあるので楽しい
環境構築は以下のサイトを参考にした。ありがとうございました
ホットリロードが神すぎる
早速作っていきたいのだけど、Flutterは言語はdartでWidget単位でコードを完成させてアプリを作っていく
公式ドキュメントでもFlutterではWidgetが全てだよって言ってる
In Flutter, “everything is a widget”! - Flutter documentation
特に重要なWidgetが Stateless Widget と Statefull Widget の2つで順番におさらいしていく
そのあと、画像ファイル・カメラへのアクセスを可能にするプラグインであるimage_picker と、データベースを扱うためのプラグインsqfliteの紹介に入って、最後にコードを説明して終わり
Stateless Widget
名前の通り状態を持たないWidget。
以下は公式YouTubeで取り上げられているコード例
class ItemCount extends StatelessWidget { final String name; final int count; ItemCount({this.name, this.count}); @overide Widget build(BuildContext context) { return Text('$name: $count') } }String型のnameとint型のcountを与えると、それらをTextWidgetに渡して表示させるようなWidgetである。
Statefull Widget
状態をもつWidgetで、Stateが更新されたときに内容が更新される。
先ほどのコードをStatefulにすると以下のような感じになる。
class ItemCount extends StatefulWidget { final String name; ItemCounter({this.name}); @override _ItemCounterState createState() => _ItemCounterState(); } class _ItemCounterState extends State<ItemCounter> { int count = 0; @overide Widget build(BuildContext context) { return GestureDetector( onTap: () { setState(() { count++; }); ), child: Text('${widget.name}: $count'), ); } }setStateはその中に書かれた変数が変化した時にクラスに変更を伝える役割を持っている
これでStatelessだった画面が、タップするとcountが上がっていくプログラムになった
image_picker
image_pickerははアプリ内でカメラを立ち上げたり、スマホ内のメディア(画像)にアクセスさせたりするのに使うプラグイン
A Flutter plugin for iOS and Android for picking images from the image library, and taking new pictures with the camera. - image_picker | pub.dev
フラッター開発チームが作った純正なのでいろいろ安心
Flutterでのプラグインの導入は
pubspec.yaml
にプラグインの情報を追加して$ flutter pub get
してあげるだけこのプラグインではフォトライブラリへのアクセスを許可しないといけないので、iOSでは以下の変数に関する設定を
ios/Runner/info.plist
に事前に書き込んでおく必要がある
変数名 役割 NSPhotoLibraryUsageDescription
フォトライブラリへのアクセス NSCameraUsageDescription
カメラへのアクセス NSMicrophoneUsageDescription
マイクへのアクセス 動画を取り込まないなら一番下の項目は無視できる
細かい設定の仕方は公式ドキュメントをご参照ください
公式ドキュメントをサンプルのコードに手を加え、ペッとmain.dartに貼り付けたら動くものを作った
動作環境はMacBookPro上の
android-x86 emulator(Pixel_3a_API_30_x86)
である例えばカメラの起動と写真の取得はたったこれだけ。
カメラの起動・写真の取得final pickedFile = await picker.getImage(source: ImageSource.camera);
ImageSource.gallery
ならフォルダへのアクセスができるかなり使えそう。
sqflite
SQLite(データベース)を導入・利用するためのプラグイン
Flutter plugin for SQLite, a self-contained, high-reliability, embedded, SQL database engine. - sqflite | pub.dev
導入は例に漏れず
pubspec.yaml
にプラグインの情報を追加して$ flutter pub get
してあげるだけ公式Githubとか他の記事見ながら理解した
主な関数の説明を列挙していく
まずはデータベースへのパスを取得する必要があるらしいので、
import 'package:path/path.dart'
しておいて、getDatabasesPath関数でパスを取得するそれを用いると、データベースを開く処理はこんな感じでできる
openDatabase関数String _path = join(await getDatabasesPath(), "mydb.db"); Database database = await openDatabase(_path, version: 1, onCreate: 処理);新規にデータベースを作成してアクセスする時などは、openDatabase関数実行時にonCreateを設定すれば良い
onCreate関数は、データを事前準備しておきたい時にも使える
onCreate関数String _path = join(await getDatabasesPath(), "mydb.db"); Database _database = await openDatabase(_path, version: 1, onCreate: (Database db, int version) async { await db.execute( "CREATE TABLE IF NOT EXISTS mydb (id INTEGER PRIMARY KEY, text TEXT)"); // データを事前に流し込みたい時はここにinsertを記述すれば良い });データベースの内容の書き換えなどで、transaction関数を利用できる
transaction関数await _database.transaction((txn) async { await txn.rawInsert('INSERT INTO mydb(text) VALUES("$textData")'); });レコードの取得にはrawQuery、保存にはrawInsert、更新はrawUpdate、削除はrawDeleteを使えば良い
rawQuery関数の結果はMap型のリストで返ってくるrawQuery関数List<Map> _result = await _database.rawQuery('SELECT * FROM mydb');SQLとかを発行しない処理であれば、execute関数で行える
本題
カメラとギャラリーから画像を取ってきて、DB(SQLite)に保存するアプリを作ってみた。
まずは完成したアプリをみて欲しい.
Androidエミュレーターでの動き
iPhoneエミュレーターでの動き
Android、iphoneの両方で動作するアプリがであることが確認できる(ただし、iphoneエミュレーター上ではカメラは起動しない)
sqfliteに画像を保存する場合は画像情報をbase64でエンコードしたテキストとして写真の実体を保存する
自分はこちらを参考にした(Thank you!)
Google’s Flutter Tutorial – Save Image as String in SQLite Database
画像を保存しておくためのテーブルは以下のように定義している
initDB() async { io.Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, DB_NAME); var db = await openDatabase(path, version: 1, onCreate: _onCreate); return db; } _onCreate(Database db, int version) async { await db.execute("CREATE TABLE $TABLE ($ID INTEGER, $DATA TEXT)"); }ギャラリーからピックアップしてきた画像をBase64でエンコードして保存
_pickImageFromGallery() { final picker = ImagePicker(); picker.getImage(source: ImageSource.gallery).then((imgFile) async { if (imgFile != null) { String imgString = Utility.base64String(await imgFile.readAsBytes()); Photo photo = Photo(0, imgString); dbHelper.save(photo); } }); }完成したコードはGitHubにおいておきます