I changed the code from Android-Data-Binding-Starter-Example
Separate class, Use ViewModel class Use latest library (2020/04)
This sample covers following topics
- View Binding
- Data Binding
- Retrofit 2
- ViewModel
- Recycler View Data Binding
build.gradle
We need dataBiding <- Data Binding and compatibility <- Retrofit
apply plugin: 'kotlin-android-extensions' android { dataBinding { enabled = true } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'com.squareup.retrofit2:retrofit:2.8.1' implementation 'com.squareup.retrofit2:converter-moshi:2.8.1' implementation 'androidx.recyclerview:recyclerview:1.1.0' }
Retrofit API
Get data from Github API
GithubApi.kt
object GithubApi { val client: GitHubService = Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(MoshiConverterFactory.create()) .build() .create(GitHubService::class.java) }
GithubService.kt
interface GitHubService { @GET("search/repositories") fun searchRepositories(@Query("q") query: String) : Call<RepositoriesResponse> }
Data
Owner.kt
data class Owner( val login: String, val avatar_url: String, val url: String )
RepositoriesResponse.kt
data class RepositoriesResponse( val items: List<RepositoryItem> )
RepositoryItem.kt
data class RepositoryItem(val name: String, val full_name: String, val owner: Owner, val stargazers_count: Int, val watchers_count: Int, val forks_count: Int, val languages : String )
ViewModel
MainViewModel.kt
class MainViewModel : ViewModel() { val searchWord = ObservableField("") val loading = ObservableBoolean() val repositories = MutableLiveData<List<RepositoryItem>>() fun search(word: String) { loading.set(true) repositories.postValue(emptyList()) GithubApi.client.searchRepositories(word).enqueue(object: Callback<RepositoriesResponse> { override fun onFailure(call: Call<RepositoriesResponse>, t: Throwable) { loading.set(false) Log.e("Error", "Why?", t) } override fun onResponse( call: Call<RepositoriesResponse>, response: Response<RepositoriesResponse> ) { loading.set(false) if (response.isSuccessful) { response.body().let { repositories.postValue(it?.items) } } } }) } }
Layout
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="android.view.View"/> <variable name="viewModel" type="com.daiji110.bindgsampleapp.viewmodel.MainViewModel" /> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/search_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toStartOf="@id/button_search" android:text="@={viewModel.searchWord}" /> <Button android:id="@+id/button_search" style="@style/Widget.AppCompat.Button.Borderless" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:onClick="@{(v) -> viewModel.search(viewModel.searchWord)}" android:text="Search"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/button_search" tools:context=".MainActivity"/> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:visibility="@{viewModel.loading ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout>
list_repository.xml
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="item" type="com.daiji110.bindgsampleapp.data.RepositoryItem" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp"> <TextView android:id="@+id/title_text_view" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@{item.full_name}" android:textStyle="bold" tools:text="Title"/> <TextView android:id="@+id/due_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{String.valueOf(item.stargazers_count)}" tools:text="100"/> </LinearLayout> </layout>
MainActivity
MainActivity.kt
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val viewModel = MainViewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.viewModel = viewModel val adapter = RepositoryAdapter(this) binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = adapter viewModel.repositories.observe(this, Observer { response -> response?.let { adapter.items = it } }) binding.lifecycleOwner = this } class RepositoryAdapter(context: Context) : RecyclerView.Adapter<Holder>() { private val inflater = LayoutInflater.from(context) var items: List<RepositoryItem> = emptyList() set(value) { field = value notifyDataSetChanged() } override fun getItemCount(): Int = items.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { val binding: ListRepositoryBinding = DataBindingUtil.inflate(inflater, R.layout.list_repository, parent, false) return Holder(binding) } override fun onBindViewHolder(holder: Holder, position: Int) { holder.binding.item = items[position] holder.binding.executePendingBindings() } } class Holder(val binding: ListRepositoryBinding) : RecyclerView.ViewHolder(binding.root) }
コメント