由于某些原因,onClick尚未在我的适配器中注册。我正在使用MVVM模式,并且确保所有部分都绑在一起,但是对于我一生来说,我不知道为什么这不起作用。
StoreFragment
package com.example.brandroidtest.main
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.navigation.fragment.findNavController
import com.example.brandroidtest.databinding.FragmentStoreBinding
class StoreFragment : Fragment() {
//Will Create a ViewModelProivders object of class DetailViewModel the first time viewModel is used
//Allows us to move this code from on create to the declaration
private val viewModel: StoreViewModel by lazy {
val factory = StoreViewModelFactory(requireNotNull(activity).application)
ViewModelProviders.of(this, factory).get(StoreViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.i("onCreateView", "StoreFragment created")
val binding = FragmentStoreBinding.inflate(inflater)
binding.setLifecycleOwner(this)
binding.viewModel = viewModel
binding.storeList.adapter = StoreAdapter(StoreAdapter.OnClickListener {
viewModel.displayStoreDetails(it)
Log.i("inside OnClickListener", "after displayDetails")
})
Log.i("between adapter.onclick", "and viewModel observe")
viewModel.selectedStore.observe(this, Observer {
Log.i("observe", "inside the selectedStore observe method")
if (null != it) {
this.findNavController().navigate(
StoreFragmentDirections.actionMainListFragmentToDetailFragment(
it
)
)
viewModel.displayStoreDetailsComplete()
}
})
return binding.root
}
}
StoreViewModel
package com.example.brandroidtest.main
import android.app.Application
import android.content.Context
import android.net.ConnectivityManager
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.example.brandroidtest.model.Store
import com.example.brandroidtest.network.StoreAPI
import kotlinx.coroutines.*
enum class StoreAPIStatus {LOADING, DONE, NO_CONNECTION}
class StoreViewModel(application: Application) : AndroidViewModel(application) {
// Response from server: Either Store Data or Failure Message
private val _status = MutableLiveData<StoreAPIStatus>()
// for status of get request
//displayed when there is no internet connection or if the connection is unstable and the data is being loaded
val status: LiveData<StoreAPIStatus>
get() = _status
//internal variable accessed within this file
private val listOfStores = MutableLiveData<List<Store>>()
//external variable for anywhere else
val stores: LiveData<List<Store>>
get() = listOfStores
private val _selectedStore = MutableLiveData<Store>()
val selectedStore: LiveData<Store>
get() = _selectedStore
private var viewModelJob = Job()
private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)
/**
* Call getStoreData() in init so we can display the result immediately.
*/
init {
Log.i("viewModel init", "inside StoreViewModel init block")
if (isNetworkConnected(application.applicationContext))
getStoreData()
else
// Log.i("Bypassed network call", "")
listOfStores.value = emptyList()
_status.value = StoreAPIStatus.NO_CONNECTION
}
/**
* Sets the value of the status LiveData to the Store API data.
*/
private fun getStoreData() {
Log.i("getStoreData()", " inside getStoreData")
coroutineScope.launch {
try {
Log.i("getStoreData()", "Inside the coroutine before getData")
_status.value = StoreAPIStatus.LOADING
var storeData = async { StoreAPI.retrofitService.getData().stores }.await()
Log.i("getStoreData()", "Inside the coroutine after getData")
_status.value = StoreAPIStatus.DONE
listOfStores.value = storeData
} catch (e: Exception) {
_status.value = StoreAPIStatus.NO_CONNECTION
listOfStores.value = ArrayList()
e.printStackTrace()
}
}
}
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
private fun isNetworkConnected(context: Context): Boolean {
val cm =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
return cm!!.activeNetworkInfo != null && cm.activeNetworkInfo.isConnected
}
//will be called to set the store as the one that was clicked
fun displayStoreDetails(store : Store){
Log.i("displayStoreDetails", "inside this method")
_selectedStore.value = store
}
//sets the selected store's value to null so that live data can be updated when we select a new store and not show us the detail apge of the same store
fun displayStoreDetailsComplete() {
Log.i("displayStoreDetailsComplete", "inside this method")
_selectedStore.value = null
}
}
StoreAdapter
package com.example.brandroidtest.main
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.brandroidtest.model.Store
import com.example.brandroidtest.databinding.ListItemBinding
class StoreAdapter(val onClickListener: OnClickListener) :
ListAdapter<Store, StoreAdapter.StoreViewHolder>(DiffCallback) {
class StoreViewHolder(private var binding: ListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(store: Store) {
binding.store = store
Log.i("Adapter bind", store.storeLogoURL)
binding.executePendingBindings()
}
}
companion object DiffCallback : DiffUtil.ItemCallback<Store>() {
override fun areItemsTheSame(oldItem: Store, newItem: Store): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Store, newItem: Store): Boolean {
return oldItem.storeID == newItem.storeID
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): StoreViewHolder {
return StoreViewHolder(ListItemBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: StoreViewHolder, position: Int) {
val store = getItem(position)
Log.i("inside onBindViewHolder", "")
holder.itemView.setOnClickListener {
Log.i("inside onBindViewHolder", "setOnClickListener")
onClickListener.onClick(store)
}
holder.bind(store)
}
class OnClickListener(val clickListener: (store: Store) -> Unit) {
fun onClick(store: Store) {
Log.i("inside onClick", "click is being registered ${store.city}")
return clickListener(store)
}
}
}
StoreDetailFragment
package com.example.brandroidtest.detailed
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import com.example.brandroidtest.R
import com.example.brandroidtest.databinding.FragmentStoreDetailBinding
/**
* A simple [Fragment] subclass.
*/
class StoreDetailFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val application = requireNotNull(activity).application
val binding = FragmentStoreDetailBinding.inflate(inflater)
binding.setLifecycleOwner(this)
val store = StoreDetailFragmentArgs.fromBundle(arguments!!).selectedStore
val viewModelFactory = StoreDetailViewModelFactory(store, application)
binding.viewModel = ViewModelProviders.of(this, viewModelFactory).get(StoreDetailViewModel::class.java)
return binding.root
}
}
StoreDetailViewModel
package com.example.brandroidtest.detailed
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.example.brandroidtest.model.Store
class StoreDetailViewModel(store: Store, application: Application) : AndroidViewModel(application) {
private val _selectedStore = MutableLiveData<Store>()
val selectedStore : LiveData<Store>
get() = _selectedStore
init {
_selectedStore.value = store
}
}
我不知道为什么onClick不能正常工作,并且由于该原因而无法显示“详细信息片段”
这是项目链接:https://drive.google.com/open?id=1m8R8HvCt4m0KIp_IwdeO1YdB5yY8A8LK
答案 0 :(得分:1)
问题来自适配器项目的布局。
每个显示项目的高度为wrap_content
。但是您使用ScrollView
作为项目视图的根视图。
删除无用的ScrollView
和下一个LinearLayout
。您的布局应如下所示:
<LinearLayout
...
android:padding="16dp"/>
<ImageView
android:id="@+id/store_logo"
.../>
<LinearLayout
android:id="@+id/store_detail"
...>
</LinearLayout>