具有两个列表和两个视图持有者的Recyclerview,未调用onCreateViewHolder和onBind

问题描述

嗨,我在回收者视图中有以下代码

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...