20210419のAndroidに関する記事は11件です。

【Android】ConstraintLayoutの用法メモ~第二回~【レイアウト】

0.前回記事 前回はConstraintLayoutの考え方とそれを使った単独View、隣接Viewの指定方法を紹介しました。 今回はその他の用法を紹介します 1.斜め配置 【ポイント】 2つのViewの対称な辺を基準に配置するTopに対してBottom,Leftに対してRight 1-1.右斜め上に配置 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintLeft_toRightOf="@+id/tx01" app:layout_constraintBottom_toTopOf="@+id/tx01" /> ②結果 1-2.左斜め下に配置 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toLeftOf="@+id/tx01" app:layout_constraintTop_toBottomOf="@+id/tx01" /> ②結果 2.GuideLineを使用して画面の定位置に表示する 【ポイント】 GuideLineを画面比率で指定した場所に配置し、それを基準にViewを配置する 2-1.画面の上から30%の場所に配置する 【ポイント】 縦の位置を比率で指定したい場合は android:orientation="horizontal" を指定する ①コード <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.3" /> <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="↑30% " android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="@+id/guideline" /> ②結果 2-2.画面の左から30%の場所に配置する 【ポイント】 横の位置を比率で指定したい場合は android:orientation="vertical" を指定する ①コード <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.3" /> <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="←30% " android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@+id/guideline" /> ②結果 2-3.画面の左から65%、上から80%の場所に配置する ①コード <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.8" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.65" /> <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="←65%,↑80% " android:textSize="24sp" android:background="@color/design_default_color_secondary" app:layout_constraintTop_toBottomOf="@+id/guideline01" app:layout_constraintLeft_toRightOf="@+id/guideline02" /> ②結果
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

とある愛知の電脳技師のConstraintLayoutの用法メモ2

0.前回記事 前回はConstraintLayoutの考え方とそれを使った単独View、隣接Viewの指定方法を紹介しました。 今回はその他の用法を紹介します 1.斜め配置 【ポイント】 2つのViewの対称な辺を基準に配置するTopに対してBottom,Leftに対してRight 1-1.右斜め上に配置 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintLeft_toRightOf="@+id/tx01" app:layout_constraintBottom_toTopOf="@+id/tx01" /> ②結果 1-2.左斜め下に配置 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toLeftOf="@+id/tx01" app:layout_constraintTop_toBottomOf="@+id/tx01" /> ②結果 2.GuideLineを使用して画面の定位置に表示する 【ポイント】 GuideLineを画面比率で指定した場所に配置し、それを基準にViewを配置する 2-1.画面の上から30%の場所に配置する 【ポイント】 縦の位置を比率で指定したい場合は android:orientation="horizontal" を指定する ①コード <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.3" /> <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="↑30% " android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="@+id/guideline" /> ②結果 2-2.画面の左から30%の場所に配置する 【ポイント】 横の位置を比率で指定したい場合は android:orientation="vertical" を指定する ①コード <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.3" /> <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="←30% " android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@+id/guideline" /> ②結果 2-3.画面の左から65%、上から80%の場所に配置する ①コード <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.8" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.65" /> <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="←65%,↑80% " android:textSize="24sp" android:background="@color/design_default_color_secondary" app:layout_constraintTop_toBottomOf="@+id/guideline01" app:layout_constraintLeft_toRightOf="@+id/guideline02" /> ②結果
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

android カウンター機能実装

前回のリハビリ投稿から3週間が開きました。 すいません忙しかったんです。許してください。 隙間時間を見つけたのでリハビリしました。 今日はボタンを押すと数字がカウントアップしていくカウンターを作ります! カウンターのレイアウト作成 画面上部にtextViewで文字列"0"を入れています。 Resetボタン:押したらカウントリセット Count:押したら数値を1つづつあげていく BackButton:MainActivityに戻る CounterActivity integer型 countを定義 String型 stringCountを定義 textView.setText countの値をtextViewに表示させる ちょっとめんどくさいことをしているので、解説 int型のcountをわざわざStringにして使っていますが、これはtextViewがString型を扱うためです。 実際に動かすと以下のようになります。 pic.twitter.com/SmJqOPP1NZ— ラボ (@ZDGr79Wk34Lyiy5) April 19, 2021
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

View の状態を正しく保存する

1. はじめに Activity が破棄されたときや画面回転が行われたときに、ビューの状態が保存できていないとユーザが入力したり選択した内容が復元されません。 Activity の破棄は、端末設定の System -> Developer options -> Don't keep activities を有効にすることで、アプリをバックグラウンドにするだけで簡単に確認が可能です。 本記事では、このような状態の消失を正しく防ぐ方法を記述します。 事故例 入力が消える 全部同じになる まとめると? View にid を振ろう dispatchFreezeSelfOnly, dispatchThawSelfOnlyをうまく使おう CustomView ではSavedState を作ると良さそう 環境 Android Studio 4.1.1 kotlin 1.4.32 2. 単一のレイアウトの場合 次のようにView がid なしで定義されているとき、最初に挙げた「入力が消える」動画のように状態は保持されません。 fragment_no_id.xml <layout ...> <LinearLayout ...> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp" /> <EditText android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="Type anything" /> </LinearLayout> </layout> ここに、id を振ってやると、それだけで状態が保持されます。 fragment_with_id.xml <layout ...> <LinearLayout ...> <CheckBox android:id="@+id/checkBox" ... /> <EditText android:id="@+id/editText" ... /> </LinearLayout> </layout> どうなってる? CheckBoxの継承元であるCompoundButtonを見てみると、onSaveInstanceStateとonRestoreInstanceState で値の保存と復元を行っています。 CompoundButton.java @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.checked = isChecked(); return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setChecked(ss.checked); requestLayout(); } この二つの関数は、View#dispatchSaveInstanceState,View#dispatchRestoreInstanceStateを見るとわかるように、View にid が振られていないと呼ばれません。 View.java protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) { ... Parcelable state = onSaveInstanceState(); ... if (state != null) { ... container.put(mID, state); } } } protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { if (mID != NO_ID) { Parcelable state = container.get(mID); if (state != null) { ... onRestoreInstanceState(state); ... } } } ここで、mIDはView 自身のid を表しています。 onSaveInstanceStateで返したParcelableはSparseArray<Parcelable>にid をキーとして保存され、onRestoreInstanceStateには同様にid をキーとして得られたParcelableが渡されます。 3. CustomView を使う場合 単純な場合にはView にid を振るだけでいいことがわかりました。 では、これらのView をまとめてCustomView とし、これを複数並べたときの問題について見てみます。 まず、適当にCustomView を用意します。 MyView MyView.kt class MyView : FrameLayout { private val binding: MyViewBinding = MyViewBinding.inflate(LayoutInflater.from(context), this, true) constructor(context: Context) : this(context, null) constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) } my_view.xml <layout ...> <androidx.constraintlayout.widget.ConstraintLayout ...> <CheckBox android:id="@+id/checkBox" ... /> <EditText android:id="@+id/editText" ... /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> そして、これを並べたレイアウトを作ります。 fragment_three_my_view.xml <layout ...> <androidx.constraintlayout.widget.ConstraintLayout ...> <io.github.warahiko.testrestorecustomviewapp.MyView android:id="@+id/myView1" ... /> <io.github.warahiko.testrestorecustomviewapp.MyView android:id="@+id/myView2" ... /> <io.github.warahiko.testrestorecustomviewapp.MyView android:id="@+id/myView3" ... /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> その結果が、最初に挙げた事故の二つ目です。全て同じになってしまいました。 どうなってる? 各View のid を確認してみます。 MyView.kt constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) { Log.i("idCheck", "checkBox: ${binding.checkBox.id.toString(16)}") Log.i("idCheck", "editText: ${binding.editText.id.toString(16)}") } 結果は次のようになりました。 I/idCheck: checkBox: 7f080069 I/idCheck: editText: 7f0800a1 I/idCheck: checkBox: 7f080069 I/idCheck: editText: 7f0800a1 I/idCheck: checkBox: 7f080069 I/idCheck: editText: 7f0800a1 一致してますね。 先ほど確認したように、Parcelableはid をキーとして保存されるため、保存の度に上書きされてしまいます。 その結果、最後に保存された状態が全てのMyView に復元されてしまったわけです。 どうする? onSaveInstanceState, onRestoreInstanceStateを自分で実装してしまいます。 MyView.kt class MyView : FrameLayout { ... override fun onSaveInstanceState(): Parcelable { return Bundle().apply { putParcelable(KEY_SUPER, super.onSaveInstanceState()) putString(KEY_TEXT, binding.editText.text.toString()) putBoolean(KEY_IS_CHECKED, binding.checkBox.isChecked) } } override fun onRestoreInstanceState(state: Parcelable?) { if (state is Bundle) { val superState = state.getParcelable<Parcelable>(KEY_SUPER) super.onRestoreInstanceState(superState) binding.editText.setText(state.getString(KEY_TEXT)) binding.checkBox.isChecked = state.getBoolean(KEY_IS_CHECKED) } else { super.onRestoreInstanceState(state) } } companion object { private const val KEY_SUPER = "keySuper" private const val KEY_TEXT = "keyText" private const val KEY_IS_CHECKED = "keyIsChecked" } } しかし、これだけではうまくいきません。 ViewGroup#dispatchSaveInstanceState, ViewGroup#dispatchRestoreInstanceState を見てみると、自身の処理の後に子View の処理をしています。 ViewGroup.java @Override protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { super.dispatchSaveInstanceState(container); final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { View c = children[i]; if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { c.dispatchSaveInstanceState(container); } } } @Override protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { super.dispatchRestoreInstanceState(container); final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { View c = children[i]; if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { c.dispatchRestoreInstanceState(container); } } } そのため、自身のdispatchRestoreInstanceStateをoverride しても子View での処理で上書きされてしまいます。 そこで、自身だけの処理を行うdispatchFreezeSelfOnly, dispatchThawSelfOnlyを使います。 ViewGroup.java protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) { super.dispatchSaveInstanceState(container); } protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) { super.dispatchRestoreInstanceState(container); } これを使って、MyView#dispatchSaveInstanceState, MyView#dispatchRestoreInstanceState もoverride します。 MyView.kt override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>?) { dispatchFreezeSelfOnly(container) } override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>?) { dispatchThawSelfOnly(container) } これにより復元がうまくできるようになりました 失敗例 API level 17 からView.generateViewId というメソッドが追加されています。 これは、レイアウト側で追加したid とぶつからない新しいid を生成します。 view.id = View.generateViewId() これを使うことでview に新しいid を設定できますが、 複数のview がある場合その分の処理が必要 コンストラクタに記述しても、再生成時にはコンストラクタが走ってまた違うid になってしまうため復元できない ので、今回の用途には適していないと思われます。 4. さらにネストされている場合 以下のようなCustomView を作ってみます。 MySecondView MySecondView.kt class MySecondView : FrameLayout { constructor(context: Context) : this(context, null) constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) { MySecondViewBinding.inflate(LayoutInflater.from(context), this, true) } } my_second_view.xml <layout ...> <androidx.constraintlayout.widget.ConstraintLayout ...> <androidx.appcompat.widget.SwitchCompat android:id="@+id/switchCompat" ... /> <io.github.warahiko.testrestorecustomviewapp.MyView android:id="@+id/myView" ... /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> そして、これを並べたレイアウトを作ります。 fragment_nested.xml fragment_nested.xml <layout ...> <androidx.constraintlayout.widget.ConstraintLayout ...> <io.github.warahiko.testrestorecustomviewapp.MySecondView android:id="@+id/mySecondView1" ... /> <io.github.warahiko.testrestorecustomviewapp.MySecondView android:id="@+id/mySecondView2" ... /> <io.github.warahiko.testrestorecustomviewapp.MySecondView android:id="@+id/mySecondView3" ... /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> これもやはりうまく状態を復元することができません。 SwitchCompat, MyView のid がそれぞれ全て一致しているため、onRestoreInstanceStateには同じ(最後に保存された)インスタンスが渡されてきます。 先ほどと同じようにして、MyViewの状態もMySecondViewで保存・復元をすることもできますが、わざわざそのような依存を作らずにMyViewの実装を使いたいですね。 しかし、onSaveInstanceStateとdispatchSaveInstanceStateはprotectedが付いているため呼ぶことができません。 ここで、渡したSparseArray<Parcelable>で値を保持・復元できるsaveHierarchyState,restoreHierarchyState が使えます。 View.java public void saveHierarchyState(SparseArray<Parcelable> container) { dispatchSaveInstanceState(container); } public void restoreHierarchyState(SparseArray<Parcelable> container) { dispatchRestoreInstanceState(container); } まず、これを使ったViewGroupの拡張関数を定義します。 ViewGroupExt.kt fun ViewGroup.saveChildStates(): SparseArray<Parcelable> { val childStates = SparseArray<Parcelable>() children.forEach { it.saveHierarchyState(childStates) } return childStates } fun ViewGroup.restoreChildStates(childStates: SparseArray<Parcelable>) { children.forEach { it.restoreHierarchyState(childStates) } } これらを用いて、MySecondViewの各関数をoverride していきます。 MySecondView.kt override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>?) { dispatchFreezeSelfOnly(container) } override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>?) { dispatchThawSelfOnly(container) } override fun onSaveInstanceState(): Parcelable { return Bundle().apply { putParcelable(KEY_SUPER, super.onSaveInstanceState()) putSparseParcelableArray(KEY_CHILDREN, saveChildStates()) } } override fun onRestoreInstanceState(state: Parcelable?) { if (state is Bundle) { val superState = state.getParcelable<Parcelable>(KEY_SUPER) super.onRestoreInstanceState(superState) val childStates = state.getSparseParcelableArray<Parcelable>(KEY_CHILDREN) ?: return restoreChildStates(childStates) } else { super.onRestoreInstanceState(state) } } companion object { private const val KEY_SUPER = "keySuper" private const val KEY_CHILDREN = "keyChildren" } これにより、うまく復元することができました 5. SavedState TextViewやCompoundButtonでのonSaveInstanceState, onRestoreInstanceStateでは、Bundleの代わりにBaseSavedStateを継承したSavedStateというクラスが実装されています。 View.BaseSavedState には Base class for derived classes that want to save and restore their own state in {@link android.view.View#onSaveInstanceState()}. とあるので推奨されているんですかね。 使い方としては、以下のように親クラスのonSaveInstanceStateの結果を渡してインスタンス化し、自身のonSaveInstanceStateの返値とします。 再生成時には、メンバであるsuperStateを親クラスのonRestoreInstanceStateに渡します。 MyThirdView.kt class MyThirdView : FrameLayout { ... override fun onSaveInstanceState(): Parcelable { return SavedState(super.onSaveInstanceState(), saveChildStates()) } override fun onRestoreInstanceState(state: Parcelable?) { if (state is SavedState) { super.onRestoreInstanceState(state.superState) state.childStates?.let { restoreChildStates(it) } } else { super.onRestoreInstanceState(state) } } internal class SavedState : BaseSavedState { var childStates: SparseArray<Parcelable>? = null private set constructor(superState: Parcelable?) : super(superState) constructor(superState: Parcelable?, childStates: SparseArray<Parcelable>) : super(superState) { this.childStates = childStates } constructor(source: Parcel) : super(source) { childStates = source.readSparseArray(javaClass.classLoader) } override fun writeToParcel(out: Parcel, flags: Int) { super.writeToParcel(out, flags) out.writeSparseArray(childStates) } companion object { @JvmField val CREATOR = object : Parcelable.Creator<SavedState> { override fun createFromParcel(source: Parcel): SavedState = SavedState(source) override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size) } } } } ここで、親クラスの処理を行わないと、例えばViewで行われているActivityResult を受け取るための情報や、autofill の情報の復元が行われません。 また、 書き込み時(writeToParcel)と読み込み時(constructor(source: Parcel))の順番を間違える CREATORを実装しない とそれぞれ実行時に落ちてしまうので注意が必要です。 実装は多少面倒ですが、このクラスを用いる利点として、親クラスの処理を忘れにくいこと、キーを用意してやるが必要ないことが挙げられます。1 6. おわりに この現象について調べることになったきっかけとして、同じ画面に複数のViewPagerを配置しようとして、復元時に同じ画面になってしまったことがありました。 このあたりは浅く触っているとスルーしがちだと思うのでしっかり理解していきたいです。 今回作成したサンプルをこちらに置いておきます。 参考 https://www.netguru.com/codestories/how-to-correctly-save-the-state-of-a-custom-view-in-android 大いに参考にさせていただきました https://y-anz-m.blogspot.com/2010/03/androidparcelable.html Parcelable の作り方について https://y-anz-m.blogspot.com/2012/04/androidfragment-view.html https://qiita.com/KazaKago/items/758076137e8d4a962dd0#5-%E7%94%BB%E9%9D%A2%E5%9B%9E%E8%BB%A2%E7%AD%89%E3%81%AEactivity%E5%86%8D%E7%94%9F%E6%88%90%E5%AF%BE%E5%BF%9C このブログにはmore intelligent memory managementとあり、画面回転時にはスパース配列に保存されずメモリに保持されるということが書かれていますが、これはBundleではMapに、SavedStateではメンバとして保持されているくらいの違いしかないと思っています。有識者に教えてほしい。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin】2021年版インタースティシャル広告の実装

はじめに 久しぶりにインタースティシャル広告を実装しようとしたらInterstitialAdがdeprecatedされていたので更新しました。まだ日本語のドキュメントが更新されていないようでしたので備忘録も兼ねて残しておこうと思います。 変なところがあれば教えていただければ幸いです。 AdMob側の操作は割愛します。 build.gradle(app) implementation 'com.google.android.gms:play-services-ads:19.8.0' //19.7.0以降でdeorecatedのようです。 実装 MainActivity.kt import com.google.android.gms.ads.interstitial.InterstitialAd//新しい方 private var interstitial:InterstitialAd? = null private val adInter = "ca-app-pub-3940256099942544/1033173712"//テスト用です InterstitialAdはnull許容でいいみたいで、onResume等でロードしコールバックさせます。 MainActivity.kt override fun onResume() { super.onResume() loadInterstitial() } private fun loadInterstitial(){//インタースティシャル、ロード val adRequest = AdRequest.Builder().build() InterstitialAd.load(this,adInter,adRequest,object : InterstitialAdLoadCallback(){ override fun onAdLoaded(p0: InterstitialAd) {//広告ロード成功時 interstitial = p0 interstitial?.fullScreenContentCallback = object : FullScreenContentCallback(){ override fun onAdDismissedFullScreenContent() {//広告を閉じた時 } override fun onAdShowedFullScreenContent() {//広告表示成功時 interstitial = null } } } override fun onAdFailedToLoad(p0: LoadAdError) {//広告ロード失敗 interstitial = null } }) } 広告の表示成功時も次の広告のためnullにしています。 あとは任意のタイミングでインタースティシャルをShowします。 MainActivity.kt private fun showInterstitial(){ if (interstitial != null){//ロードできていれば interstitial?.show(this) } } おしまい リスナーからコールバックに変わっただけで仕様自体はたいして変わっていないようでした。 それにしても日本語のドキュメントが中々更新されないのはどうにかしてほしいですよね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]RecyclerView の ConcatAdapter を使う方法

はじめに 例えば今までの RecyclerView.Adapter を利用した実装では "ヘッダーを表示する View" と "コンテンツを表示する View" を組み合わせて表示するとなると大変だったんですが ConcatAdapter を利用すると簡単に実装できるらしいです。 ConcatAdapter を利用して "ヘッダーを表示する View" と "コンテンツを表示する View" を組み合わせた表示どうやったらできるのか以下のサンプルを作って調べたのでまとめたいと思います。 セットアップ ConcatAdapter は Version 1.2.0-alpha03 から MergerAdapter という名称で追加されたあと、Version 1.2.0-alpha04 にて今の ConcatAdapter という名称に変更されました。なので ConcatAdapter を使うには Version 1.2.0-alpha04 以上のバージョンを使う必要があります。今回は現時点での最新版である Version 1.2.0-rc01 を使っていこうかと思います。 dependencies { ︙ implementation "androidx.recyclerview:recyclerview:1.2.0-rc01" ︙ } 表示するデータを宣言する User という firstName と lastName, age を持つデータクラスと Category という title を持つデータクラスを定義します。今回はこの User と Category に定義したデータを ConcatAdapter を利用して一つの RecyclerView で表示できるようにします。 data class User(val firstName: String, val lastName: String, val age: Int) { val id = UUID.randomUUID().toString() } data class Category(val title: String) { val id = UUID.randomUUID().toString() } 表示する View を宣言する 今回は viewBinding を利用したいので buildFeatures で viewBinding を true にしておきます。 android { ︙省略 buildFeatures { viewBinding true } ︙省略 } Category を表示する View を宣言します。layout_category_item.xml というファイルに以下の定義をして View として title を表示できるようにしておきます。 <?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="wrap_content" xmlns:tools="http://schemas.android.com/tools"> <TextView android:id="@+id/category_title_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="48sp" tools:text="CATEGORY" /> </FrameLayout> User を表示する View を宣言しておきます。layout_user_item.xml というファイルに以下の定義をして定義して firstName や lastName, age を表示できるようにしておきます。 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content" android:layout_margin="8dp" android:background="#eeeeee" android:padding="8dp"> <TextView android:id="@+id/age_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="64sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/first_name_text_view" app:layout_constraintTop_toTopOf="parent" tools:text="100" /> <TextView android:id="@+id/first_name_text_view" android:layout_width="0dp" android:layout_height="wrap_content" android:textSize="32sp" app:layout_constraintBottom_toTopOf="@id/last_name_text_view" app:layout_constraintEnd_toStartOf="@id/age_text_view" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="FIRST NAME" /> <TextView android:id="@+id/last_name_text_view" android:layout_width="0dp" android:layout_height="wrap_content" android:textSize="32sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/age_text_view" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/first_name_text_view" tools:text="LAST NAME" /> </androidx.constraintlayout.widget.ConstraintLayout> 表示を制御するアダプターを作成する ConcatAdapter に追加する RecyclerView.Adapter を作成します。Category を表示する CategoryAdapter、User を表示する UserListAdapter の 2つの RecyclerView.Adapter を定義します。 class CategoryItemViewHolder( private val binding: LayoutCategoryItemBinding ) : RecyclerView.ViewHolder(binding.root) { fun bind(category: Category) { binding.categoryTitleTextView.text = category.title } } val DIFF_UTIL_CATEGORY_ITEM_CALLBACK = object : DiffUtil.ItemCallback<Category>() { override fun areContentsTheSame(oldItem: Category, newItem: Category): Boolean { return oldItem == newItem } override fun areItemsTheSame(oldItem: Category, newItem: Category): Boolean { return oldItem.id == newItem.id } } class CategoryAdapter : ListAdapter<Category, CategoryItemViewHolder>(DIFF_UTIL_CATEGORY_ITEM_CALLBACK) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryItemViewHolder { val view = LayoutCategoryItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) return CategoryItemViewHolder(view) } override fun onBindViewHolder(holder: CategoryItemViewHolder, position: Int) { holder.bind(getItem(position)) } } class UserItemViewHolder( private val binding: LayoutUserItemBinding ) : RecyclerView.ViewHolder(binding.root) { fun bind(user: User) { binding.firstNameTextView.text = user.firstName binding.lastNameTextView.text = user.lastName binding.ageTextView.text = user.age.toString() } } val DIFF_UTIL_USER_ITEM_CALLBACK = object : DiffUtil.ItemCallback<User>() { override fun areContentsTheSame(oldItem: User, newItem: User): Boolean { return oldItem == newItem } override fun areItemsTheSame(oldItem: User, newItem: User): Boolean { return oldItem.id == newItem.id } } class UserListAdapter : ListAdapter<User, UserItemViewHolder>(DIFF_UTIL_USER_ITEM_CALLBACK) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserItemViewHolder { val view = LayoutUserItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) return UserItemViewHolder(view) } override fun onBindViewHolder(holderUser: UserItemViewHolder, position: Int) { holderUser.bind(getItem(position)) } } ConcatAdapter を使ってデータを表示する あとは RecyclerView を定義して、作成した RecyclerView.Adapter を ConcatAdapter にセットすればOKです。 ConcatAdapter.add にて ReyclerView.Adapter を追加する ConcatAdapter.add で追加した RecyclerView.Adapter の submitList を呼び出す class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) setupConcatAdapter() val categoryAdapter = CategoryAdapter() val userListAdapter = UserListAdapter() val concatAdapter = ConcatAdapter().apply { addAdapter(categoryAdapter) addAdapter(userListAdapter) } binding.recyclerView.adapter = concatAdapter binding.recyclerView.layoutManager = LinearLayoutManager(applicationContext, RecyclerView.VERTICAL, false) categoryAdapter.submitList(listOf(Category("人名図鑑"))) userListAdapter.submitList( listOf( User("あいざわ", "かずき", 29), User("ふじくら", "まさひろ", 52), User("よしずみ", "ひろゆき", 54), User("ほりのうち", "しんいち", 40), User("はすぬま", "よしろう", 37), User("はなわ", "のぶお", 38), User("おじま", "おじま", 31), User("しんざき", "くにひと", 35) ) ) } } <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp" /> </androidx.constraintlayout.widget.ConstraintLayout> どうやら ConcatAdapter.add を実行した順で表示されるようです。例えば次のように UserListAdapter を追加した後に CategoryAdapter を追加すると順番が逆になります。 class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) setupConcatAdapter() val categoryAdapter = CategoryAdapter() val userListAdapter = UserListAdapter() val concatAdapter = ConcatAdapter().apply { addAdapter(userListAdapter) addAdapter(categoryAdapter) } binding.recyclerView.adapter = concatAdapter binding.recyclerView.layoutManager = LinearLayoutManager(applicationContext, RecyclerView.VERTICAL, false) categoryAdapter.submitList(listOf(Category("人名図鑑"))) userListAdapter.submitList( listOf( User("あいざわ", "かずき", 29), User("ふじくら", "まさひろ", 52), User("よしずみ", "ひろゆき", 54), User("ほりのうち", "しんいち", 40), User("はすぬま", "よしろう", 37), User("はなわ", "のぶお", 38), User("おじま", "おじま", 31), User("しんざき", "くにひと", 35) ) ) } } おわりに というように ConcatAdapter を使うと簡単にヘッダー付きやフッダー付きの RecyclerView を作成できます。以前はヘッダーやフッダーをつけようとすると RecyclerView.Adapter が複雑化してしまうことがありましたがこれなら簡単にできるかなと思います。 しかし ConcatAdapter は RecyclerView.Adapter を結合するだけなので複雑な表示をしたいならば Epoxy や Groupie を使うのが良さそうだと思いました。まだまだ公式が提供する Adapter だけでは実装が難しい表示があるのは変わらないですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]RecyclerView の ListAdapter を viewBinding と組み合わせて使う方法

はじめに RecyclerView で ViewBinding と ListAdapter を組み合わせて使う方法について調べたのでまとめます。 表示するデータを宣言する はじめに User という firstName と lastName, age を持つデータクラスを定義します 今回はこの User に定義したデータを RecyclerView で表示できるようにします。 data class User(val firstName: String, val lastName: String, val age: Int) { val id = UUID.randomUUID().toString() } 表示する View を宣言する RecyclerView に表示する View を宣言する前に buildFeatures で viewBinding を true にしておきます。これで viewBinding が有効になり viewBinding を使った View の生成ができるようになります。 android { ︙省略 buildFeatures { viewBinding true } ︙省略 } 今回は View を layout_user_item.xml というファイルに定義しておきます。 以下の通り View として firstName や lastName, age を表示できるようにしておきます。 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content" android:layout_margin="8dp" android:background="#eeeeee" android:padding="8dp"> <TextView android:id="@+id/age_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="64sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/first_name_text_view" app:layout_constraintTop_toTopOf="parent" tools:text="100" /> <TextView android:id="@+id/first_name_text_view" android:layout_width="0dp" android:layout_height="wrap_content" android:textSize="32sp" app:layout_constraintBottom_toTopOf="@id/last_name_text_view" app:layout_constraintEnd_toStartOf="@id/age_text_view" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="FIRST NAME" /> <TextView android:id="@+id/last_name_text_view" android:layout_width="0dp" android:layout_height="wrap_content" android:textSize="32sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/age_text_view" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/first_name_text_view" tools:text="LAST NAME" /> </androidx.constraintlayout.widget.ConstraintLayout> 表示を制御するアダプターを作成する 次に RecyclerView の表示を制御する RecyclerView.Adapter を作成します。以前は RecyclerView.Adapter を一から実装する必要がありかなり大変だったのですが今は ListAdapter という RecyclerViewAdapter の実装を楽にしてくれるクラスが公式から提供されています。 List Adapter には以下の特徴がありメリットがあるアダプターですが次のように実装します。 RecyclerView.Adapter は RecyclerView でデータを表示を制御するために必要なクラス ReccylerView.Adapter をより簡単に実装できるようにしたものが ListAdater というクラス また ListAdapter では差分更新の仕組みがサポートされていて、簡単に差分更新が実装できるようになっている。 /** * ListAdapter<A: Object, B: RecyclerView.ViewHolder>(C: DiffUtils.ItemCallback<A>) を継承する * 今回は User を表示するので、User を表示するための View を保持した ViewHolder と User の差分を比較するための ItemCallback をセットしてやります。 * * - A:Object には表示するデータを保持するクラスをセットする * - B:RecyclerView.ViewHolder には表示する View を保持する ViewHolder をセットする * - C:DiffUtils.ItemCallback<A> には A:Object の差分確認方法を実装した ItemCallback をセットする */ class UserListAdapter : ListAdapter<User, UserItemViewHolder>(DIFF_UTIL_ITEM_CALLBACK) { // ここで View を生成する、生成した View は ViewHolder に格納して、戻り値として返す override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserItemViewHolder { val view = LayoutUserItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) return UserItemViewHolder(view) } // その位置の User を取得し、ViewHolder を通じて View に User 情報をセットする override fun onBindViewHolder(holderUser: UserItemViewHolder, position: Int) { holderUser.bind(getItem(position)) } } /** * 生成した View を保持する、 bind で User を View に反映させる */ class UserItemViewHolder( private val binding: LayoutUserItemBinding ) : RecyclerView.ViewHolder(binding.root) { fun bind(user: User) { binding.firstNameTextView.text = user.firstName binding.lastNameTextView.text = user.lastName binding.ageTextView.text = user.age.toString() } } /** * User の差分確認する */ val DIFF_UTIL_ITEM_CALLBACK = object : DiffUtil.ItemCallback<User>() { // 渡されたデータが同じ値であるか確認をする override fun areContentsTheSame(oldItem: User, newItem: User): Boolean { return oldItem == newItem } // 渡されたデータが同じ項目であるか確認する override fun areItemsTheSame(oldItem: User, newItem: User): Boolean { return oldItem.id == newItem.id } } RecyclerView にデータを表示する 表示するデータ、表示する View、表示を制御するアダプターが完成したので、 あとは RecyclerView を定義して、作成した ListAdapter をセットすればOKです。 そして ListAdapter の submitList を呼び出せばセットしたデータが RecyclerView に表示されます。 class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val listAdapter = UserListAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.recyclerView.adapter = listAdapter binding.recyclerView.layoutManager = LinearLayoutManager(applicationContext, RecyclerView.VERTICAL, false) listAdapter.submitList( listOf( User("あいざわ", "かずき", 29), User("ふじくら", "まさひろ", 52), User("よしずみ", "ひろゆき", 54), User("ほりのうち", "しんいち", 40), User("はすぬま", "よしろう", 37), User("はなわ", "のぶお", 38), User("おじま", "おじま", 31), User("しんざき", "くにひと", 35) ) ) } } <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp" /> </androidx.constraintlayout.widget.ConstraintLayout> おわりに 以前は RecyclerView を実装したコードを記述するとなるとかなり大変なイメージでしたが、 ViewBinding と ListAdapter を組み合わせて使うことでかなりスマートな実装になりますね。 Android 公式ライブラリでここまでできるようになっているのは感動ですね。 現在の Android 開発では Epoxy や Groupie などのライブラリがよく使われると思いますが、 ListAdapter がここまで簡単に使えるとなるとこの類のライブラリを使うメリットは薄れてきますね。個人的には Epoxy に苦しめられることが多いので公式ライブラリだけで実装できる未来が来ることを願っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Flutter2】ElevatedButtonとTextButtonのデザイン12種類をまとめて紹介!

Flutter2へのアップデートに伴ってRaisedButtonとFlatButtonが非推奨になりました。その代わりとしてElevatedButtonとTextButtonが推奨になったので、今回はElevatedButtonとTextButtonのデザインをまとめて紹介します。 1.背景に色を付ける ElevatedButton( onPressed: () {}, child: Text( "背景", ), style: ElevatedButton.styleFrom( primary: Colors.red, ), 2.アイコン付きボタン ElevatedButton.icon( onPressed: () {}, label: Text('アイコン'), icon: Icon(Icons.star), ), 3.非活性のボタン ElevatedButton( onPressed: null, child: Text('押せないボタン'), ), 4.横丸ボタン ElevatedButton( onPressed: () {}, child: Text('横丸ボタン'), style: ElevatedButton.styleFrom( shape: StadiumBorder(), ), ), 5.サイズの大きいボタン ElevatedButton( onPressed: () {}, child: Text('大きいボタン'), style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.all(30)), textStyle: MaterialStateProperty.all(TextStyle(fontSize: 25)), ), ), 6.影がついているボタン ElevatedButton( onPressed: () {}, child: Text('影付きボタン'), style: ElevatedButton.styleFrom(elevation: 10), ), 7.丸いボタン ElevatedButton( onPressed: () {}, child: Text('丸'), style: ElevatedButton.styleFrom(shape: CircleBorder()), ), 8.外枠付きのボタン ElevatedButton( onPressed: () {}, child: Text( "外枠", style: TextStyle(color: Colors.blue), ), style: ElevatedButton.styleFrom( primary: Colors.white, side: BorderSide( color: Colors.blue, width: 2, ), ), ), 9.外枠付きの横丸ボタン ElevatedButton( onPressed: () {}, child: Text( "外枠付き横丸", style: TextStyle(color: Colors.blue), ), style: ElevatedButton.styleFrom( shape: StadiumBorder(), primary: Colors.white, side: BorderSide( color: Colors.blue, width: 2, ), ), ), 10.文字ボタン TextButton( onPressed: () {}, child: Text('文字ボタン'), style: ButtonStyle( foregroundColor: MaterialStateProperty.all<Color>(Colors.blue), ), ), 11.外枠付きの文字ボタン OutlinedButton( onPressed: () {}, child: Text('外枠付き文字ボタン'), style: OutlinedButton.styleFrom( side: BorderSide(width: 2, color: Colors.blue), ), ), 12.横丸の文字ボタン OutlinedButton( onPressed: () {}, child: Text('横丸文字ボタン'), style: OutlinedButton.styleFrom( shape: StadiumBorder(), side: BorderSide(width: 2, color: Colors.blue), ), ),
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidエミュレータ(AVD)からローカルのwebサーバにアクセス

Androidエミュレータ(AVD)内のブラウザから ホストOS側で実行しているローカルwebサーバにアクセスする方法のメモです ・hostsファイルで任意ドメインをローカルに割り当てている場合 ・mkcertで発行したSSL証明書を使ってSSL化している場合 についても補足します ※例示しているドメイン名(example.com)や、 webサーバの起動コマンド(http-server)は適宜読み替えてください ※わかりにくい点などありましたら、ご指摘いただけるとありがたいです 前提 Android Studio をインストールし 任意AndroidOSバージョンのAVD作成済みであることを前提にします 以下の記事もよかったらご参照ください ・Android Studio のインストールとAVD作成 AVD内のブラウザからホスト側にhttpアクセス AVD内からはホストOS側のIPアドレスは 10.0.2.2 になります ホスト側でwebサーバを起動 NodeJSで動作するhttp-serverを利用する例 mkdir public echo "<body>Hello,World!</body>" > ./public/index.html npm install -g http-server http-server -p 80 ./public PHPの組み込み開発サーバを使う例 mkdir public echo "<body>Hello,World!</body>" > ./public/index.html php -S 0.0.0.0:80 -t ./public エミュレータを起動してブラウザで http://10.0.2.2/ を開く 任意ドメインでアクセスできるようにする ホスト側でhostsを書き換え任意ドメインでローカルwebサーバにアクセスできるようにしている場合 そのままではAVD内のブラウザから同様にアクセスすることができません AVD内のhostsファイルにも設定する必要があります 準備: Android SDK のコマンドラインツールにPathを通す 起動オプションを指定してエミュレータを起動したり AVD内のファイルを書き換えるためadbコマンドを使えるように Android SDK インストールディレクトリ配下の platform-tools と emulator を環境変数Pathに追加 macOS .zshrc ANDROID_HOME=$HOME/Library/Android/sdk PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator export PATH Windows SDKは C:\Users\(ユーザ名)\AppData\Local\Android\Sdk あたりにインストールされているはずです 環境変数はシステムのプロパティで設定できます (スタートメニューのファイル名を指定して実行でSystemPropertiesAdvancedで起動できます 詳細設定タブ→環境変数押下→(ユーザ名)のユーザ環境変数の欄からPathを選んで編集押下) 以下2つをユーザー環境変数 Pathに追加します C:\Users\(ユーザ名)\AppData\Local\Android\Sdk\emulator C:\Users\(ユーザ名)\AppData\Local\Android\Sdk\platform-tools AVDの hosts を設定 ターミナルから、AVDを起動 通常のAVD ManagerからAVDを起動した場合、hostsなどのシステムファイルに書き込むことができません システムイメージへの書き込みを可能にするオプション-writable-systemをつけてコマンドラインから起動します ※コマンドラインオプションについてはこちらの公式ドキュメントもご参照ください emulator -avd "AVD Name" -writable-system ※"AVD Name"は起動したいAVDの名前に置き換えてください adb コマンドを用いてAVDのhostsを書き換える ※emulatorコマンドを実行したのとは別のターミナルで実行します 接続 adb reconnect 再マウント adb remount hosts取得 adb pull /system/etc/hosts 取得したhostsを編集 (設定したいドメインを 10.0.2.2 に向ける) hosts設定例 127.0.0.1 localhost 10.0.2.2 example.com 編集したhostsをpush adb push hosts /system/etc AVD内のブラウザを起動し http://example.com/ を入力すると アクセスできるようになっていると思います HTTPS(SSL)でアクセスできるようにする 以前紹介したmkcertで発行したSSL証明書を使用してhttpsサーバを起動している場合 AVD内のブラウザから有効な証明書としてアクセスできるようにするには mkcertのルートCA証明書をAVD内にインストールします ルートCAインストール ホスト側のmkcertがインストールしたルート証明書のインストール先を調べる mkcert -CAROOT このディレクトリをwebサーバでホスト http-server -p 8000 -t (調べたディレクトリ) ※ポートは8000でなくてもよいです エミュレータ内のブラウザから http://10.0.2.2:8000/ にアクセス rootCA.pem をダウンロード Certificate name を入力して OK ※エミュレータ内で端末のロックパスワード(パターンやPIN)を設定してない場合、ここで設定を求められる場合があります 試してみる SSL証明書発行 mkcertでexample.comの証明書を発行する例 mkcert --cert-file=crt.pem --key-file=key.pem example.com SSLサーバ起動 http-server -S -C crt.pem -K key.pem -p 443 ./public AVD内のブラウザから https://example.com/ にアクセスすると、SSLでアクセスできました ※http-serverは古いバージョンのSSL(TLS v1.0など)に対応しておらず、 古いAndroidOSバージョンのAVDのブラウザでは証明書エラーになる場合があります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

とある愛知の電脳技師のConstraintLayoutの用法メモ1

0.ConstraintLayoutとは? 0-1.概要 Viewの要素を配置する際に各要素の相関関係により配置場所を決定する。 これにより、画面の解像度や縦横比率の違いによりレイアウト崩れが発生することを抑制できる 0-2.主な各要素の説明 ①右側の指定 app:layout_constraintRight_toRightOf →要素の右側の位置を関連する要素の右側に対して指定する app:layout_constraintRight_toLeftOf →要素の右側の位置を関連する要素の左側に対して指定する ②左側の指定 app:layout_constraintLeft_toLeftOf →要素の左側の位置を関連する要素の左側に対して指定する app:layout_constraintLeft_toRightOf →要素の左側の位置を関連する要素の左側に対して指定する ③上側の指定 app:layout_constraintTop_toTopOf →要素の上側の位置を関連する要素の上側に対して指定する app:layout_constraintTop_toBottomOf →要素の上側の位置を関連する要素の下側に対して指定する ④下側の指定 app:layout_constraintBottom_toBottomOf →要素の下側の位置を関連する要素の下側に対して指定する app:layout_constraintBottom_toTopOf →要素の下側の位置を関連する要素の上側に対して指定する 0-3.注意点 ①上下の内1つ以上かつ左右の内1つ以上の要素を指定しなければエラーになる。 つまり、下記のようなイメージ ・constraintTopとconstraintBottomだけだとエラーになる(左右のどちらかがないから) ・constraintRightとconstraintLeftだけだとエラーになる(上下のどちらかがないから) ・constraintTopとconstraintRightだけだとエラーにならない(上下の内どちらか、左右のうちどちらかが両方あるから) ②特定のタグで囲まれている必要がある <androidx.constraintlayout.widget.ConstraintLayout> 上記のタグで囲まれた要素についてConstraintLayoutのルールが適用される。 下記の内容は画面のレイアウトの最上位から最下部までをこのコードで囲んでいる状態である。 1.中央配置 【ポイント】 両端の要素を指定することで指定した要素の中央にViewを配置できる 1-1.上下左右中央 ①コード <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> ②結果 1-2.上下中央 ①コード <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent" /> ②結果 1-3.左右中央 ①コード <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> ②結果 2.隣配置 【ポイント】 接する面に隣に配置したいViewを指定することでその隣にViewを配置する ここではtextview「tx01(青色)」に対して「tx02(赤色)」をどのように指定すればどのように表示されるかを記載している 2-1.右隣 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintLeft_toRightOf="@+id/tx01" app:layout_constraintTop_toTopOf="@+id/tx01" /> ②結果 2-2.左隣 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toLeftOf="@+id/tx01" app:layout_constraintTop_toTopOf="@+id/tx01" /> ②結果 2-3.上隣 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toRightOf="@+id/tx01" app:layout_constraintBottom_toTopOf="@+id/tx01" /> ②結果 2-4.下隣 <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toRightOf="@+id/tx01" app:layout_constraintTop_toBottomOf="@+id/tx01" /> ②結果 ※ConstraintLayout関連 次回記事※ 【斜め配置】 【GudeLineを使用した配置】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】ConstraintLayoutの用法メモ~第一回~【レイアウト】

0.ConstraintLayoutとは? 0-1.概要 Viewの要素を配置する際に各要素の相関関係により配置場所を決定する。 これにより、画面の解像度や縦横比率の違いによりレイアウト崩れが発生することを抑制できる 0-2.主な各要素の説明 ①右側の指定 app:layout_constraintRight_toRightOf →要素の右側の位置を関連する要素の右側に対して指定する app:layout_constraintRight_toLeftOf →要素の右側の位置を関連する要素の左側に対して指定する ②左側の指定 app:layout_constraintLeft_toLeftOf →要素の左側の位置を関連する要素の左側に対して指定する app:layout_constraintLeft_toRightOf →要素の左側の位置を関連する要素の左側に対して指定する ③上側の指定 app:layout_constraintTop_toTopOf →要素の上側の位置を関連する要素の上側に対して指定する app:layout_constraintTop_toBottomOf →要素の上側の位置を関連する要素の下側に対して指定する ④下側の指定 app:layout_constraintBottom_toBottomOf →要素の下側の位置を関連する要素の下側に対して指定する app:layout_constraintBottom_toTopOf →要素の下側の位置を関連する要素の上側に対して指定する 0-3.注意点 ①上下の内1つ以上かつ左右の内1つ以上の要素を指定しなければエラーになる。 つまり、下記のようなイメージ ・constraintTopとconstraintBottomだけだとエラーになる(左右のどちらかがないから) ・constraintRightとconstraintLeftだけだとエラーになる(上下のどちらかがないから) ・constraintTopとconstraintRightだけだとエラーにならない(上下の内どちらか、左右のうちどちらかが両方あるから) ②特定のタグで囲まれている必要がある <androidx.constraintlayout.widget.ConstraintLayout> 上記のタグで囲まれた要素についてConstraintLayoutのルールが適用される。 下記の内容は画面のレイアウトの最上位から最下部までをこのコードで囲んでいる状態である。 1.中央配置 【ポイント】 両端の要素を指定することで指定した要素の中央にViewを配置できる 1-1.上下左右中央 ①コード <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> ②結果 1-2.上下中央 ①コード <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent" /> ②結果 1-3.左右中央 ①コード <TextView android:id="@+id/tx01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="30sp" android:background="@color/design_default_color_secondary" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> ②結果 2.隣配置 【ポイント】 接する面に隣に配置したいViewを指定することでその隣にViewを配置する ここではtextview「tx01(青色)」に対して「tx02(赤色)」をどのように指定すればどのように表示されるかを記載している 2-1.右隣 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintLeft_toRightOf="@+id/tx01" app:layout_constraintTop_toTopOf="@+id/tx01" /> ②結果 2-2.左隣 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toLeftOf="@+id/tx01" app:layout_constraintTop_toTopOf="@+id/tx01" /> ②結果 2-3.上隣 ①コード <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toRightOf="@+id/tx01" app:layout_constraintBottom_toTopOf="@+id/tx01" /> ②結果 2-4.下隣 <TextView android:id="@+id/tx02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="World!" android:textSize="30sp" android:background="@color/design_default_color_error" app:layout_constraintRight_toRightOf="@+id/tx01" app:layout_constraintTop_toBottomOf="@+id/tx01" /> ②結果 ※ConstraintLayout関連 次回記事※ 【斜め配置】 【GudeLineを使用した配置】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む