20210610のAndroidに関する記事は6件です。

Harmony OSはオープンソースなのか?OpenHarmonyと何が違うか?

Harmony OSはオープンソースコードのOSで、ファーウェイはオープンソース財団OpenAtom Foundationにリリースしたのですが、しかし、これは完全に正しくありません。正確にいうと、ファーウェイがオープンソースにしたのは、Harmony OSではなく、そのもとであるOpenHarmonyです。 ではHarmony OSとOpenHarmonyはどう違うでしょうか?それをお話しする前に、まずAndroidについておさらいします。 よくあるAndroid端末 AndroidもHarmony OSと同じく、オープンソースというイメージが強いですが、実はこれも間違いです。 私たちがよく使っているAndroidはGmailが使えたり、YouTubeが見れたり、Googleマップが使えたりします。その中身はAndroid オープンソース プロジェクト(AOSP)+Google モバイル サービスです。 つまり、一般的なAndroidの正体はこのようなオープンソースの部分と非公開の部分で構成されたものです。 当然AOSPだけでも動作します。ただグーグルのモバイルサービスが使えないだけです。たとえば中国のAndroid端末はGMSを搭載しないものが多いです。 Harmony OS Harmony OSとOpenHarmonyの関係性をわかりやすく説明すると、Harmony OSはオープンソースであるOpenHarmonyと非公開であるHMSで構成されたものというわけです。 参考 Harmony OS : https://www.harmonyos.com/en/home OpenHarmony : https://gitee.com/openharmony HMS : https://consumer.huawei.com/jp/mobileservices/ HMS開発 : https://developer.huawei.com/consumer/jp/hms
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】ダイアログのmatch_parentについて

ダイアログの幅・高さいっぱいに広げるのに手間取ったのでメモ。 あまり使う場面はないと思うが。 AlertDialog AlertDialogは比較的簡単に実装可能なため、これを使用していた。 ただ、これだと幅・高さいっぱいに広げられない。 MainActivity.kt val dialog = AlertDialog.Builder(this).apply { setTitle("タイトル") setMessage("メッセージ") setNegativeButton("閉じる") { dialog, _ -> dialog.cancel() } }.create() //このようにmatch_parentを使おうとしても意味がない dialog.window?.setLayout( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) dialog.show() Dialog 通常のダイアログを使用すると、幅・高さいっぱいに広げることができる。 まずダイアログのレイアウト作成。 test_dialog.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" android:gravity="center" android:orientation="vertical" android:paddingHorizontal="60dp" android:paddingVertical="20dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start" android:text="タイトル" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start" android:text="メッセージ" android:textSize="18sp" /> <Button android:id="@+id/button" style="?android:attr/borderlessButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:text="閉じる" android:textColor="@color/black" /> </LinearLayout> 次に処理部分の作成。ここでmatch_parent指定。 TestDialogFragment.kt class TestDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return Dialog(requireContext()).apply { val binding = TestDialogBinding.inflate(LayoutInflater.from(context)) binding.button.setOnClickListener { dismiss() } setContentView(binding.root) //ここで幅・高さをmatch_parentにする window?.setLayout( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) } } } 最後にMainActivityでダイアログ表示 MainActivity.kt TestDialogFragment().show(supportFragmentManager, TestDialogFragment::class.java.simpleName)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin研修6日目】SQLiteデータベースを用いたデータ管理

Androidにおけるデータ保存 端末内にデータを保存する方法は以下の4通り。 方法 内容 SQLiteデータベース Android OS標準のRDB プレファレンス キーと値の簡易データ→アプリの設定値の保存 内部ストレージ 端末内部のストレージ 外部ストレージ カードスロットに挿入するストレージ SQLiteデータベースの利用 参考: SQLite 参考: SQLite入門(SQL文) SQLiteデータベースを利用する手順は、以下の通り。 SQLiteOpenHelperクラスを継承するDatabaseHelper(=DBヘルパーオブジェクト)クラスを作成し、SQLiteDatabaseオブジェクト(=DB接続オブジェクト)を生成 アクティビティで、DBヘルパーオブジェクトからDB接続オブジェクトを取得 アクティビティで、DB接続オブジェクトを用いてSQL文を実行 データベースヘルパー(DatabaseHelper)クラスの作成 app/java/<パッケージ名>フォルダにKotlin Fileを新規追加し、 SQLiteOpenHelperクラスを継承するDatabaseHelperクラスを作成する。 SQLiteOpenHelperクラス サブクラスを作成することで、保持しているDB接続オブジェクトを通じてSQLiteデータベースの作成と管理を可能にするクラス。 SQLiteOpenHelperのサブクラスオブジェクトはDBヘルパーオブジェクトと呼ばれ、DB接続オブジェクトを所有する。 SQLiteDatabaseクラス アクティビティとSQLiteデータベースの橋渡しを行うクラス。 アクティビティで記述したSQL文を用いてSQLiteデータベースにアクセスし、 実行結果をCursorオブジェクトに格納するDB接続オブジェクト。 Cursorクラス SQL文(SELECT文)の実行結果を表形式で格納するカーソルクラス。 Cursorクラスのメソッドを用いてデータを取得する。 定義 DatabaseHelper.kt class DatabaseHelper(context: Context): SQLiteOpenHelper( context: Context?, name: String?, factory: SQLiteDatabase.CursorFactory?. version: Int ) { ... // 端末内部のSQLiteデータベースのバージョンが古い場合に実行される処理 // -> 抽象メソッドのため、処理が不要でも記述する必要がある override fun onUpgrade( db: SQLiteDatabase?, oldVersion: Int, newVersion: Int ) {} } // パラメータ(SQLiteOpenHelperコンストラクタ) // context: DatabaseHelperクラスを利用するアクティビティオブジェクト(コンテキスト) // -> DatabaseHelperクラス側では用意できないため、クラスコンストラクタの引数を利用 // name: DB名 // factory: 手動的に作成するCursorFactoryオブジェクト(通常: null) // version: DBのバージョン番号(作成時は1で定義) // パラメータ(onUpgrade()メソッド) // db: DB接続オブジェクト(後述) // oldVersion: 内部DBのバージョン番号 // newVersion: コンストラクタで設定したバージョン番号 DB接続オブジェクトの生成 定義したDBヘルパーオブジェクトの初期化時(=onCreate())に、 DDL文を用いてSQLiteデータベースを生成する。 DDL(Data Definition Language) SQLiteデータベースの生成・削除を行うSQL文。 CREATE文によるDB生成 CREATE文 CREATE TABLE <テーブル名> ( _id INTEGER PRIMARY KEY, -- 主キーとなるID(INTEGER型) /* 主キーによってレコード(=行)が特定できる */ <カラム名1> TEXT, -- カラム①(TEXT型) <カラム名2> BOOLEAN, -- カラム②(BOOLEAN型) <カラム名3> DOUBLE, -- カラム③(DOUBLE型) <カラム名4> DATE, -- カラム④(DATE型) <カラム名5> BLOB -- カラム⑤(BLOB型) ); 定義 SQLiteDatabase?.execSQL(sql: String!): Unit // パラメータ // sql: DDL文 サンプルコード DatabaseHelper.kt // SQLiteデータベースを生成するクラス // -> SQLiteOpenHelperのコンテキストを代入するため、 // クラスコンストラクタにContextを必要とする class DatabaseHelper(context: Context): SQLiteOpenHelper( context, DATABASE_NAME, null, DATABASE_VERSION ) { // private定数の定義 companion object { // DB名 private const val DATABASE_NAME = "itemmemo.db" // 生成時のDBバージョン番号 private const val DATABASE_VERSION = 1 } // 初期化時に実行される処理 // -> DatabaseHelperクラスの初期化と同時に、 // 保持しているSQLiteDatabaseオブジェクトを通じてSQLiteデータベースを生成 override fun onCreate(db: SQLiteDatabase?) { // DDL文 // -> 改行によって可読性を高めるため、append()ができるStringBuilderを用いる // StringBuilder: 文字列を格納する(同一ポインタ内での)可変配列を定義するクラス val sb = StringBuilder() // DDL文の定義 sb.append("<DBを作成するDDL文>") ... // DDL文に応じてappend()の繰り返し // DDL文をString型に変換 val sql = sb.toString() // SQLiteデータベースの生成 db?.execSQL(sql) } ... } SQLiteDatabaseオブジェクトの取得 DBヘルパーオブジェクトが所有するDB接続オブジェクトには、プロパティを通じてアクセスすることができる。 定義 // 読み書き可能なSQLiteDatabaseオブジェクト val db = SQLiteOpenHelper.writableDatabase // 読み込み専用のSQLiteDatabaseオブジェクト // -> データベースにデータが書き込めない場合などに用いる val db = SQLiteOpenHelper.readableDatabase サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // DatabaseHelperオブジェクトの生成 // -> クラスコンストラクタを定義しているため、 // DatabaseHelperクラスを利用するコンテキストを指定 // <- 様々な処理で使用するため、処理直前ではなく事前に生成 private val _helper = DatabaseHelper(this@MainActivity) // SQLiteDatabaseオブジェクト(DB接続オブジェクト)の指定 // SQLiteOpenHelper.writableDatabase: 読み書き可能なSQLiteDatabaseオブジェクト val db = _helper.writableDatabase ... } SQL文によるデータベースへのアクセス SQL文を用いてデータベースにアクセスする手順は、データ更新処理とデータ取得処理によって異なる。 データ更新処理 データ更新処理を行う手順は、以下の通り。 DML文をSQLiteDatabaseクラスのメソッドを用いてコンパイルを実行 1.でバインド変数(後述)を用いた場合は値をバインド コンパイルしたDML文を実行 DML文のコンパイル コンパイルされたDML文は、ステートメントオブジェクトに変換される。 ステートメントオブジェクトはSQLiteStatementクラスで定義される。 DML(Data Manipulation Language) テーブルに対して、データの取得・追加・更新・削除を行うSQL文。 SQLiteStatementクラス ステートメントオブジェクトを定義するクラス。 SQLiteStatementクラスのメソッドを用いて、定義したDML文を実行することができる。 定義 SQLiteDatabase.compileStatement(sql: String!): SQLiteStatement! // sql: SQL文 サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // DatabaseHelperオブジェクトの生成 private val _helper = DatabaseHelper(this@MainActivity) // ボタンの"タップ"イベント検知時の処理(イベントハンドラ) fun onSaveButtonClick(view: View) { // SQLiteDatabaseオブジェクト(DB接続オブジェクト)の指定 val db = _helper.writableDatabase // 更新前データを削除するSQL文 // ?: 変数が後から挿入される箇所(=バインド変数) val sqlDelete = "DELETE FROM itemmemos WHERE _id = ?" // 更新前データを削除するSQL文のコンパイル var stmt = db.compileStatement(sqlDelete) ... } } バインド変数への値のバインド バインド変数(=?)を用いた場合は、SQLiteStatementクラスが継承している、 SQLiteProgramクラスのbind<T>()メソッドを用いてバインド変数に値をバインドする。 ※<T>は型のプレースホルダ バインド変数 後からSQL文中に値をバインドできる変数で、?によって表される。 SQLiteProgram コンパイルされたSQL文を実行するSQLiteStatementクラスの基本クラス。 定義 SQLiteProgram.bind<T>(index: Int, value: <T>): Unit // パラメータ // index: SQL文の"?"のインデックス番号 // -> 1番目は"1", 2番目は"2", ..., n番目は"n" // value: SQL文の"?"にバインドする値 サンプルコード MainActivity.kt // 更新前データを削除するSQL文 // ?: 変数が後から挿入される箇所(=バインド変数) val sqlDelete = "DELETE FROM itemmemos WHERE _id = ?" // 更新前データを削除するSQL文のコンパイル var stmt = db.compileStatement(sqlDelete) // バインド変数への値のバインド stmt.bindLong(1, _itemId.toLong()) // ------------------------------- // 更新後データを挿入するSQL文 // ?: 変数が後から挿入される箇所(=バインド変数) val sqlInsert = "INSERT INTO itemmemos (_id, item, note) VALUES (?, ?, ?)" // 更新後データを挿入するSQL文のコンパイル stmt = db.compileStatement(sqlInsert) // バインド変数への値のバインド stmt.bindLong(1, _itemId.toLong()) stmt.bindString(2, _item) stmt.bindString(3, note) SQL文(DML文)の実行 コンパイルされたSQL文(=ステートメントオブジェクト)は、 更新・削除を行うexecuteUpdateDelete()メソッドと、 追加(=挿入)を行うexecuteInsert()メソッドを用いて実行する。 DELETE, INSERT, UPDATE文によるテーブルの更新 -- データの削除 DELETE FROM <テーブル名> WHERE <条件式>; -- データの挿入(追加) /* 全てのカラムにデータを追加する場合 */ INSERT INTO <テーブル名> VALUES (<追加するデータ>) /* 特定のカラムにデータを追加する場合 */ INSERT INTO <テーブル名> (<カラム名>) VALUES (<追加するデータ>); -- データの更新 UPDATE <テーブル名> SET <カラム名>=<更新後のデータ> WHERE <条件式>; 定義 // データの更新・削除 // -> 返り値はSQL文の実行によって変更された行の数 SQLiteStatement.executeUpdateDelete(): Int // データの追加(挿入) // -> 返り値はSQL文の実行によって変更された行の主キー SQLiteStatement.executeInsert(): Long サンプルコード MainActivity.kt // データの(更新・)削除 // 更新前データを削除するSQL文(DML文) val sqlDelete = "DELETE FROM itemmemos WHERE _id = ?" // 更新前データを削除するSQL文のコンパイル var stmt = db.compileStatement(sqlDelete) ... // 値のバインド処理 // (更新・削除を行う)DML文の実行 stmt.executeUpdateDelete() // ------------------------------- // データの追加(挿入) // 更新後データを挿入するSQL文(DML文) val sqlInsert = "INSERT INTO itemmemos (_id, item, note) VALUES (?, ?, ?)" // 更新後データを挿入するSQL文のコンパイル stmt = db.compileStatement(sqlInsert) ... // 値のバインド処理 // (挿入を行う)SQL文の実行 stmt.executeInsert() データ取得処理 データ取得処理を行う手順は、以下の通り。 SQLiteDatabaseクラスのメソッドを用いてDML文を実行し、実行結果をCursorオブジェクトに格納※コンパイルは不要※バインド変数を用いる場合は、実行時にバインド処理を行う カーソルをループ処理で走査しながら、各レコードのデータを取得 SQL文(DML文)の実行、Cursorオブジェクトへの格納 SELECT文によるデータの取得 SELECT <カラム名> FROM <テーブル名> WHERE <条件式>; 定義 SQLiteDatabase.rawQuery( sql: String!, selectionArgs: Array<String!>! ): Cursor! // パラメータ // sql: 実行するSQL文 // selectionArgs: バインド変数用のString型配列 // -> バインド変数を利用しない場合はnull // 返り値はSQL文の実行結果表を格納するCursorオブジェクト サンプルコード MainActivity.kt // SQLiteDatabaseオブジェクト(DB接続オブジェクト)の指定 val db = _helper.writableDatabase // データを取得するSQL文 val sql = "SELECT * FROM itemmemos WHERE _id = ${_itemId}" // SQL文(SELECT文)の実行結果表を格納するCursorオブジェクト val cursor = db.rawQuery(sql, null) カーソルの走査によるデータの取得 SQL文(SELECT文)によって得られた実行結果は、 表形式でCursorオブジェクト(=カーソル)に格納されているため、 カーソルをレコード(=行)単位で走査することで実行結果を参照する。 走査を行う中で、カーソルがもつテーブルから、指定したカラム(=列)以外を排除しながら 条件に一致するレコードのインデックス番号を取得できるgetColumnIndex()メソッドを用いて、 カーソルから取得するデータを抽出する。 定義 // 指定したカラム(=列)名に一致するレコード(=行)の、Index番号を取得 // -> 該当レコードが存在しない場合は-1を返却 // <- 指定したカラム以外のカラムは排除される Cursor.getColumnIndex(columnName: String!): Int // パラメータ // columnName: カラム名 // データの取得 Cursor.get<T>(columnIndex: Int): <T> // パラメータ // columnIndex: 取得する列のIndex番号 サンプルコード MainActivity.kt // 取得する文字列 // -> データが存在しない場合に備えて空文字で初期化 var note = "" // SQL文(SELECT文)の実行結果表を格納するCursorオブジェクト val cursor = db.rawQuery(sql, null) // ループによるデータ取得 // Cursor.moveToNext(): 次の行に移動 // -> 次の行が存在する場合はtrue, 存在しない場合はfalseを返す // <- 最初はテーブルの0行目に位置しているため、 // while構文を用いて最初に1行目に移る処理を行う while (cursor.moveToNext()) { // 列名を指定してIndex番号を取得 // <- 指定したカラム以外のカラムは排除される val idxNote = cursor.getColumnIndex("note") // 文字列の取得 note = cursor.getString(idxNote) } バインド変数を用いたデータ取得 サンプルコード MainActivity.kt // 発展: // データを取得するSQL文 val sql = "SELECT * FROM itemmemos WHERE _id = ?" // バインドする値を格納した配列 val params = arrayOf(_itemId.toString()) // SQL文(SELECT文)の実行結果表を格納するCursorオブジェクト val cursor = db.rawQuery(sql, params) SQL文を記述せずにデータを取得 定義 SQLiteDatabase.query( table: String!, columns: Array<String!>!, selection: String!, selectionArgs: Array<String!>!, groupBy: String!, having: String!, orderBy: String! ): Cursor! // パラメータ // table: データベース名 // columns: 取得する列名を格納したString型配列 // -> nullの場合は全ての列を取得 // selection: 取得する行の条件 // selectionArgs: バインド変数用のString型配列 // -> バインド変数を利用しない場合はnull // groupBy: グループ化条件 // -> nullの場合はグループ化を行わない // having: 抽出するグループ条件 // -> nullの場合はグループ化を行わない // orderBy: ソート条件 // -> nullの場合はソートを行わない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin研修7日目】SQLiteデータベースを用いたデータ管理

Androidにおけるデータ保存 端末内にデータを保存する方法は以下の4通り。 方法 内容 SQLiteデータベース Android OS標準のRDB プレファレンス キーと値の簡易データ→アプリの設定値の保存 内部ストレージ 端末内部のストレージ 外部ストレージ カードスロットに挿入するストレージ SQLiteデータベースの利用 参考: SQLite 参考: SQLite入門(SQL文) SQLiteデータベースを利用する手順は、以下の通り。 SQLiteOpenHelperクラスを継承するDatabaseHelper(=DBヘルパーオブジェクト)クラスを作成し、SQLiteDatabaseオブジェクト(=DB接続オブジェクト)を生成 アクティビティで、DBヘルパーオブジェクトからDB接続オブジェクトを取得 アクティビティで、DB接続オブジェクトを用いてSQL文を実行 アクティビティ終了時にDBヘルパーオブジェクトを解放 データベースヘルパー(DatabaseHelper)クラスの作成 app/java/<パッケージ名>フォルダにKotlin Fileを新規追加し、 SQLiteOpenHelperクラスを継承するDatabaseHelperクラスを作成する。 SQLiteOpenHelperクラス サブクラスを作成することで、保持しているDB接続オブジェクトを通じてSQLiteデータベースの作成と管理を可能にするクラス。 SQLiteOpenHelperのサブクラスオブジェクトはDBヘルパーオブジェクトと呼ばれ、DB接続オブジェクトを所有する。 SQLiteDatabaseクラス アクティビティとSQLiteデータベースの橋渡しを行うクラス。 アクティビティで記述したSQL文を用いてSQLiteデータベースにアクセスし、 実行結果をCursorオブジェクトに格納するDB接続オブジェクト。 Cursorクラス SQL文(SELECT文)の実行結果を表形式で格納するカーソルクラス。 Cursorクラスのメソッドを用いてデータを取得する。 定義 DatabaseHelper.kt class DatabaseHelper(context: Context): SQLiteOpenHelper( context: Context?, name: String?, factory: SQLiteDatabase.CursorFactory?. version: Int ) { ... // 端末内部のSQLiteデータベースのバージョンが古い場合に実行される処理 // -> 抽象メソッドのため、処理が不要でも記述する必要がある override fun onUpgrade( db: SQLiteDatabase?, oldVersion: Int, newVersion: Int ) {} } // パラメータ(SQLiteOpenHelperコンストラクタ) // context: DatabaseHelperクラスを利用するアクティビティオブジェクト(コンテキスト) // -> DatabaseHelperクラス側では用意できないため、クラスコンストラクタの引数を利用 // name: DB名 // factory: 手動的に作成するCursorFactoryオブジェクト(通常: null) // version: DBのバージョン番号(作成時は1で定義) // パラメータ(onUpgrade()メソッド) // db: DB接続オブジェクト(後述) // oldVersion: 内部DBのバージョン番号 // newVersion: コンストラクタで設定したバージョン番号 DB接続オブジェクトの生成 定義したDBヘルパーオブジェクトの初期化時(=onCreate())に、 DDL文を用いてSQLiteデータベースを生成する。 DDL(Data Definition Language) SQLiteデータベースの生成・削除を行うSQL文。 CREATE文によるDB生成 CREATE文 CREATE TABLE <テーブル名> ( _id INTEGER PRIMARY KEY, -- 主キーとなるID(INTEGER型) /* 主キーによってレコード(=行)が特定できる */ <カラム名1> TEXT, -- カラム①(TEXT型) <カラム名2> BOOLEAN, -- カラム②(BOOLEAN型) <カラム名3> DOUBLE, -- カラム③(DOUBLE型) <カラム名4> DATE, -- カラム④(DATE型) <カラム名5> BLOB -- カラム⑤(BLOB型) ); 定義 SQLiteDatabase?.execSQL(sql: String!): Unit // パラメータ // sql: DDL文 サンプルコード DatabaseHelper.kt // SQLiteデータベースを生成するクラス // -> SQLiteOpenHelperのコンテキストを代入するため、 // クラスコンストラクタにContextを必要とする class DatabaseHelper(context: Context): SQLiteOpenHelper( context, DATABASE_NAME, null, DATABASE_VERSION ) { // private定数の定義 companion object { // DB名 private const val DATABASE_NAME = "itemmemo.db" // 生成時のDBバージョン番号 private const val DATABASE_VERSION = 1 } // 初期化時に実行される処理 // -> DatabaseHelperクラスの初期化と同時に、 // 保持しているSQLiteDatabaseオブジェクトを通じてSQLiteデータベースを生成 override fun onCreate(db: SQLiteDatabase?) { // DDL文 // -> 改行によって可読性を高めるため、append()ができるStringBuilderを用いる // StringBuilder: 文字列を格納する(同一ポインタ内での)可変配列を定義するクラス val sb = StringBuilder() // DDL文の定義 sb.append("<DBを作成するDDL文>") ... // DDL文に応じてappend()の繰り返し // DDL文をString型に変換 val sql = sb.toString() // SQLiteデータベースの生成 db?.execSQL(sql) } ... } SQLiteDatabaseオブジェクトの取得 DBヘルパーオブジェクトが所有するDB接続オブジェクトには、プロパティを通じてアクセスすることができる。 定義 // 読み書き可能なSQLiteDatabaseオブジェクト val db = SQLiteOpenHelper.writableDatabase // 読み込み専用のSQLiteDatabaseオブジェクト // -> データベースにデータが書き込めない場合などに用いる val db = SQLiteOpenHelper.readableDatabase サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // DatabaseHelperオブジェクトの生成 // -> クラスコンストラクタを定義しているため、 // DatabaseHelperクラスを利用するコンテキストを指定 // <- 様々な処理で使用するため、処理直前ではなく事前に生成 private val _helper = DatabaseHelper(this@MainActivity) // SQLiteDatabaseオブジェクト(DB接続オブジェクト)の指定 // SQLiteOpenHelper.writableDatabase: 読み書き可能なSQLiteDatabaseオブジェクト val db = _helper.writableDatabase ... } DBヘルパーオブジェクトの解放 SQLiteデータベースを利用するアクティビティの終了(=onDestroy())時、 close()メソッドを用いてDBヘルパーオブジェクトを解放する必要がある。 定義 SQLiteOpenHelper.close() サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // DatabaseHelperオブジェクトの生成 private val _helper = DatabaseHelper(this@MainActivity) // アクティビティ終了時に実行する処理 override fun onDestroy() { // DatabaseHelperオブジェクトの解放 // SQLiteOpenHelper.close(): Databaseオブジェクトの解放 _helper.close() // アクティビティの終了 super.onDestroy() } } SQL文によるデータベースへのアクセス SQL文を用いてデータベースにアクセスする手順は、データ更新処理とデータ取得処理によって異なる。 データ更新処理 データ更新処理を行う手順は、以下の通り。 DML文をSQLiteDatabaseクラスのメソッドを用いてコンパイルを実行 1.でバインド変数(後述)を用いた場合は値をバインド コンパイルしたDML文を実行 DML文のコンパイル コンパイルされたDML文は、ステートメントオブジェクトに変換される。 ステートメントオブジェクトはSQLiteStatementクラスで定義される。 DML(Data Manipulation Language) テーブルに対して、データの取得・追加・更新・削除を行うSQL文。 SQLiteStatementクラス ステートメントオブジェクトを定義するクラス。 SQLiteStatementクラスのメソッドを用いて、定義したDML文を実行することができる。 定義 SQLiteDatabase.compileStatement(sql: String!): SQLiteStatement! // sql: SQL文 サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // DatabaseHelperオブジェクトの生成 private val _helper = DatabaseHelper(this@MainActivity) // ボタンの"タップ"イベント検知時の処理(イベントハンドラ) fun onSaveButtonClick(view: View) { // SQLiteDatabaseオブジェクト(DB接続オブジェクト)の指定 val db = _helper.writableDatabase // 更新前データを削除するSQL文 // ?: 変数が後から挿入される箇所(=バインド変数) val sqlDelete = "DELETE FROM itemmemos WHERE _id = ?" // 更新前データを削除するSQL文のコンパイル var stmt = db.compileStatement(sqlDelete) ... } } バインド変数への値のバインド バインド変数(=?)を用いた場合は、SQLiteStatementクラスが継承している、 SQLiteProgramクラスのbind<T>()メソッドを用いてバインド変数に値をバインドする。 ※<T>は型のプレースホルダ バインド変数 後からSQL文中に値をバインドできる変数で、?によって表される。 SQLiteProgram コンパイルされたSQL文を実行するSQLiteStatementクラスの基本クラス。 定義 SQLiteProgram.bind<T>(index: Int, value: <T>): Unit // パラメータ // index: SQL文の"?"のインデックス番号 // -> 1番目は"1", 2番目は"2", ..., n番目は"n" // value: SQL文の"?"にバインドする値 サンプルコード MainActivity.kt // 更新前データを削除するSQL文 // ?: 変数が後から挿入される箇所(=バインド変数) val sqlDelete = "DELETE FROM itemmemos WHERE _id = ?" // 更新前データを削除するSQL文のコンパイル var stmt = db.compileStatement(sqlDelete) // バインド変数への値のバインド stmt.bindLong(1, _itemId.toLong()) // ------------------------------- // 更新後データを挿入するSQL文 // ?: 変数が後から挿入される箇所(=バインド変数) val sqlInsert = "INSERT INTO itemmemos (_id, item, note) VALUES (?, ?, ?)" // 更新後データを挿入するSQL文のコンパイル stmt = db.compileStatement(sqlInsert) // バインド変数への値のバインド stmt.bindLong(1, _itemId.toLong()) stmt.bindString(2, _item) stmt.bindString(3, note) SQL文(DML文)の実行 コンパイルされたSQL文(=ステートメントオブジェクト)は、 更新・削除を行うexecuteUpdateDelete()メソッドと、 追加(=挿入)を行うexecuteInsert()メソッドを用いて実行する。 DELETE, INSERT, UPDATE文によるテーブルの更新 -- データの削除 DELETE FROM <テーブル名> WHERE <条件式>; -- データの挿入(追加) /* 全てのカラムにデータを追加する場合 */ INSERT INTO <テーブル名> VALUES (<追加するデータ>) /* 特定のカラムにデータを追加する場合 */ INSERT INTO <テーブル名> (<カラム名>) VALUES (<追加するデータ>); -- データの更新 UPDATE <テーブル名> SET <カラム名>=<更新後のデータ> WHERE <条件式>; 定義 // データの更新・削除 // -> 返り値はSQL文の実行によって変更された行の数 SQLiteStatement.executeUpdateDelete(): Int // データの追加(挿入) // -> 返り値はSQL文の実行によって変更された行の主キー SQLiteStatement.executeInsert(): Long サンプルコード MainActivity.kt // データの(更新・)削除 // 更新前データを削除するSQL文(DML文) val sqlDelete = "DELETE FROM itemmemos WHERE _id = ?" // 更新前データを削除するSQL文のコンパイル var stmt = db.compileStatement(sqlDelete) ... // 値のバインド処理 // (更新・削除を行う)DML文の実行 stmt.executeUpdateDelete() // ------------------------------- // データの追加(挿入) // 更新後データを挿入するSQL文(DML文) val sqlInsert = "INSERT INTO itemmemos (_id, item, note) VALUES (?, ?, ?)" // 更新後データを挿入するSQL文のコンパイル stmt = db.compileStatement(sqlInsert) ... // 値のバインド処理 // (挿入を行う)SQL文の実行 stmt.executeInsert() データ取得処理 データ取得処理を行う手順は、以下の通り。 SQLiteDatabaseクラスのメソッドを用いてDML文を実行し、実行結果をCursorオブジェクトに格納※コンパイルは不要※バインド変数を用いる場合は、実行時にバインド処理を行う カーソルをループ処理で走査しながら、各レコードのデータを取得 SQL文(DML文)の実行、Cursorオブジェクトへの格納 SELECT文によるデータの取得 SELECT <カラム名> FROM <テーブル名> WHERE <条件式>; 定義 SQLiteDatabase.rawQuery( sql: String!, selectionArgs: Array<String!>! ): Cursor! // パラメータ // sql: 実行するSQL文 // selectionArgs: バインド変数用のString型配列 // -> バインド変数を利用しない場合はnull // 返り値はSQL文の実行結果表を格納するCursorオブジェクト サンプルコード MainActivity.kt // SQLiteDatabaseオブジェクト(DB接続オブジェクト)の指定 val db = _helper.writableDatabase // データを取得するSQL文 val sql = "SELECT * FROM itemmemos WHERE _id = ${_itemId}" // SQL文(SELECT文)の実行結果表を格納するCursorオブジェクト val cursor = db.rawQuery(sql, null) カーソルの走査によるデータの取得 SQL文(SELECT文)によって得られた実行結果は、 表形式でCursorオブジェクト(=カーソル)に格納されているため、 カーソルをレコード(=行)単位で走査することで実行結果を参照する。 走査を行う中で、カーソルがもつテーブルから、指定したカラム(=列)以外を排除しながら 条件に一致するレコードのインデックス番号を取得できるgetColumnIndex()メソッドを用いて、 カーソルから取得するデータを抽出する。 定義 // 指定したカラム(=列)名に一致するレコード(=行)の、Index番号を取得 // -> 該当レコードが存在しない場合は-1を返却 // <- 指定したカラム以外のカラムは排除される Cursor.getColumnIndex(columnName: String!): Int // パラメータ // columnName: カラム名 // データの取得 Cursor.get<T>(columnIndex: Int): <T> // パラメータ // columnIndex: 取得する列のIndex番号 サンプルコード MainActivity.kt // 取得する文字列 // -> データが存在しない場合に備えて空文字で初期化 var note = "" // SQL文(SELECT文)の実行結果表を格納するCursorオブジェクト val cursor = db.rawQuery(sql, null) // ループによるデータ取得 // Cursor.moveToNext(): 次の行に移動 // -> 次の行が存在する場合はtrue, 存在しない場合はfalseを返す // <- 最初はテーブルの0行目に位置しているため、 // while構文を用いて最初に1行目に移る処理を行う while (cursor.moveToNext()) { // 列名を指定してIndex番号を取得 // <- 指定したカラム以外のカラムは排除される val idxNote = cursor.getColumnIndex("note") // 文字列の取得 note = cursor.getString(idxNote) } バインド変数を用いたデータ取得 サンプルコード MainActivity.kt // 発展: // データを取得するSQL文 val sql = "SELECT * FROM itemmemos WHERE _id = ?" // バインドする値を格納した配列 val params = arrayOf(_itemId.toString()) // SQL文(SELECT文)の実行結果表を格納するCursorオブジェクト val cursor = db.rawQuery(sql, params) SQL文を記述せずにデータを取得 定義 SQLiteDatabase.query( table: String!, columns: Array<String!>!, selection: String!, selectionArgs: Array<String!>!, groupBy: String!, having: String!, orderBy: String! ): Cursor! // パラメータ // table: データベース名 // columns: 取得する列名を格納したString型配列 // -> nullの場合は全ての列を取得 // selection: 取得する行の条件 // selectionArgs: バインド変数用のString型配列 // -> バインド変数を利用しない場合はnull // groupBy: グループ化条件 // -> nullの場合はグループ化を行わない // having: 抽出するグループ条件 // -> nullの場合はグループ化を行わない // orderBy: ソート条件 // -> nullの場合はソートを行わない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AndroidStudioのMainActivityクラスについて

今回はAndroidStudioのMainActivityクラスについてまとめていきます。 サンプルコード MainActivity.kt package com.websarva.wings.android.hellosample import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } プロジェクトを作成すると、上記のコードが自動で入力されます。 それぞれにどんな意味があるのかを噛み砕いれいきます。 解説 class MainActivity : AppCompatActivity()  ∟MainActivityと言う子クラスに、AppCompatActivityと言う親クラスの継承を定義している。   ∟つまり、MainActivityクラスは、AppCompatActivityクラスの要素を引き継ぐことができる。 AppCompatActivity  ∟AppCompatActivityクラスは、Activityクラスの子クラスです。かつては、Activityクラスを継承して、アクティビティクラスを作成していましたが、通常は、AppCompatActivityクラスを継承してアクティビティクラスを作成します。 つまり、 Activityクラス  ∟AppCompatActivityクラス   ∟MainActivityクラス という構造になっている。 AppCompatActivityクラスのonCreate()メソッドは、androidアプリが起動すると、実行されるメソッドです。  ∟画面作成やデータの用意など、初期処理として必要なものを記述する。 onCreate()メソッドの中身 super.onCreate(savedInstanceState)  ∟親クラスのonCreateメソッドを呼び出している(AppCompatActivity)  ∟アクティビティクラスはActivityクラスをを継承して作る必要があります。  ∟onCreateメソッドを、Activityクラスから、オーバーライドする必要があるため、この記述が必要があります。 setContentView(R.layout.activity_main)  ∟このアプリで表示する画面の設定をしています。  ∟今回は activity_main.xmlに記述したものを画面として使うので、引数を 「R.layout.activity_main」としています。 リソースを管理してくれるRクラス setContentView(R.layout.activity_main)で使用されている「R...」について見ていきます。 android開発では、resフォルダ内のファイルや「@+id」の値などのリソースを、Kotlinクラスから効率よく利用できるように、Androidではそのファイルや値を識別する為のJavaのint型定数を指定することになっています。 このint定数をまとめて記述するクラスとして、Rクラスを用意し、そこにAndroidStudioの方で自動追記してくれる仕組みになっています。 これにより、Rクラス中の定数(R値と呼ぶ)を使ってリソースをやり取りできる。 ちなみに、 「R.layout.activity_main」は、「res/layout/activity_main」と同じ意味。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Composableのカスタムレイアウト

このページではComposeの1.0.0-beta08バージョンを基としたアニメーション周りの感想をまとめたものです。参考にした動画はこちらですhttps://youtu.be/DDd6IOlH3io beta08での情報なので、随時変更される可能性はございます。 @Composable fun Layout()でカスタムレイアウトを作成! ComposeではLayoutブロックを使要することでカスタムレイアウトを作成することができます。以下動画内の一部を抜粋したサンプルで説明します。 (※画像でのminは140dpですが、この記事を書いている時点では134dpに変更されています) サンプルコード。 https://github.com/android/compose-samples/blob/01d35510a3d5d72099ffbb2c6e25ef4782291d3d/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Categories.kt#L97 Composableレイアウトを作る関数の宣言は以下の通りです。Modifierとcontentを引数にとり、メインブロックにmeasureBlockがあります。 fun Layout( modifier: Modifier = Modifier, content:@Composable () -> Unit, ){ measureBlock: MeasureBlock } メジャーブロックは3つのステップで作ることができます。 1. それぞれのアイテムのmeasureを呼び出す。 2. layout()を呼ぶ。 3. それぞれのアイテムを置く(placeを呼び出す)。 コードの外観 Layout( modifier = ..., children = { Text( text = category.name, ... ) SnackImage( ... ) } ) { measurables: List<Measurable>, constraints: Constraints-> // 1. それぞれのアイテムのmeasureを呼び出す。 // 2. layout()を呼ぶ。 // 3. それぞれのアイテムを置く(placeを呼び出す)。 } MeasureBlockの引数について 引数で渡ってくる constraints: Constraints とはなにか? AndroidエンジニアにはおなじみのConstraintLayoutとは何も関係なく、maxやminのWidthやHeightが取得できるだけで、このViewの大きさの上限と下限がわかるだけのオブジェクトです。 引数で渡ってくる measurables: List<Measurable> とはなにか? このリストの一つ一つの要素がUIのコンポーネントのmeasureを呼べるmeasurableになっている。 例えばここでmeasurables[0]はTextに対応して、measurables[1]はSnackImageに対応する。 このMeasurableに対して Measurable#measure()を呼び出すと Placeable を返す。 一番シンプルな例 一番左上にすべての物を置く例がこちらになります。 以下の流れを行っています。 それぞれのアイテムのmeasureを呼び出す。 layout()を呼ぶ。 それぞれのアイテムを置く(placeを呼び出す)。 Layout( modifier = ..., content = { Text( text = category.name, ... ) SnackImage( ... ) } ) { measurables: List<Measurable>, constraints: Constraints-> // 1. それぞれのアイテムのmeasureを呼び出す。 val placeables = measurables.map { it.measure(constraints) } // 2. layout()を呼ぶ。 layout( width = constraints.maxWidth, height = constraints.maxHeight ) { placeables.forEach { // 3. それぞれのアイテムを置く(placeを呼び出す)。 it.place( x = 0, y = 0 ) } } } placeable.place()関数は、うまくKotlin DSLの機能を使っておりlayout()のブロックの中でしか呼べなくなっています。 Jetpack Composeのサンプルではどうやっているか? 以下のように実際に行っているようです。 https://github.com/android/compose-samples/blob/34a75fb3672622a3fb0e6a78adc88bbc2886c28f/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Categories.kt#L102 より コードの中にコメントとして解説を入れてみました。 Layout( modifier = modifier // レイアウトのアスペクト比を設定 .aspectRatio(1.45f) .shadow(elevation = 3.dp, shape = CategoryShape) // 丸みを帯びた長方形にカット .clip(CategoryShape) .background(Brush.horizontalGradient(gradient)) .clickable { /* todo */ }, content = { Text( text = category.name, style = MaterialTheme.typography.subtitle1, color = JetsnackTheme.colors.textSecondary, modifier = Modifier .padding(4.dp) .padding(start = 8.dp) ) SnackImage( imageUrl = category.imageUrl, contentDescription = null, modifier = Modifier.fillMaxSize() ) } ) { measurables, constraints -> // テキストの横幅の比率設定(55%) val textWidth = (constraints.maxWidth * CategoryTextProportion).toInt() val textPlaceable = measurables[0].measure(Constraints.fixedWidth(textWidth)) // 画像が大きい場合は原寸大を試用し、小さい場合はConstraintの最大サイズに合わせて拡大 val imageSize = max(MinImageSize.roundToPx(), constraints.maxHeight) // 縦横の大きさを固定して、Constraintに渡しmeasure()する val imagePlaceable = measurables[1].measure(Constraints.fixed(imageSize, imageSize)) layout( width = constraints.maxWidth, height = constraints.minHeight ) { //テキストの左寄せ textPlaceable.placeRelative( x = 0, y = (constraints.maxHeight - textPlaceable.height) / 2 // 縦方向の中央配置 ) //はみ出した画像はクリップされる imagePlaceable.placeRelative( //テキストの最後尾に画像を配置 x = textWidth, y = (constraints.maxHeight - imagePlaceable.height) / 2 // 縦方向の中央配置 ) } } スクロールと連動のアニメーション処理 Layout()でスクロールに連動した伸縮のエフェクトも実現できます。 以下のコードは主に、画像の伸縮の処理を行っています。 @Composable private fun CollapsingImageLayout( collapseFraction: Float, modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurables, constraints -> check(measurables.size == 1) val imageMaxSize = min(ExpandedImageSize.roundToPx(), constraints.maxWidth) val imageMinSize = max(CollapsedImageSize.roundToPx(), constraints.minWidth) val imageWidth = lerp(imageMaxSize, imageMinSize, collapseFraction) // measureする val imagePlaceable = measurables[0].measure(Constraints.fixed(imageWidth, imageWidth)) // lerpは2点A,B間の間のベクトルを近似的関数で求めてくれるFunctionです val imageY = lerp(MinTitleOffset, MinImageOffset, collapseFraction).roundToPx() val imageX = lerp( (constraints.maxWidth - imageWidth) / 2, // centered when expanded constraints.maxWidth - imageWidth, // right aligned when collapsed collapseFraction ) // layoutのセット layout( width = constraints.maxWidth, height = imageY + imageWidth ) { imagePlaceable.placeRelative(imageX, imageY) } } } スクロールとの連動方法 ScrollStateを作成し、スクロールの変化量を常に記録します。スクロールの情報を、それぞれBody()やImage()などのComposable関数に渡すことで、全体がスクロールに連動するようになります。 @Composable fun SnackDetail( ... ) { ... Stack(Modifier.fillMaxSize()) { val scroll = rememberScrollState(0f) // ← ここでScrollStateを記録      ... Body(related, scroll)      ... Image(snack.imageUrl, scroll.value)       ... } 他にも様々な実装がサンプルで用意されています 他にもJetsnackサンプル内で様々な動きが実装されていますので、興味のある方は以下のリンクから是非参考にしてみてください! https://github.com/android/compose-samples/tree/master/Jetsnack#features
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む