问题描述
嗨,我在回收者视图中有以下代码
package io.keepcoding.globaldisastertracker.ui.detail
import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import io.keepcoding.globaldisastertracker.R
import kotlinx.android.synthetic.main.events_recycler_view_item.view.*
import kotlinx.android.synthetic.main.images_recycler_view_item.view.*
import kotlinx.android.synthetic.main.news_recycler_view_item.view.*
class DetailAdapter(val context: Context,itemClickListener: DetailInteractionListener? = null) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var newsItems = mutablelistof<NewsItemviewmodel?>()
private var imagesItems = mutablelistof<ImageItemviewmodel?>()
private var isNewsFragment: Boolean = false
private var newsDetailInteractionListener: ((View) -> Unit)? = {
if(it.tag is NewsItemviewmodel){
itemClickListener?.onNewsItemClick((it.tag as NewsItemviewmodel)?.newsUrl as String)
}
}
private var imagesDetailInteractionListener: ((View) ->Unit)? = {
if(it.tag is ImageItemviewmodel){
itemClickListener?.onImageItemClick((it.tag as ImageItemviewmodel)?.image as String)
}
}
companion object {
const val NEWS = 1
const val IMAGE = 2
}
fun setData(newsList: List<NewsItemviewmodel?>?,imagesList: List<ImageItemviewmodel?>?,isNews: Boolean){
isNewsFragment = isNews
if(isNewsFragment) {
newsList?.let {
newsItems = it.toMutableList()
}
}
else{
imagesList?.let {
this.imagesItems = it.toMutableList()
}
}
notifyDataSetChanged()
}
inner class NewsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var article: NewsItemviewmodel? = null
set(value) {
field = value
itemView.tag = field
field?.let {
Glide.with(context)
.load(it.thumbnail)
.apply {
RequestOptions()
.placeholder(R.drawable.ic_launcher_background)
}.into(itemView.news_image)
itemView.headline.text = it.title
itemView.content.text = it.description
}
}
}
inner class ImagesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var image: ImageItemviewmodel? = null
set(value) {
field = value
itemView.tag = field
field?.let {
Glide.with(context)
.load(it.image)
.apply {
RequestOptions()
.placeholder(R.drawable.ic_launcher_background)
}.into(itemView.image)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): RecyclerView.ViewHolder {
if(viewType == NEWS){
return NewsViewHolder(LayoutInflater.from(context).inflate(R.layout.news_recycler_view_item,parent,false))
}
return ImagesViewHolder(LayoutInflater.from(context).inflate(R.layout.images_recycler_view_item,false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder,position: Int) {
if(isNewsFragment){
val article = newsItems?.get(position)
(holder as NewsViewHolder).article = article
holder.itemView.setonClickListener(newsDetailInteractionListener)
} else {
val image = imagesItems?.get(position)
(holder as ImagesViewHolder).image = image
holder.itemView.setonClickListener(imagesDetailInteractionListener)
}
}
override fun getItemViewType(position: Int): Int {
if(isNewsFragment){
return newsItems?.get(position)!!.viewType
}
return imagesItems?.get(position)!!.viewType
}
override fun getItemCount(): Int {
return if(isNewsFragment){
newsItems!!.size
} else imagesItems!!.size
}
}
这是我在包含适配器的片段中设置适配器的地方,我懒惰地声明了它,然后在从视图模型中观察实时数据变量后将其设置为recyclerview的适配器。
package io.keepcoding.globaldisastertracker.ui.detail
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.viewmodelProvider
import androidx.recyclerview.widget.DividerItemdecoration
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.linearlayoutmanager
import com.google.android.material.floatingactionbutton.FloatingActionButton
import io.keepcoding.globaldisastertracker.R
import io.keepcoding.globaldisastertracker.repository.local.disasterEventsRoomDatabase
import io.keepcoding.globaldisastertracker.repository.local.LocalHelperImpl
import io.keepcoding.globaldisastertracker.repository.remote.ApiHelperImpl
import io.keepcoding.globaldisastertracker.repository.remote.RemoteDataManager
import io.keepcoding.globaldisastertracker.ui.main.EventItemviewmodel
import io.keepcoding.globaldisastertracker.utils.CustomviewmodelFactory
import io.keepcoding.globaldisastertracker.utils.Status
import kotlinx.android.synthetic.main.fragment_detail.list
import kotlinx.android.synthetic.main.fragment_detail.loadingView
import kotlinx.android.synthetic.main.fragment_detail.retry
import kotlinx.android.synthetic.main.try_again.*
// Todo: Rename parameter arguments,choose names that match
// the fragment initialization parameters,e.g. ARG_ITEM_NUMBER
private const val ARG_FROM_SERVER = "FROM_SERVER"
private const val ARG_EVENT_ITEM = "EVENT_ITEM"
private const val ARG_IS_NEWS_FRAGMENT ="IS_NEWS_FRAGMENT"
/**
* A simple [Fragment] subclass.
* Use the [ListFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class DetailFragment : Fragment() {
// Fragment parameters
private var fromServer: Boolean = false
private var eventItem: EventItemviewmodel? = null
private var isNewsFragment: Boolean = false
private var sharedFAB: FloatingActionButton? = null
private var imageItems: List<ImageItemviewmodel?>? = mutablelistof(null)
private var newsItems: List<NewsItemviewmodel?>? = mutablelistof(null)
private val detailsAdapter: DetailAdapter by lazy {
lateinit var adapter: DetailAdapter
context?.let { context ->
adapter = DetailAdapter(context,requireActivity() as DetailActivity)
}
adapter.setData(newsItems,imageItems,isNewsFragment)
adapter
}
private val viewmodel: DetailFragmentviewmodel by lazy {
val factory = CustomviewmodelFactory(requireActivity().application,ApiHelperImpl(RemoteDataManager().bingSearchApi,RemoteDataManager().eonetApi),LocalHelperImpl(disasterEventsRoomDatabase.getInstance(requireActivity().applicationContext))
)
viewmodelProvider(this,factory).get(DetailFragmentviewmodel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
fromServer = it.getBoolean(ARG_FROM_SERVER)
eventItem = it.getParcelable(ARG_EVENT_ITEM)
isNewsFragment = it.getBoolean(ARG_IS_NEWS_FRAGMENT)
}
}
override fun onCreateView(
inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_detail,container,false)
}
override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
super.onViewCreated(view,savedInstanceState)
setUpListeners()
setUpRecyclerView()
setUpObservers()
}
fun setFABClickListener(fab: FloatingActionButton?){
sharedFAB = fab
if(fromServer){
sharedFAB?.setonClickListener {
eventItem?.let { eventItem ->
viewmodel.saveEvent(eventviewmodel = eventItem)
}
}
} else {
sharedFAB?.setonClickListener {
eventItem?.let { eventItem ->
viewmodel.deleteEvent(eventItem.id!!)
}
}
}
}
private fun setUpListeners(){
buttonRetry.setonClickListener {
eventItem?.let {
fetchData(it)
}
}
}
private fun setUpRecyclerView(){
if(isNewsFragment){ // display linear layout with news
list.layoutManager = linearlayoutmanager(context,linearlayoutmanager.VERTICAL,false)
} else { // display grid layout with images
list.layoutManager = GridLayoutManager(context,3)
}
list.addItemdecoration(DividerItemdecoration(context,DividerItemdecoration.VERTICAL))
}
private fun fetchData(event: EventItemviewmodel){
if(fromServer){
event.title?.let { title ->
if(isNewsFragment) viewmodel.fetchApiNews(title)
else viewmodel.fetchApiImages(title)
}
} else {
event.id?.let {id ->
if(isNewsFragment) viewmodel.loadNewsFromLocal(id)
else viewmodel.loadImagesFromLocal(id)
}
}
}
private fun observeImages(){
viewmodel.getimages().observe(viewLifecycleOwner,Observer { images ->
when (images.status) {
Status.SUCCESS -> {
imageItems = images.data
loadingView.visibility = View.INVISIBLE
list.visibility = View.VISIBLE
list.adapter = detailsAdapter
}
Status.LOADING -> {
loadingView.visibility = View.VISIBLE
retry.visibility = View.INVISIBLE
}
Status.ERROR -> {
retry.visibility = View.VISIBLE
loadingView.visibility = View.INVISIBLE
list.visibility = View.INVISIBLE
}
}
})
}
private fun observeNews(){
viewmodel.getNews().observe(viewLifecycleOwner,Observer { news ->
when (news.status) {
Status.SUCCESS -> {
newsItems = news.data
loadingView.visibility = View.INVISIBLE
list.visibility = View.VISIBLE
list.adapter = detailsAdapter
}
Status.LOADING -> {
retry.visibility = View.INVISIBLE
loadingView.visibility = View.VISIBLE
list.visibility = View.INVISIBLE
}
Status.ERROR -> {
retry.visibility = View.VISIBLE
loadingView.visibility = View.INVISIBLE
list.visibility = View.INVISIBLE
}
}
})
}
private fun setUpObservers(){
eventItem?.let { event ->
fetchData(event)
if(isNewsFragment){
observeNews()
} else {
observeImages()
}
viewmodel.getSnackBar().observe(viewLifecycleOwner,Observer { snackBar ->
when(snackBar.status) {
Status.SUCCESS -> Toast.makeText(requireActivity().application,snackBar.data,Toast.LENGTH_LONG).show()
Status.ERROR -> Toast.makeText(requireActivity().application,snackBar.message,Toast.LENGTH_LONG).show()
}
})
}
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment ListFragment.
*/
// Todo: Rename and change types and number of parameters
@JvmStatic
fun newInstance(fromServer: Boolean,eventItem : EventItemviewmodel,isNewsFragment: Boolean) =
DetailFragment().apply {
arguments = Bundle().apply {
putBoolean(ARG_FROM_SERVER,fromServer)
putBoolean(ARG_IS_NEWS_FRAGMENT,isNewsFragment)
putParcelable(ARG_EVENT_ITEM,eventItem)
}
}
}
}
Github仓库在这里 https://github.com/AntonioRoldan/GlobalDisasterTracker
更新: 我发现,当使用FrameLayout作为片段时,显示的是回收站视图,而不是显示为LinearLayout时,也没有显示,而当它是FrameLayout时,回收站视图会覆盖选项卡布局,这是为什么?
解决方法
事实证明,回收者视图代码很好,这是布局问题。 我包括其他布局,用于显示加载微调框和失败呼叫的重试按钮,并且它们的高度为match_parent,我只是在两个布局文件中将其更改为wrap_content,这样我可以使用LinearLayout而不是FrameLayout在以下位置显示回收器视图标签布局。 然后,在获得API响应后,我将重试和加载的可见性设置为GONE