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

ZOZOのアプリのUIを再現しようの会 【お気に入りタブ編】その2

前回の記事 はじめに お気に入り画面のアイテムタブを作りました!そこ周りの実装について書いていきます!時系列的には先に新着タブを作っているのですが、先にこちらを記事にしました! 今回の完成像 アディパンしか扱ってないZOZO pic.twitter.com/2QhvDf1upm— ねも (@nemo_855) October 15, 2021 全体の構成 この画面全体の構成として、FavoriteFragmentという親FragmentがViewPager2とTabLayoutを持っています。その中で3つのFragmentを切り替えています。 このアイテムのタブのFragment(以下FavoriteItemFragment)は画面全体がRecyclerViewで作られてます。この画面のようにリストの要素が複数(今回はお気に入りのブランドの登録がありません、今人気のアイテム、一つ一つの商品のセルの3種類)ある場合にはViewHolderを分岐する方法が考えられます。ただViewHolderの分岐はRecyclerView.AdapterもしくはListAdapterのonCreateViewHolderの内部でViewTypeを用いて生成するViewHolderを分岐して、さらにonBindViewHolderでViewHolderごとにbindの処理を分岐するということをやらないといけないです。 そのような処理をしているとコード量が多くなってしまいすぎると思ったので今回はGroupieを採用することにしました!GroupieはRecyclerView.ViewHolderを継承しているBindableItemというクラスを使ってそれをレイアウトの種類ごとに分岐して継承することで直接ViewHolderを扱わなくてもいい感じにRecyclerView内部のレイアウトを分岐してくれるというライブラリです! また差分更新もListAdapterと同じで、DiffUtilという二つのリストの差分を計算してくれるクラスが管理してくれます!(DiffUtilの中でどのような処理がされているかまではわからないです🙇‍♂️勉強します!!)なのでGroupieを使う人は表示したいリストの管理だけしてそれをAdapterに渡すだけでよくなります。 全体のコード FavoriteItemFragment @AndroidEntryPoint class FavoriteItemFragment : Fragment(R.layout.fragment_favorite_item) { companion object { fun newInstance() = FavoriteItemFragment() } private val viewModel: FavoriteItemViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val binding = FragmentFavoriteItemBinding.bind(view) val adapter = CustomGroupieAdapter() fetchClothsAndUpdateList(adapter) setupRecycler(binding, adapter) } private fun fetchClothsAndUpdateList(adapter: CustomGroupieAdapter) { viewModel.clothsLD.observe(viewLifecycleOwner) { cloths -> adapter.updateList(cloths) } viewModel.fetchDisplayClothsList() } private fun setupRecycler( binding: FragmentFavoriteItemBinding, adapter: CustomGroupieAdapter ) { val spanSize = DisplayItemKind.values().maxOf { it.spanSize } val gridLayoutManager = GridLayoutManager(requireContext(), spanSize).also { it.spanSizeLookup = CustomGridSpanSizeLookup(adapter, resources) } binding.favoriteItemRecycler.layoutManager = gridLayoutManager binding.favoriteItemRecycler.adapter = adapter } private class CustomGroupieAdapter : GroupieAdapter() { private var _itemList: List<BindableItem<out ViewBinding>> = makeDefaultList() val itemList: List<BindableItem<out ViewBinding>> get() = _itemList private fun makeDefaultList() = listOf( FavoriteNoItemRegistered(), FavoriteNowPopularItem() ) fun updateList(newItemList: List<FavoriteItemViewModel.DisplayClothsData>) { _itemList = makeDefaultList() + newItemList.map { FavoriteItemDescription(displayData = it) } update(_itemList) } } private class CustomGridSpanSizeLookup( private val adapter: CustomGroupieAdapter, private val res: Resources ) : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { return when (adapter.itemList[position]) { is FavoriteNoItemRegistered -> DisplayItemKind.NO_ITEM.spanSize is FavoriteNowPopularItem -> DisplayItemKind.POPULAR.spanSize is FavoriteItemDescription -> DisplayItemKind.DESCRIPTION.spanSize else -> throw IllegalArgumentException(res.getString(R.string.illegal_class)) } } } private enum class DisplayItemKind(val spanSize: Int) { NO_ITEM(3), POPULAR(3), DESCRIPTION(1) } } またそれぞれのBindableItemを継承したクラスの実装はこんな感じです お気に入りのアイテムがありませんのセル class FavoriteNoItemRegistered : BindableItem<FavoriteItemNoItemRegisteredBinding>() { override fun bind(viewBinding: FavoriteItemNoItemRegisteredBinding, position: Int) { } override fun getLayout() = R.layout.favorite_item_no_item_registered override fun initializeViewBinding(view: View): FavoriteItemNoItemRegisteredBinding { return FavoriteItemNoItemRegisteredBinding.bind(view) } } 今人気のアイテムのセル class FavoriteNowPopularItem : BindableItem<FavoriteItemNowPopularBinding>() { override fun bind(viewBinding: FavoriteItemNowPopularBinding, position: Int) { } override fun getLayout() = R.layout.favorite_item_now_popular override fun initializeViewBinding(view: View): FavoriteItemNowPopularBinding { return FavoriteItemNowPopularBinding.bind(view) } } 一つ一つの商品のセル class FavoriteItemDescription( private val displayData: FavoriteItemViewModel.DisplayClothsData ) : BindableItem<FavoriteItemDescriptionBinding>() { override fun bind(viewBinding: FavoriteItemDescriptionBinding, position: Int) { viewBinding.clothsNameTv.text = displayData.itemName viewBinding.clothsGenreTv.text = displayData.itemGenre viewBinding.clothsPriceTv.text = displayData.itemPrice.toString() viewBinding.mainImage.load(displayData.itemImage) { this.error(R.drawable.ic_android_black_24dp) } when (val percent = displayData.discountPercent) { null -> viewBinding.discountPercentTv.visibility = View.GONE else -> { viewBinding.discountPercentTv.visibility = View.VISIBLE viewBinding.discountPercentTv.text = percent.toString() } } when (val price = displayData.couponPrice) { null -> viewBinding.couponPriceTv.visibility = View.GONE else -> { viewBinding.couponPriceTv.visibility = View.VISIBLE viewBinding.couponPriceTv.text = price.toString() } } } override fun getLayout() = R.layout.favorite_item_description override fun initializeViewBinding(view: View): FavoriteItemDescriptionBinding { return FavoriteItemDescriptionBinding.bind(view) } } このFragmentのViewModel内部に定義している、商品一つ一つの表示データのモデルクラス data class DisplayClothsData( val itemImage: String, val discountPercent: Int?, val couponPrice: Int?, val itemName: String, val itemGenre: String, val itemPrice: Int ) 終わりに 次は一番詰まったCoordinatorLayoutについて記事にしようと思います!!まだ正直理解が曖昧なので頑張ります💪 ハッカソンのメンバー レビュアー: どすこいさん https://twitter.com/dosukoi_android ホームタブ担当(&主催): みっちゃんさん https://twitter.com/mimimi_engineer 探すタブ担当: らべさん https://twitter.com/wawatantanwawa 他2人 リポジトリ 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]プロローグにはコンテンツを指定できません。というエラーを解決する方法について

はじめに こんにちは。システムエンジニアのDiavoloです。 最近、仕事でAndroidStudioを触ることが増えてきました。当方、Androidアプリ開発をがっつり経験したことがなく、またテレワークのため、気軽に聞ける相手がいるわけでもない状態です。 そんな感じで、AndroidStudioの操作にも慣れておらず。トホホ・・・。 そんなこんなで、見慣れぬエラーに苦戦するなかで、Google先生にお伺いをたてても、すぐに解決できなかったエラーなどを備忘録として残しておきたいと考えて、この記事を書いております。 このエラーを解決するのに、2時間もかかってしまいました。 プロローグにはコンテンツを指定できません。 ん?むむむ。さっきまで、エラーが出ていなかったのに、急にこんなエラーが出ました。プロローグって一体。rowとかcolとか、これはエクセルか?と汗 結論。キーボードの誤入力でMainActivity.xmlに余計なsが存在してました <RelativeLayout の先頭に不要な文字 s があることに気付きました。 どうやら、プロジェクトを保存するときに、Alt+Fからのsで保存したつもりが、sを入力していたというオチです。つまらないオチですいません。 ただ、無意識で入力しているので、気付くのに時間がかかりました。 では、また。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】Qiita デイリー LGTM 数ランキング【自動更新】

他のタグ AWS Android Docker Git Go iOS Java JavaScript Linux Node.js PHP Python Rails React Ruby Swift TypeScript Vim Vue.js 初心者 集計について 期間: 2021-10-20 ~ 2021-10-21 GitHub スターをもらえるととっても励みになります LGTM 数ランキング 1 位: Android 12βを正式版に更新する! Android android開発 1 LGTM 0 ストック @kano-ts さん ( 2021-10-20 12:40 に投稿 )
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】スレッド間通信 in Kotlin

➊ はじめに Androidで、スレッド間通信を実現するには、どうすれば良いのか調べました。 ➋ やること スレッド間通信の仕組みをお勉強するために、以下のような簡単なアプリを作ることとしました。 (1) アプリイメージ なんやこれ?スレッド間通信言うてるから来たのに、クリックに合わせて、textView1と2に数字出しとるだけやん!ワシは帰るぞ😠 お待ち下さい。見えませんが、裏でスレッド間通信をやっております。 (2) シーケンス 画面(textView1)をタッチすると、UIスレッドでランダム値(1~100)を生成します。 生成したランダム値を、UIスレッドからSUBスレッドへ送信します。 SUBスレッドでランダム値を受け取り、計算処理(ランダム値に100を足すだけ)を行います。 SUBスレッドで受け取ったランダム値と計算処理した結果を、SUBスレッドからUIスレッドへ送信します。 UIスレッドで、受け取ったランダム値をtextView1に、計算結果をtextView2に表示します。 以下シーケンスになります。 ➌ お勉強 Androidでのスレッド間通信の方法はいくつかあるみたいですが、今回は、LooperとHandlerを使用した方法で実施したいと思います。Looper、Handlerの役割、スレッド間通信のイメージとしては、以下のような感じです。 (1) Threadとは Threadとは、プログラム処理の実行単位です。プログラム処理の実行単位は、プログラマの必要に応じて増減できます。 ■ わざわざThreadを追加する理由 わざわざThreadを追加する理由のひとつとしては、Androidは、シングルスレッドモデルにより、アプリのUIの応答性のためにも、UIスレッドをブロックしないことが不可欠です。即座に実行する必要のない操作の場合は、別のスレッドで実行するようにする必要があります。例えば、画像処理に約10秒かかるとして、これをUIスレッドで実施してしまうと、ユーザからは約10秒間応答が無いように感じられてしまいます。時間がかかる処理がある場合、この様なことを回避するために、別スレッドを作る必要があります。 ■ UIスレッドでなければ、UIの更新はできない 注意としては、基本的に、UIスレッド以外のスレッドからUIを更新することはできない仕様となっているので、UIスレッド以外での処理結果によりUIを更新したい場合は、UIスレッドへメッセージ通知などを行い、それをトリガとしてUIスレッド上からUIを更新する必要があります。 (2) Looperとは Looperとは、メッセージQueueのことです。メッセージQueueとは先入先出法(FIFO:First In First Out)で管理するメッセージバッファのことです。 ※ちなみに、UIスレッドは、Looperを既に持っています(Handlerは持っていません)ので、新規に作る必要がありませんが、スレッドを新規に生成した場合は、Looperは生成されないので、新たに生成する必要があります。 (3) Handlerとは Handlerとは、LooperへQueuingしたり、LooperからQueuingされたメッセージを取り出す役割を持っています。メッセージ送信や、LooperへのQueuing、LooperからのQueue取り出しを簡単にしてくれる便利なモノと考えると良いと思います。Looperとbindして(結びつけて)使用します。 (4) サンプルプログラム snippet1が、SUBスレッドのクラス定義、snippet2が、SUBスレッドを生成するUIスレッド側のサンプルプログラムです。SUBスレッド開始処理(runメソッド)の中で、LooperとHandlerの生成及びバインド、Looper監視開始を行っています。handleMessageをoverrideしておくと、Looperがメッセージ受信した際に、ここが呼び出されるようになります。 snippet1 class SubThread: Thread() { // SUBスレッドClass定義 companion object { const val MSG_NUMBER_1 = 1 // MessageNumber1 } var mHandler: Handler? = null // mHandlerはメッセージ送信のためにPublic設定 override fun run() { // subThread.start()で実行される Looper.prepare() // Looper生成 mHandler = object : Handler(Looper.myLooper()!!) { // Handler生成(LooperとBind) override fun handleMessage(msg: Message) { // ここにメッセージ受信処理を追加します // ... } } Looper.loop() // Looper監視開始 } } snippet2 val subThread = SubThread() // SUBスレッド生成 subThread.start() // SUBスレッド開始 subThread.mHandler?.sendMessage(SubThreadHandler.MSG_NUMBER_1) // SUBスレッドへメッセージ送信 ➍ 実装 さて、いよいよ実装です。以下、2点の仕組みを理解しておけば、なんとなくできちゃいます。(なんとなくかいっ!😅) メッセージ受信側にLooperとHandlerを生成してbindする(UIスレッドはLooper生成済み)。 送信先Handlerのインスタンスが分かれば、どこからでもメッセージ送信できる。 (1) activity_main.kt activity_main.kt package com.poodlemaster.app11 import android.content.Context import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Handler import android.os.Looper import android.os.Message import android.util.Log import android.widget.TextView import kotlin.random.Random import kotlin.random.nextUInt // ------------------------------------------------------------------------------------------------ class MainActivity : AppCompatActivity() { companion object { const val LOGCAT_TAG = "sample.MainActivity" } lateinit var textView1 : TextView lateinit var textView2 : TextView private lateinit var mainHandler : Handler private lateinit var subThreadHandler : Handler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) SysInfo.logThread(LOGCAT_TAG) textView1 = findViewById(R.id.textView1) textView2 = findViewById(R.id.textView2) // MainLooperからのメッセージハンドラをバインド mainHandler = MainHandler(this) // Thread作成&開始 val subThread = SubThread(mainHandler) subThread.start() // textView1クリックイベント待ち textView1.setOnClickListener { Log.d(LOGCAT_TAG, "textView1 Click") val rnd : Int = (Random.nextUInt(100u)+1u).toInt() // rnd = 1~100 Log.d("$LOGCAT_TAG/onCreate/Random(rnd)", rnd.toString()) subThreadHandler = subThread.getHandler()!! sendMessage(subThreadHandler, rnd) } } // ハンドラへメッセージ送信 private fun sendMessage(distHandler : Handler, sendData : Int) { val msg = distHandler.obtainMessage(SubThreadHandler.MSG_SUB_THREAD_HANDLER1) msg.obj = MessageData(sendData, 0) Log.d("$LOGCAT_TAG/sendMessage", distHandler.toString() + "|" + msg.obj.toString()) distHandler.sendMessage(msg) } } // ------------------------------------------------------------------------------------------------ class MainHandler(private val context : Context) : Handler(Looper.getMainLooper()) { companion object { const val LOGCAT_TAG = "sample.MainHandler" const val MSG_MAIN_HANDLER1 = 1 } override fun handleMessage(msg: Message) { var text1 = "textView1 : " var text2 = "textView2 : " SysInfo.logThread("$LOGCAT_TAG/handleMessage") when (msg.what) { MSG_MAIN_HANDLER1 -> { Log.d("$LOGCAT_TAG/handleMessage", "msgId=${msg.what} Receive, obj=${msg.obj}") text1 += (msg.obj as MessageData).intData.toString() text2 += (msg.obj as MessageData).editIntData.toString() } } (context as MainActivity?)!!.textView1.text = text1 (context as MainActivity?)!!.textView2.text = text2 } } // ------------------------------------------------------------------------------------------------ class SubThread(private val mainHandler: Handler) : Thread() { companion object { const val LOGCAT_TAG = "sample.SubThread" } private var subHandler : SubThreadHandler? = null override fun run() { SysInfo.logThread("$LOGCAT_TAG/run") try{ Looper.prepare() // Looper生成 subHandler = SubThreadHandler(mainHandler) // 実行中ThreadのLooperにHandlerを設定 Looper.loop() // MessageQueue監視 } catch (e : InterruptedException) { currentThread().interrupt() Log.d("$LOGCAT_TAG/run", "InterruptedException") } } fun getHandler() : Handler? { return(subHandler) // 送信先ハンドラを教えるために使用 } } // ------------------------------------------------------------------------------------------------ class SubThreadHandler(private val mainHandler : Handler) : Handler(Looper.myLooper()!!) { companion object { const val LOGCAT_TAG = "sample.SubThreadHandler" const val MSG_SUB_THREAD_HANDLER1 = 1 } override fun handleMessage(msg: Message) { SysInfo.logThread("$LOGCAT_TAG/handleMessage") when (msg.what) { MSG_SUB_THREAD_HANDLER1 -> { Log.d("$LOGCAT_TAG/handleMessage", "msgId=${msg.what} Receive, obj=${msg.obj}") val intData : Int = (msg.obj as MessageData).intData val editIntData : Int = (msg.obj as MessageData).intData + 100 sendMessage(intData, editIntData) } } } private fun sendMessage(IntData : Int, EditIntData : Int) { val msg = mainHandler.obtainMessage(MainHandler.MSG_MAIN_HANDLER1) msg.obj = MessageData(IntData, EditIntData) Log.d("$LOGCAT_TAG/sendMessage", mainHandler.toString() + "|" + msg.obj.toString()) mainHandler.sendMessage(msg) } } // ------------------------------------------------------------------------------------------------ data class MessageData(val intData : Int, val editIntData : Int) // ------------------------------------------------------------------------------------------------ class SysInfo { companion object Utility { fun logThread(tag : String) : Long { val threadName = Thread.currentThread().name val threadId = Thread.currentThread().id Log.d(tag, "ThreadName(ThreadId) = %s(%d)".format(threadName, threadId)) return(threadId) } } } (2) activity_main.xml activity_main.xml <?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"> <TextView android:id="@+id/textView1" android:layout_width="371dp" android:layout_height="49dp" android:text="textView1 : Click Me!!" android:textSize="34sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.4" /> <TextView android:id="@+id/textView2" android:layout_width="371dp" android:layout_height="49dp" android:text="textView2 : Hello World!!" android:textSize="34sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="20dp" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_begin="20dp" /> </androidx.constraintlayout.widget.ConstraintLayout> ➎ 概略図 実装の概略図を載せておきます。イメージ伝わるかな? ➏ 実行ログ 分かりやすく加工した実行ログを載せておきます。 logcat(分かりやすく加工済み) MainActivity: ThreadName(ThreadId) = main(2) # UIスレッド(スレッド名:main、スレッドID:2) SubThread/run: ThreadName(ThreadId) = Thread-2(271) # SUBスレッド(スレッド名:Thread-2、スレッドID:271) MainActivity: textView1 Click # UIスレッドでtextView1のクリックを検知 MainActivity/onCreate/Random(rnd): 93 # UIスレッドで生成したランダム値 # UIスレッドからSUBスレッドへメッセージ送信 MainActivity/sendMessage: Handler (com.poodlemaster.app11.SubThreadHandler) {ad160fd}|MessageData(intData=93, editIntData=0) SubThreadHandler/handleMessage: ThreadName(ThreadId) = Thread-2(271) # SUBスレッドでメッセージ受信 SubThreadHandler/handleMessage: msgId=1 Receive, obj=MessageData(intData=93, editIntData=0) # SUBスレッドからUIスレッドへメッセージ送信 SubThreadHandler/sendMessage: Handler (com.poodlemaster.app11.MainHandler) {76191f2}|MessageData(intData=93, editIntData=193) MainHandler/handleMessage: ThreadName(ThreadId) = main(2) # UIスレッドでメッセージ受信 MainHandler/handleMessage: msgId=1 Receive, obj=MessageData(intData=93, editIntData=193) ➐ 以上 とりあえず、スレッド間通信を通して、簡単なマルチスレッドの勉強ができました。 「Android Studio」でkotlin言語を使って簡単なアプリを作れたら楽しいだろうなと思って少し使い始めました。まずは、機能の基礎勉強中です。同士の役に立てれば本望です😉
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android+DataBinding】バインディング式のメソッド呼び出しでデフォルト引数は使えない

問題 タイトルのままです。 DataBindingと式言語(バインディング式)については Android Developers-レイアウトとバインディング式 が詳しいです. 環境 Android Studio 4.1.2 Kotlin version 1.5.10 Android Gradle Plugin 4.1.2 ソース まず、呼び出すメソッドは以下のEvent#formatMessage Event.kt @Serializable data class Event( @SerialName("n") val name: String, @SerialName("t") val message: String ) { override fun toString(): String { return "- $name\n ${formatMessage("\n ")}" } fun formatMessage(separator: String = "\n"): String { val lines = message.split("[br]", "<hr>") return lines.joinToString(separator = separator) } } 呼び出すバインディングレイアウトはこちら. android:text="@{event.formatMessage()}"の部分が問題でビルドが通りません. list_choice_item.xml <?xml version="1.0" encoding="utf-8"?> <layout 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"> <data> <variable name="event" type="jp.seo.uma.eventchecker.model.Event" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="1dp"> <ImageView android:id="@+id/img_choice_symbol" android:padding="2dp" android:layout_width="20dp" android:layout_height="20dp" android:src="@drawable/ic_uma_symbol" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/text_choice_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:lines="1" tools:text="選択肢の名前" android:text="@{event.name}" android:textSize="@dimen/text_size_overlay" android:textStyle="bold" android:textColor="@color/text_gray" android:layout_marginStart="3dp" app:layout_constraintStart_toEndOf="@id/img_choice_symbol" app:layout_constraintTop_toTopOf="@id/img_choice_symbol" app:layout_constraintBottom_toBottomOf="@id/img_choice_symbol" /> <TextView android:id="@+id/text_choice_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignStart="@id/text_choice_name" tools:text="選択時の効果説明" android:text="@{event.formatMessage()}" android:textSize="@dimen/text_size_overlay" android:textStyle="normal" android:textColor="@color/text_gray" app:layout_constraintStart_toStartOf="@id/text_choice_name" android:layout_marginTop="2dp" app:layout_constraintTop_toBottomOf="@id/text_choice_name" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> ログ importできないとbuild以下のファイルで怒られますが、そんなの知らん!これだけでは原因が分かりにくい… エラー: シンボルを見つけられません import jp.seo.uma.eventchecker.databinding.ListChoiceItemBindingImpl; ^ シンボル: クラス ListChoiceItemBindingImpl 場所: パッケージ jp.seo.uma.eventchecker.databinding FAILURE: Build failed with an exception. 解決 Kotlinで便利なデフォルト引数ですが、バインディング式では使えないようです. 文字リテラルはバッククオートで囲みます. list_choice_item.xml <TextView android:id="@+id/text_choice_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignStart="@id/text_choice_name" tools:text="選択時の効果説明" - android:text="@{event.formatMessage()}" + android:text="@{event.formatMessage(`\n`)}" android:textSize="@dimen/text_size_overlay" android:textStyle="normal" android:textColor="@color/text_gray" app:layout_constraintStart_toStartOf="@id/text_choice_name" android:layout_marginTop="2dp" app:layout_constraintTop_toBottomOf="@id/text_choice_name" />
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]コピペでできる!でバージョンアップを検出しよう!!

初めに アプリを開発しているときに、ユーザーにバージョンアップの情報を届けたいときや、バージョンアップによるデータの変更を一度だけ行いたい時があると思います。 その時にバージョンアップの検出を行いたいのだが、少しめんどくさいと感じたため、できるだけ手軽に、コピペだけでできるようにメモを残しておきたいと思った。 内容 // 今のバージョン番号を取り出す val versionNow = PackageInfoCompat.getLongVersionCode(this.packageManager.getPackageInfo(this.packageName, 0)) // シェアプリのインスタンス化 val sp: SharedPreferences = getSharedPreferences("DateStore", Context.MODE_PRIVATE) // シェアプリから値を取り出す val vCode: Int = sp.getInt("VersionCode", 1) // 値を比べてバージョンが上がっているか判断 if(versionNow > vCode){ //   バージョンが上がっていた場合、新しいバージョン番号に上書きするための部分 val editor = sp.edit() editor.putInt("VersionCode", versionNow.toInt()) editor.apply() //更新時行いたい動作を下に書いていく。     }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む