MainActivityBinding not found

DataBindingを使う時に下記のように書いたんですけど

//MainActivity.kt内
class MainActivity : AppCompatActivity() {

    private lateinit var model: TaskViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var binding:ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.content_main)
  }
}

まずはDataBindingUtilが見つからない。

これは、build.gradleに下記を書いてないからでした。

android{ 
dataBinding{
enabled = true
}
}

これねー、Room+LiveData+ViewModelでやるときは、この

dataBinding enabled =true

いらないんですよ。なので、ちょっとした落とし穴ですね。

んで、本題の

MainActivityBinding not found

ですが、この一文の中にすでに答えがあります。

var binding:ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.content_main)

Bindingの型はレイアウトファイルの名称に依存するのです。

つまり、この場合は、レイアウトファイルがcontent_mainという名前なので、

var binding:ContentMainBinding =
            DataBindingUtil.setContentView(this, R.layout.content_main)

ってすると、動きます。


ジオコーディングで緯度経度が取得できないとき

住所から緯度経度が取得できなくなました。

どうやら外部サイトから情報を取得するcurl関数でひかかっているみたいです。

curlのエラーを文字列で起こしてみよう。
$error = curl_error($ch);
var_dump($error);

‘error setting certificate verify locations:
CAfile: C:\xampp\apache\bin\curl-ca-bundle.crt
CApath: none’

ふむふむ、ssh証明書がないらしいです。
ということで、、

①https://curl.haxx.se/docs/caextract.htmlからcacert.pemをダウンロードします。
②C:\xampp\apache\bin(場所は環境による)に置きます。
③curl-ca-bundle.crtに名前を変更します。(拡張子もそのまま変更して大丈夫)

vagrantからxamppにローカル環境を移行したことによって、ssh証明書が欠陥していることが原因でした。

Android Parcelableなクラスのメンバ変数に独自オブジェクトを入れる(Kotlin サンプルコード)

例えば、TaskというクラスとUserというクラスがあります。

Taskのメンバ変数に、Userクラスを指定したいものがあります。

//Task.kt内
@Parcelize
class Task(val id: String, var content: String?,  val to:User, var limit:String?):Parcelable{
    //中略
}
//User.kt内
class
User(val id: String, val name: String){
    //中略
}

ちな、Kotlinのどっかのバージョンから、Parcelableにしたいクラスを超簡単にかけるようになりました!!

以前は、定義しなければならないメソッドなどがいっぱいあって、クラスをParcelableにするのに一手間かかってました。

それが、 Task.ktであるように、

@Parcelable

というアノテーションと、

:Parcelable

で継承するだけでOkになりました。時代が進むっていいね(´ω`)

さて、本題に戻り、上記のTaskとUserでは、下記のエラーが出てこのままではビルドできません。

Type is not directly supported by ‘Parcelize’. Annotate the parameter type with ‘@RawValue’ if you want it to be serialized using ‘writeValue()’

えー。

これ、Userの方もParcelableにすれば直ります。

なので、User.ktを次のようにします。

@Parcelize
class
User(val id: String, val name: String) :Parcelable {
    //中略
}

Javaでもこうでしたっけ? 昨日、I forget.


Parcelは小包みって意味らしいです。

エラー: Methods annotated with @Insert can return either void, long, Long, long[], Long[] or List. public abstract java.lang.Object insertOne(@org.jetbrains.annotations.NotNull()

Android Kotlinでコルーチンに取り組んでおります。

コードは断片的になっちゃうんですけど、下記の方法でコルーチンでDBにデータを追加するのをやってみました。

ボタンをタップすると、データが追加されるというものです。

//Purpose.kt
@Entity
data class Purpose constructor(
    @PrimaryKey val purposeId: Int = 0,
    val purpose: String
)
//PurposeDao.kt
@Dao
interface PurposeDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertOne(purpose: Purpose)
}
//TaskViewModel.kt
class TaskViewModel(private val repository: TaskRepository) : ViewModel() {

    companion object {
        val FACTORY = singleArgViewModelFactory(::TaskViewModel)
    }

    // Create a LiveData with a String
    val currentTask: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }


    fun onButtonClicked(context: Context) {
        val coroutine_a = addTask(context)
    }

    //コルーチンのテスト
    fun addTask(context: Context) {

        viewModelScope.launch {

            addTaskDb(context)

        }
    }

    suspend fun addTaskDb(context: Context) {
        try {

            repository.addTask(context)

        } catch (cause: Throwable) {
            // If anything throws an exception, inform the caller
            Log.e("TaskViewModel", "エラー でーたべーす")
            Toast.makeText(context, "データベースエラーで更新できませんでした!", Toast.LENGTH_LONG).show();
        }
    }

}
//TaskRepository.kt
class TaskRepository(val purposeDao: PurposeDao) {

    suspend fun addTask(context:Context) {

        try {

           //コルーチンのテストのために、とりあえずデータを指定
            val purpose = Purpose(3, "がんばる");

            purposeDao.insertOne(purpose)

        } catch (cause: Throwable) {

            Log.e("TaskRepository", cause.toString())
            Toast.makeText(context, "Agent already exists!", Toast.LENGTH_LONG).show();

        }
    }
}
//MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.content_main)


        val database = getDatabase(this)
        val repository = TaskRepository( database.purposeDao)

        // Get the ViewModel.
        model = ViewModelProviders
            .of(this, TaskViewModel.FACTORY(repository))
            .get(TaskViewModel::class.java)

	//中略
        }
//AppDatabase.kt
@Database(entities = [Purpose::class], version = 2)
abstract class AppDatabase : RoomDatabase() {
    //abstract fun purposeDao(): PurposeDao
    abstract val purposeDao: PurposeDao
}

private lateinit var INSTANCE: AppDatabase

fun getDatabase(context: Context): AppDatabase {
    synchronized(AppDatabase::class) {
        if (!::INSTANCE.isInitialized) {
            INSTANCE = Room
                .databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        "task-admin"
                )
                .fallbackToDestructiveMigration()
                .build()
        }
    }
    return INSTANCE
}

そしたら、インストール時に上記のエラーが出て、ビルドできません。(´ω`)

色々と悩みましたが、アプリケーションのbuild.gradleを下記に変更したらできるようになりました。

annotationProcessor 'androidx.room:room-compiler:2.0.0'
implementation "androidx.room:room-ktx:2.0.0"

↓ 上記を下記に変更

annotationProcessor 'androidx.room:room-compiler:2.2.2'
implementation "androidx.room:room-ktx:2.2.2"
ロリポップのドロイド君。懐かしい。

AndroidXへの移行

前回、下記のエントリを書きましたが、

Android コルーチンを便利に利用するためのviewModelScopeが読み込めない

その後、下記のようなエラーが出て、ビルドできなくなりました。

java.lang.RuntimeException: Manifest merger failed : Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory)

AndroidXを使おうと思うと、gradle.propertiesに

android.enableJetifier=true
android.useAndroidX=true

と書くと、今までのライブラリが使えなくなります。
たとえば

import android.support.v7.app.AppCompatActivity

のままではAppCompatActivityが使えなくなり

import androidx.appcompat.app.AppCompatActivity

に置き換えないといけません。
これらを全部importするところは手でやらないといけないので、パッケージ内のコード量が多い場合は、よく考えないといけないですよ!!?

さっきの

という記事での、このようなimport時に古いAPIを読み込んじゃうようなことは防げますね。

ちな、下記のAndroidの公式サイトでは

https://developer.android.com/jetpack/androidx/migrate?hl=ja

Android Studio 3.2 以上では AndroidX を使用して既存のプロジェクトを迅速に移行できるようになりました。移行するには、メニューバーから [Refactor] > [Migrate to AndroidX] を選択します。

って書いてあるんですが、やるとbuild.gradleをきれいにしてくれるのと、レイアウトのxmlファイルを書き直してくれます。xmlファイルは

<android.support.v4.widget.DrawerLayout

<androidx.core.widget.DrawerLayout

のような書き直しを全部でやらないといけないので、自動でやってくれるのは大変助かります。

しかし、プログラム部分は手動で新しいAPIをインポートしないといけないのは変わりありませんでした…。(>_<)

それにしても、こういうビルドできない問題にかける時間や苦しみがすごいですよね。

早く、俺にプログラム書かせてくれってばよ。

というわけで、これからAndroidアプリを作ろうという人は、最初っからAndroidXで作った方がいいです。(重要なことなので、太字にしました)

令和2年も弊社をよろしくお願いいたします。