20210510のAndroidに関する記事は3件です。

【Flutter】Dartから入るオブジェクト指向プログラミング入門

Flutterからプログラミングを学び始めた人が、オブジェクト指向プログラミング(OOP)の考え方をさらっと学習するために本記事を執筆します。 間違いや分かりずらい点ありましたらご指摘お願いします サンプルプロジェクトの簡単な説明 Flutterプロジェクト作成時の初期アプリであるカウントアップアプリに機能追加したものになります。 カウンターの種類を変えることができ、0,1,2,・・・と進んでいき値が12になったらまた0に戻るHourCounterと単純に0から無限にカウントアップしていくSimpleCounterを左上のスイッチで切り替えることが出来ます。 ついでに、スイッチの状態は一度アプリを切っても保持されます(shared_preferencesを使用) カウンターのクラス図は以下のようになっています。 ※きちんとしたUMLの記法で書いていません。あくまでイメージとしてとらえてください。 以下にコードを置いています ※レクチャーの綴り間違えました...作り直すの面倒くさいのでそのまま... オブジェクト指向基礎 手続き型言語のコード量の多さや、保守性の悪さを解決する、プログラミング言語のアーキテクチャの一つ。 データと操作をまとめて一つのオブジェクトとしてとらえる。 オブジェクト指向の3大要素として「カプセル化、継承、ポリモーフィズム」がある。 クラスとメンバ クラスとはオブジェクトを生成するためにデータと処理を定義したもの。 データのことをフィールドといい、処理のことをメソッドという。 フィールドとメソッドのことをメンバという。メンバと言われたら、フィールドかメソッドのこと。 クラスを実際にオブジェクトにして使うことをインスタンス化と言い、生成されたものをインスタンスと呼ぶ。 Dartではnewをつけてもつけなくても良い。 class_example.dart class Hoge { // フィールド String value; // メソッド void fuga() { value = 'piyo'; } } アクセス修飾子 メンバにはアクセス制限をつけることが出来る。Javaのようにpublic、protected、privateのようなキーワードはなく、 Dartでは名前の頭に_(アンダースコア)をつけるとprivateとなる。 privateなメンバはクラス外からアクセスできない。(可視性の制御) counter_model.dart class CounterModel extends ChangeNotifier { ... // このcount自体はpublicなので外部から見える int get count => _counter?.counter; bool isChangedHourCounter = true; // _counterと_counterTypeは_がついているのでprivate CounterBase _counter; CounterType _counterType; ... その他の修飾子 var 通常の変数指定。型を指定しない場合につける。指定しない場合でもDartの場合型推論を行う。 個人的には基本使用せず、型を指定すべきかと思う。(型が決まっていない場合はdynamicを使うようにする) final 再代入不可となるが、参照先の内容は書き換えることが出来る。 finalの例 final String hoge = 'hoge'; hoge = 'fuga'; // NG final List<String> piyo = ['piyo','piyopiyo']; piyo[0] = 'piyopiyopiyo'; // OK 内容は書き換えられる const finalと似ているが、コンパイル時に値が決まっていることを保証する。 また、finalと違って参照先の内容を書き換えることも出来ない。 constのイメージ const String hoge = 'fuga'; print('hoge is ${hoge}'); // ↓コンパイル時に以下に変換されるイメージ print('hoge is fuga'); static 指定したクラス内のフィールドがインスタンスごとに保持されるのではなく、クラスで1つの実体を持つことを宣言する。 コンストラクタを用いたインスタンス化を行わなくても、ダイレクトに参照できるようになる。 staticの例 class Hoge { static String fuga = 'piyo'; } main() { print(Hoge.fuga); // piyo } 参考:Dartの変数定義時の修飾static/final/const、そしてconst constructorについて - Qiita カプセル化 アクセス修飾子を使って外部から見えるメンバを制限すること。 内部で何が行われているかを隠蔽し、使いやすくする。 コンストラクタ インスタンス化するときに呼ばれるメソッド。クラス内に定義しない場合、自動的に作成されたデフォルトコンストラクタが呼ばれる。 Dartでは他の言語のようにオーバーロード(同名で、受け取る値が異なるコンストラクタを複数定義し、インスタンス化するときに渡す変数を変えることで、生成されたオブジェクトを変化させる機能)は出来ないが、名前付きコンストラクタという機能で同じようなことが出来る。 コンストラクタの例 class CounterModel extends ChangeNotifier { // クラス名と同じ名前でコンストラクタを作る // メソッド全般の話だが、{}の中に引数を書くことで、使う時に変数名を参照できる // 下の例だと使う時、CounterModel(storageRepository: hoge)のようになる CounterModel({@required StorageRepositoryBase storageRepository}) : _storageRepository = storageRepository; // クラス内のフィールドをfinal(以降変更できない)とするときはコンストラクタ内で初期化するか // 宣言と同時に初期化する必要がある final StorageRepositoryBase _storageRepository; 以下は名前付きコンストラクタの例だが、_で始まっているのでprivateとなる 名前付きコンストラクタの例 class PersistenceStorageProvider { PersistenceStorageProvider._(); static final PersistenceStorageProvider instance = PersistenceStorageProvider._(); 抽象クラスとインターフェース いきなりDartでの仕様を学ぼうとすると混乱するので、一般的な話をする。 抽象クラスとはabstractというキーワードをclassキーワードの前につけたクラスのことで、継承されることを前提として設計される。 継承とはすでに存在しているクラスに、メンバを追加したり元々のメソッドを上書きしたりして新しいクラスを作ること。 継承元のクラスをスーパークラスと言い、継承して出来たクラスをサブクラスという。 抽象クラスは処理の再利用をしたいときに使う。 一方インターフェースは使えるメソッドを定義するためのもの。インターフェースを実装したクラスは、インターフェース内で定義されているメソッドがオーバーライドされていることが保証される。 参考:【詳解】抽象クラスとインタフェースを使いこなそう!! - Qiita Dartにおける抽象クラスと暗黙的インターフェース Dartではinterfaceといったキーワードは使えないが、代わりに全てのクラスは暗黙的に自動でinterfaceが定義される。 継承したいときはextendsを使い、実装したいとき(インターフェースとして使いたいとき)はimplementsを使用する。 abstractクラスをextendsした場合でもimplementsした場合でもスーパークラスで定義されているメソッドはすべてオーバーライドする必要がある。 違いとしては、extendsの場合、abstractクラスで定義したフィールドの利用が可能であり、メソッドの実装までabstract側でやっておいて、superとして処理を呼べる。(処理の共通化) counter_base.dart /// Counterの基本となる抽象クラス /// フィールドにはcounterを持ち /// incrementというcounterをインクリメントするメソッドの /// 実装を強制する abstract class CounterBase { int counter = 0; void increment(); } 以下が継承したサブクラス simple_counter.dart import 'package:flutter_oop_recture/domain/counter_base.dart'; class SimpleCounter extends CounterBase { // @override のアノテーションをつけてメソッドをオーバーライドする @override void increment() { // SimpleCounter自体のフィールドにcounterというものはないが // スーパークラスのフィールドを使用できる super.counter++; } } 一方implementsの場合、変数を定義できず、superとして処理も呼べないため、必ずメソッドの実装をオーバーライドして書き換える必要がある。 storag_repository_base.dart // abstract として定義しているが、目的はinterfaceとしての利用 abstract class StorageRepositoryBase { Future<void> savePersistenceStorage(String key, String value); Future<String> loadPersistenceStorage(String key); Future<bool> isExistKey(String key); Future<void> remove(String key); } 以下が実装クラス storage_repository.dart class StorageRepository implements StorageRepositoryBase { final PersistenceStorageProvider _instance = PersistenceStorageProvider.instance; @override Future<void> savePersistenceStorage(String key, String value) async { final SharedPreferences pref = await _instance.prefs; await pref.setString(key, value); } // 他すべてのメソッドをオーバーライド ... 参考:【Dart】abstract,mixin,extends,implements,with等の使い方基礎 - Zenn ポリモーフィズム ポリモーフィズム(多態性)とはクラスを継承してメソッドをオーバーライドしたことで、同名のメソッドを呼んだ時に挙動が変わることです。 先ほども紹介したCounterBaseの抽象クラスですが、incrementというメソッドを持っています。 counter_base.dart abstract class CounterBase { int counter = 0; void increment(); } SimpleCounterでは単純にインクリメントを行うだけに対し simple_counter.dart class SimpleCounter extends CounterBase { @override void increment() { super.counter++; } } HourCounterではincrementメソッドの中でresetメソッドを呼び、もし指定したmodular(法)の数になったら、値を0にリセットする処理を、スーパークラスのModularCounterで実装しています。 modular_counter.dart abstract class ModularCounter extends CounterBase { ModularCounter(this.modular); final int modular; /// ModularCounterにはリセット機能を実装するよう強制する /// スーパークラスのcounterを用いて実装しておく void reset() { // 明示的にsuper.counterと書かなくとも、スーパークラスのフィールドが使える if (counter >= modular) { counter = 0; } } } このModularCounterを継承することで、resetメソッドを呼び出すことが出来ます。 hour_counter.dart class HourCounter extends ModularCounter { HourCounter() : super(12); @override void increment() { super.counter++; reset(); } @override void reset() { // reset機能はスーパークラスの実装をそのまま使うため // 内部でsuper.reset()を呼ぶだけにしておく super.reset(); } } 依存関係逆転の原則 このようにextendsやimplementsを行って作ったクラスを使う時に注意すべきなのが依存関係逆転の原則です。 CounterBaseを継承して作られたSimpleCounterを使いたい場合に、SimpleCounterをそのまま宣言してしまうとSimpleCounterに依存してしまうことになります。 そうではなく、もっと抽象的な存在であるCounterBaseに依存させることで、後からHourCounterに差し替えることが可能になります。 counter_model.dart class CounterModel extends ChangeNotifier { ... CounterBase _counter; CounterType _counterType; ... /// _counterをCounterBaseとして定義しているので /// 後からSimpleCounterやHourCounterに差し替えることが出来る Future<void> switchCounter() async { if (isChangedHourCounter) { _counter = SimpleCounter(); isChangedHourCounter = false; await _storageRepository.savePersistenceStorage( key_counter, CounterType.simpleCounter.value); } else { _counter = HourCounter(); isChangedHourCounter = true; await _storageRepository.savePersistenceStorage( key_counter, CounterType.hourCounter.value); } notifyListeners(); } /// CounterBaseにはincrementメソッドを定義しているので、気にせずincrementを呼ぶことが出来る /// 実際に行われる処理は具体的に実装されたSimpleCounterやHourCounterで実装したincrementメソッド void increment() { _counter.increment(); notifyListeners(); } } 上記のケースでは単にカウンターの切替を行うだけですが、結局CounterModelがSimpleCounterとHourCounterに依存してしまっています。 そこで、このような依存関係をなくすために、外部から実装クラスを受け取るように実装したくなる時があります。 そのような手法をDI(Dependency Injection)と言います。 CounterModelではコンストラクタからStorageRepositoryBase(インターフェース)を受け取っており、内部ではどのような実装が行われているかは意識しません。 counter_model.dart class CounterModel extends ChangeNotifier { CounterModel({@required StorageRepositoryBase storageRepository}) : _storageRepository = storageRepository; final StorageRepositoryBase _storageRepository; これを外から代入する方法は様々ですが、FlutterではProviderパッケージを使って代入を行うことが出来ます。 main.dart void main() { runApp( MultiProvider( providers: [ // 後から読み込む場合の型はStorageRepositoryBase(インターフェース) Provider<StorageRepositoryBase>( // 実際に生成しているのはStorageRepository(StorageRepositoryのサブクラス) create: (BuildContext context) => StorageRepository(), ), ChangeNotifierProvider<CounterModel>( create: (BuildContext context) => CounterModel( // 上記で生成したStorageRepositoryBaseを型に持つ実体をCounterModelのコンストラクタへ代入 storageRepository: context.read<StorageRepositoryBase>()) ..init(), ), ], child: MyApp(), ), ); } 参考:オブジェクト指向 依存関係逆転の原則の「逆転」とは - Qiita シングルトン 生成したインスタンスが単一であることを保証するもの。 状態を保持したいときや、何度も同じ処理を行いたくないときに使う。 サンプルプロジェクトではshared_preferencesのインスタンスを提供するクラスをシングルトンで定義している。 persistence_storage_provider.dart class PersistenceStorageProvider { PersistenceStorageProvider._(); // staticとすることでインスタンスごとに保持されるのではなく、クラスで1つの実体を持つことを宣言する // instanceという変数への参照に自分自身を代入している static final PersistenceStorageProvider instance = PersistenceStorageProvider._(); // 内部で保持しておきたいインスタンス  SharedPreferences _prefs; Future<SharedPreferences> get prefs async { // ??=を使ってもし_prefがnullならば右辺の結果を代入し、そうでなければそのまま_prefを返却する return _prefs ??= await initSharedPreferences(); } // 実際にSharedPreferencesのインスタンスを取得する処理 Future<SharedPreferences> initSharedPreferences() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs; } } 参考: Dartの変数定義時の修飾static/final/const、そしてconst constructorについて - Qiita 【Flutter】Dartでシングルトンパターンを実装する - taketiyo.log おまけ Unit Test 依存関係逆転の法則を用いてCounterModelからSharedPreferencesの依存を排除したことによって、単体テストが書きやすくなる。単体テストの書き方については過去の記事を参照してください。 counter_model_test.dart void main() { // テスト用のメソッドを使いたいので、型はテスト用repositoryの方を指定する final StorageMemRepository storageRepository = StorageMemRepository(); group('init', () { test('shared_preferencesに値がない場合', () async { storageRepository.clear(); // ここでCounterModelを生成しているが、渡しているのは初めに定義したテスト用のインスタンス final model = CounterModel(storageRepository: storageRepository); await model.init(); // isChangeHourCounterは初期状態のtrueのままであること expect(model.isChangedHourCounter, true); model.dispose(); }); test('shared_preferencesにSimpleCounterが設定してある場合', () async { storageRepository.clear(); // あらかじめsimpleCounterの方をセットしておく await storageRepository.savePersistenceStorage( key_counter, CounterType.simpleCounter.value); final model = CounterModel(storageRepository: storageRepository); await model.init(); // isChangeHourCounterはfalseであること expect(model.isChangedHourCounter, false); model.dispose(); }); }); ... テスト用のレポジトリは以下のようにインメモリで動作するように記載している。 StorageRepositoryBaseをimplementsしているので、CounterModelのコンストラクタで代入出来る。 storage_mem_repository.dart class StorageMemRepository implements StorageRepositoryBase { // テスト用のレポジトリ内のデータの実態はただのMap final Map<String, String> _data = <String, String>{}; // データを初期化できるようにテスト用リポジトリだけclearメソッドを追加 void clear() { _data.clear(); } // その他のメソッドは実際にSharedPreferencesにアクセスせずに // 単に_dataを操作するように書く @override Future<bool> isExistKey(String key) { return Future<bool>.value(_data[key] != null); } ... } まとめ 書く中で色々調べなおして整理したりしたので自分自身の勉強にもなりました。 プログラミング初心者に向けてのつもりで書き始めたけど、初心者を抜け出して中級者になろうとしている人や中級者が読むといいのかもしれないと思いました。 参考 tokku5552/flutter_oop_recture - GitHub 【詳解】抽象クラスとインタフェースを使いこなそう!! - Qiita 【Dart】abstract,mixin,extends,implements,with等の使い方基礎 - Zenn オブジェクト指向 依存関係逆転の原則の「逆転」とは - Qiita Dartの変数定義時の修飾static/final/const、そしてconst constructorについて - Qiita 【Flutter】Dartでシングルトンパターンを実装する - taketiyo.log 【Flutter】Providerで最低限のDIを行ってテスタブルなコードにリファクタリングする - Qiita Flutterで単体テストを書く - Qiita Dartの変数定義時の修飾static/final/const、そしてconst constructorについて - Qiita
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

知識ゼロから独学3ヵ月でAndroidアプリをリリースしよう

はじめに はじめまして。 今回ひょんなことからアプリを開発したくなったので(理由は本筋と離れるので割愛)、全く知識のない人間がどんなことから取り組んだか、どういう進め方をしたか、どういう点で躓いたかを記したいと思います。 ◆対象読者 ・アプリ開発どころかコードも書いたことが無い人 ・何から手をつければいいか分からない人 ・初めてAndroidアプリを開発したいと思っている人 ・初心者の開発プロセスに興味がある人 上記のような人にとって、少しでも参考になったら幸いです。 作ったアプリ↓ Angie&Debbie 女の子を着せ替えるミニゲームアプリです。 よかったら触ってみてくれるととても嬉しいです。 ①Androidアプリしか開発できないことを知る 使っているスマホがiPhoneなので、iOSアプリをつくりたいな~と漠然と考えていたのですがMacでしかiOSアプリは開発できないことを知り驚愕しました。 Windouwsしか持っていない私は、自動的にAndroidアプリ開発の道しか残されていなかったわけです。 スタートはこういったレベルです。 (ちなみに後々実機テストの為にAndroid端末を購入しました。出費……) ②アプリ開発の前に プログラミングとは 急にアプリ開発って出来るものじゃなくない?と、そもそもコードすら書いたことが無い私は不安に思いました。 「初心者 プログラミング」「アプリ開発 初めて」とかでググりまくります。 そして参考にした記事がこちら 【初心者向け】ド素人が29歳からプログラミングを覚えた方法 こちらの記事に ガチの ITド素人 は HTML, CSS からやってください。ある程度ショボい画面が作れるレベルになったら、もう Java や Python などのバックエンドに行っちゃいましょう。(1つに拘ったり、極め過ぎないことがポイント) と書いてあったのを参考に、まずはHTMLとCSSから始めてみることに。 ③ドットインストールでHTML、CSSの勉強 あらゆるサイトや記事でおススメされていたドットインスト-ルでHTMLとCSSを勉強することにしました。 ドットインスト-ルは初学者に非常に親切で、コードとはこういう風に書きます、というのを動画で手取り足取り教えてくれます。 VSCode(コードエディターの1つです)を開いて 「よくエンジニアが使ってる黒い画面だ!!!」 と感動したのを覚えています。 新しいものに触れることの連続で、毎日非常に楽しく勉強出来ました。 そして何より嬉しかったのが自分が書いた通りに画面が表示されたり、ときには動いたりすること。 プログラミングなんて一生縁のないものだと思っていたので、この感動を味わえて良かったと強く思っています。 HTML、CSSの勉強には3週間費やしました。 ドットインストールの講座を一通り見る→学んだことを応用して自分でHPを作って公開してみる で計3週間です。 ④JavaとJavaScriptを間違える HTML、CSSを一通り勉強したので、次にとうとうアプリ開発に移ろうとしました。 AndroidアプリはどうやらJavaという言語で開発するらしいので、これもドットインストールで勉強しようと講座を見ていました。 講座一覧から「Java」の文字を探し、何か後ろに変なのついてるけどこれだろ~と思い、何と私はJavaScriptの講座を見始めてしまったのでした。 間違いに気づいたのは翌日でした。(早く気づいてよかった) Javaのいい参考書などないかとググっていたところ、サジェストに「Java JavaScript 違い」と表示されたため、この2つはどうやら違う言語らしいということに気付けました。あぶねー! そうして私は無事「Java8入門」の講座に転換することが出来ました。 ⑤Kotlinとの出会い ドットインストールでJavaを勉強しつつ、アプリ制作についてのいい参考書が無いかと図書館で探していました。 ただ、図書館にある本は発行されたのが数年前と、少し古いものばかり。 アプリ開発が日々進歩していくことは知っていたので、なるべく新しい本を参考にすべきだと私は考えました。 そこで見つけたのがこの本です。 はじめてのAndroidプログラミング 第5版 発行されたのは2020/12/19。つい最近(現在2021年5月)じゃないですか! 中身はどうやらお手本のアプリを作りながらアプリ制作を勉強していくというものらしい。 しかもアマゾンレビューの点数も高いし、この本を参考にしようと思いました。 しかし、この本はKotlin対応=Javaではないという点が気になりました。 KotlinというJavaをシンプルにしたような言語があり、その存在自体も、GoogleがこのKotlinを推していることも知っていました。 シンプルにした分書きやすい(のか?)言語であるものの、Javaと比べるとネット上に情報が少なく、アプリ開発の途中で何かを調べるとJavaでの情報しか出てこないのではということを危惧しました。 しかし、何も分からないのであればネット上の情報よりも書籍を信頼すべきと考えたため、この本を購入、Kotlinでアプリ開発をすることにしたのでした。 とりあえずJavaの知識はある方が良さげだったので、本に手を出す前にJavaの講座は一通り終えました。 これにかかった時間が約2週間ほどです。 ⑥Android Studioが使えない‼ さて、ここから本格的に”モノ”を作っていくフェーズに入っていきます。 ここから自分のアプリを作るまでの流れは、基本的に先ほどの参考書を進めていくかたちになりますが、その中で躓いたところを書きたいと思います。 AndroidアプリはGoogleが提供してくれているAndroidSudioという開発環境を使って開発します。 AndroidStudio起動後、SDKの保存先を指定しインストールすることになるのですが、この保存先に日本語が含まれているとエラーになってしまいます。 この場合、ローカルディスク(C:)のユーザーフォルダに適当な半角英数字のみのフォルダを作り、そこを保存先として指定することで解決しました。 ⑦本の中身を一周する Androidsudioの使い方も先ほどの本に書いてくれているので、それを見ながら使い方を学んでいきました。 この本は初めに基礎的な知識が書いてあり、その後サンプルアプリをいくつも作っていく流れになっています。 そのサンプルアプリを作る中でいろいろな動作(画面遷移したり、SEをつけたり)を実現するためのコードを学んでいく感じです。 この本を一周すれば私が今回作ったようなシンプルなアプリなら作れるようになるのではないかと思います。 アニメーションを多用したりとか、シンプルなアプリでは満足できない方はこの本だけでは難しそうなので別のものを参考にするのが良いかと思います。 ⑧自分のアプリを作る 本の中身を一周したらとうとう自分の作りたいアプリを作るフェーズに入りました。 初めから着せ替えゲームを作りたいと思っていたので、なんとなくのイメージは出来ていました。 機能に関しても本に書いていあったことを応用+書いてなくてもネットでググればそこそこスムーズに実装出来る範囲のものだったのでそこまで苦労はしなかったのですが、ここでも躓いた点を描こうと思います。 画面キャプチャを撮る 着せ替えゲームなので、着せ替えた結果を画像で保存したいなぁと考えていました。 ただ、これに関しては参考にした本には一切記述が無かったのでイチから自分で調べることになりました。 AndroidSudioをいじるようになれば分かるのですが、アプリの要素には「View」というものがあります。 その名の通り、画面に何かを表示する器みたいなものだと思ってください。 このViewのキャプチャを撮る方法なら沢山出てくるのですが  (例:特定のViewのスクリーンショットを撮る     AndroidでViewのキャプチャを撮る 私が撮りたいものは少し違いました。 私が作成していたものは着せ替えアプリなので、素体画像の上にトップスやボトムス等のパーツごとのレイヤーを表示する仕組みになっています。 なので一つのViewのキャプチャでは着せ替え結果が撮れず…… いろいろ試した結果、キャプチャを撮りたい要素を全てFramelayoutという、Viewを格納出来る更に大きな器のような要素に入れ、それのキャプチャを撮ることで解決しました。 Framelayoutのキャプチャが撮れること自体知らずにダメ元でやってみたら出来たので万々歳でした。 パーミッションまわり アプリを使っているとこういったダイアログが表示されるのを見たことがあると思います。 アプリが何かをするために権限が必要だから許可してくださいね~っていうダイアログですね。 この実装方法も本に書いてあったのですがその通りにコードを書くとアプリが落ちてしまい(どうして…)、ネットの情報を頼りに実装しました。 この辺りはググると鬼のように出てくるのでリンクは割愛します。 (ちなみに、このパーミッションダイアログではなく「画像を保存するには端末の設定から権限を許可して下さい」的な内容のポップアップを出しているアプリを見たことがありますが、正直使ってる側からすると至極面倒だと思うのでやめた方がいいと思いました。) 小さいものも含めると躓いたところは数えきれないほどあるのですが、大きなものはこの2つでした。 ⑨やってみたかったこと AndroidアプリでGif再生する方法 -Androidアプリ開発 こちらの記事を見て「キャラクターをまばたきさせたい!」と考えていたのですが、肝心のまばたきgifアニメが酷いクオリティとなってしまい今回は見送りました。 ただ、アプリは一回リリースして終わりではなくアップデートを重ねていけるものですので、いずれちゃんとしたGifを作って表示させたいと思います。挫けない。 おわりに 想定よりも長々と書いてしまいました。 もしここまで読んでくださった方がいたら心からお礼を言いたいです。 ありがとうございました。 これからアプリを初めて作ろうとしている人へ。 どんなものが出来たとしても、「何かを完成させる」というのはとても自信に繋がります。 そしてトライ&エラーの連続は思っていたより凄く凄く楽しいです。 悩んでいたところでアプリが想定通りの動きをしたとき、ピザとってしまうくらい嬉しいです。 初心者がアプリを作るうえで必要なものは知識よりもガッツだと私は感じました。 分からないことがあっても投げ出さないことと継続して開発を進めるガッツ。もうこれさえあればどうにかなります。 今後Qiitaに投稿するか分かりませんが、またご縁があったらそのときはよろしくお願いします。それでは。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

androidでベーシックなコントローラ配置を学ぶ

今回の教科書 すいすい進んでいいね。おすすめできる ラベルを表示 layout/activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A1A9BA" android:orientation="vertical"> <TextView android:id="@+id/tvLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:text="@string/tv_msg"/> </LinearLayout> 確認 EditTextを追加 layout/activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A1A9BA" android:orientation="vertical"> <TextView android:id="@+id/tvLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:text="@string/tv_msg"/> + <EditText + android:id="@+id/etInput" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="25dp" + android:layout_marginTop="5dp" + android:inputType="text" /> </LinearLayout> 確認 下に出てきた入力UIはもともとandroidが用意してくれているもの ボタンを追加 layout/activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A1A9BA" android:orientation="vertical"> <TextView android:id="@+id/tvLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:text="@string/tv_msg"/> <EditText android:id="@+id/etInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="25dp" android:layout_marginTop="5dp" android:inputType="text" /> + <Button + android:id="@+id/btSave" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/bt_save"/> </LinearLayout> 確認 リニアレイアウトを入れ子にする layout/activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A1A9BA" android:orientation="vertical"> <TextView android:id="@+id/tvLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:text="@string/tv_msg"/> <EditText android:id="@+id/etInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="25dp" android:layout_marginTop="5dp" android:inputType="text" /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <CheckBox + android:id="@+id/cbDrink" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="25dp" + android:text="@string/cb_drink"/> + <CheckBox + android:id="@+id/cbFood" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/cb_food"/> + </LinearLayout> <Button android:id="@+id/btSave" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bt_save"/> </LinearLayout> 確認 ラジオボタンを追加 layout/activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A1A9BA" android:orientation="vertical"> <TextView android:id="@+id/tvLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:text="@string/tv_msg"/> <EditText android:id="@+id/etInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="25dp" android:layout_marginTop="5dp" android:inputType="text" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <CheckBox android:id="@+id/cbDrink" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="25dp" android:text="@string/cb_drink"/> <CheckBox android:id="@+id/cbFood" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/cb_food"/> </LinearLayout> + <RadioGroup + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="10dp" + android:layout_marginTop="10dp" + android:orientation="horizontal" + android:paddingBottom="10dp" + android:paddingTop="10dp"> + <RadioButton + android:id="@+id/rbMale" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="25dp" + android:layout_marginRight="25dp" + android:text="@string/rb_male" /> + <RadioButton + android:id="@+id/rbFemale" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/rb_female" /> + </RadioGroup> <Button android:id="@+id/btSave" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bt_save"/> </LinearLayout> 確認 まぁなんかVisualStudio的な感じよね 選択ボックスを追加 values/strings.xml <resources> <string name="app_name">HelloAndroid</string> <string name="tv_msg">お名前を入力してください</string> <string name="bt_save">保存</string> <string name="cb_drink">ドリンク</string> <string name="cb_food">フード</string> <string name="rb_male">男</string> <string name="rb_female">女</string> + <string-array name="sp_curryList"> + <item>ドライカレー</item> + <item>カツカレー</item> + <item>ビーフカレー</item> + <item>チキンカレー</item> + <item>シーフードカレー</item> + <item>キーマカレー</item> + <item>グリーンカレー</item> + </string-array> </resources> layout/activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A1A9BA" android:orientation="vertical"> <TextView android:id="@+id/tvLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:text="@string/tv_msg"/> <EditText android:id="@+id/etInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="25dp" android:layout_marginTop="5dp" android:inputType="text" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <CheckBox android:id="@+id/cbDrink" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="25dp" android:text="@string/cb_drink"/> <CheckBox android:id="@+id/cbFood" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/cb_food"/> </LinearLayout> <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="10dp" android:orientation="horizontal" android:paddingBottom="10dp" android:paddingTop="10dp"> <RadioButton android:id="@+id/rbMale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:layout_marginRight="25dp" android:text="@string/rb_male" /> <RadioButton android:id="@+id/rbFemale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/rb_female" /> </RadioGroup> + <Spinner + android:id="@+id/spCurryList" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:entries="@array/sp_curryList" + android:paddingBottom="5dp" + android:paddingTop="5dp" /> <Button android:id="@+id/btSave" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bt_save"/> </LinearLayout> 確認 リストを追加 values/strings.xml <resources> <string name="app_name">HelloAndroid</string> <string name="tv_msg">お名前を入力してください</string> <string name="bt_save">保存</string> <string name="cb_drink">ドリンク</string> <string name="cb_food">フード</string> <string name="rb_male">男</string> <string name="rb_female">女</string> <string-array name="sp_curryList"> <item>ドライカレー</item> <item>カツカレー</item> <item>ビーフカレー</item> <item>チキンカレー</item> <item>シーフードカレー</item> <item>キーマカレー</item> <item>グリーンカレー</item> </string-array> + <string-array name="lv_cocktailList"> + <item>ホワイト・レディー</item> + <item>バラライカ</item> + <item>XYZ</item> + <item>ニューヨーク</item> + <item>マンハッタン</item> + <item>ミシシッピミュール</item> + <item>マイタイ</item> + <item>マティーニ</item> + </string-array> </resources> layout/activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A1A9BA" android:orientation="vertical"> <TextView android:id="@+id/tvLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:text="@string/tv_msg"/> <EditText android:id="@+id/etInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="25dp" android:layout_marginTop="5dp" android:inputType="text" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <CheckBox android:id="@+id/cbDrink" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="25dp" android:text="@string/cb_drink"/> <CheckBox android:id="@+id/cbFood" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/cb_food"/> </LinearLayout> <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginTop="10dp" android:orientation="horizontal" android:paddingBottom="10dp" android:paddingTop="10dp"> <RadioButton android:id="@+id/rbMale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:layout_marginRight="25dp" android:text="@string/rb_male" /> <RadioButton android:id="@+id/rbFemale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/rb_female" /> </RadioGroup> <Spinner android:id="@+id/spCurryList" android:layout_width="match_parent" android:layout_height="wrap_content" android:entries="@array/sp_curryList" android:paddingBottom="5dp" android:paddingTop="5dp" /> <Button android:id="@+id/btSave" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bt_save"/> + <ListView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:background="#ffffff" + android:entries="@array/lv_cocktailList" /> </LinearLayout> 確認
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む