Android 回收器视图不显示

Android 回收器视图不显示,android,android-layout,kotlin,android-fragments,android-recyclerview,Android,Android Layout,Kotlin,Android Fragments,Android Recyclerview,我有一个具有网格布局的回收器视图,显示带有图像、标题、说明和价格的卡片,但它不显示任何内容。 数据提取正确,我假设布局文件中一定有错误,但我不知道它是什么 这是片段代码 package io.keepcoding.androidfinalproject.ui.products import android.app.SearchManager import android.content.Context import android.content.pm.PackageManager impor

我有一个具有网格布局的回收器视图,显示带有图像、标题、说明和价格的卡片,但它不显示任何内容。 数据提取正确,我假设布局文件中一定有错误,但我不知道它是什么

这是片段代码

package io.keepcoding.androidfinalproject.ui.products

import android.app.SearchManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.*
import android.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import io.keepcoding.androidfinalproject.R
import io.keepcoding.androidfinalproject.domain.Product
import io.keepcoding.androidfinalproject.repository.DataManager
import io.keepcoding.androidfinalproject.utils.CustomViewModelFactory
import io.keepcoding.androidfinalproject.utils.Status
import android.Manifest
import android.util.Log
import android.widget.Toast
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
import io.keepcoding.androidfinalproject.ui.main.ClientActivity
import kotlinx.android.synthetic.main.fragment_products.*

/**
 * A simple [Fragment] subclass.
 * Use the [Products.newInstance] factory method to
 * create an instance of this fragment.
 */
class ProductsFragment : Fragment() {

    private val viewModel: ProductsFragmentViewModel by lazy {
        val factory = CustomViewModelFactory(requireActivity().application,
            DataManager())
        ViewModelProvider(this, factory).get(ProductsFragmentViewModel::class.java)
    }

    private val LOCATION_PERMISSION_REQUEST_CODE = 2000

    private var latitude: Double = 51.507351

    private var longitude: Double = -0.127759

    private var selectedRadius : Int = 1000

    private var productViewModels: List<Product>? = null

    private var productsAdapter: ProductsAdapter? = null

    var productsInteractionListener: ProductInteractionListener? = null

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is ProductInteractionListener){
            productsInteractionListener = context
        } else
            throw IllegalArgumentException("Context does not implement product interaction listener")
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_products, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        this.setUpRecyclerView()
        this.prepRequestLocationUpdates()
        this.fetchData()
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.products_search, menu)
        activity?.let { activity ->
            val searchViewItem = menu.findItem(R.id.action_search)
            val searchManager = activity.getSystemService(Context.SEARCH_SERVICE) as SearchManager
            val searchView = searchViewItem.actionView as SearchView
            searchView.queryHint = "Search products"
            searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.componentName))
            searchView.isIconifiedByDefault = false
            searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {

                override fun onQueryTextSubmit(p0: String?): Boolean {
                    return false
                }

                override fun onQueryTextChange(newText: String?): Boolean {
                    var text = newText?.toLowerCase()
                    text?.let {
                        viewModel.searchProducts(selectedRadius, latitude, longitude, it)
                    }
                    return true
                }
            })
        }
        super.onCreateOptionsMenu(menu, inflater)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        selectedRadius = when(item.itemId) {
            R.id.one_radius -> 1000
            R.id.two_radius -> 2000
            R.id.three_radius -> 3000
            R.id.four_radius -> 4000
            else -> {
                5000
            }
        }
        return super.onOptionsItemSelected(item)
    }


    private fun setUpAdapter() {
        context?.let { context ->
            productsAdapter = ProductsAdapter(context) {
                this.productsInteractionListener?.goToProductDetail()
            }
            productViewModels?.let { products ->
                productsAdapter?.setData(products)
            }
        }
    }

    private fun setUpRecyclerView(){
        productsList.layoutManager = GridLayoutManager(context, 3)
        productsList.addItemDecoration(DividerItemDecoration(context, GridLayoutManager.VERTICAL))
    }

    private fun setUpObservers() {
        viewModel.getProducts().observe(viewLifecycleOwner, Observer { products ->
            when(products.status){
                Status.SUCCESS -> {
                    productViewModels = products.data
                    Log.    d("PRODUCTS", productViewModels.toString())
                    this.setUpAdapter()
                    productsList.visibility = View.VISIBLE
                    loadingView.visibility = View.INVISIBLE
                    retry.visibility = View.INVISIBLE
                }
                Status.LOADING -> {
                    productsList.visibility = View.INVISIBLE
                    loadingView.visibility = View.VISIBLE
                    retry.visibility = View.INVISIBLE
                }
                Status.ERROR -> {
                    productsList.visibility = View.INVISIBLE
                    loadingView.visibility = View.INVISIBLE
                    retry.visibility = View.VISIBLE
                }
            }
        })
    }

    private fun setUpListeners(){
        TODO("Set up retry button listener")
    }

    private fun fetchData() {
        viewModel.fetchProducts(selectedRadius, latitude, longitude)
        this.setUpObservers()
    }

    private fun prepRequestLocationUpdates() {
        if (ContextCompat.checkSelfPermission(requireActivity().application, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            requestLocationUpdates()
        } else {
            val permissionRequest = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
            requestPermissions(permissionRequest, LOCATION_PERMISSION_REQUEST_CODE)
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        when(requestCode){
            LOCATION_PERMISSION_REQUEST_CODE -> {
                if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    requestLocationUpdates()
                } else {
                    Toast.makeText(requireActivity().application, "Unable to update location without permission", Toast.LENGTH_LONG).show()
                }
            } else -> {
                super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    private fun requestLocationUpdates(){
        viewModel.getLocationData().observe(viewLifecycleOwner, Observer { location ->
            latitude = location.latitude
            longitude = location.longitude
        })
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @return A new instance of fragment Products.
         */
        @JvmStatic
        fun newInstance() =
            ProductsFragment()
    }
}
包io.keepcoding.androidfinalproject.ui.products
导入android.app.SearchManager
导入android.content.Context
导入android.content.pm.PackageManager
导入android.os.Bundle
导入android.view*
导入android.widget.SearchView
导入androidx.core.content.ContextCompat
导入androidx.fragment.app.fragment
导入androidx.lifecycle.Observer
导入androidx.lifecycle.ViewModelProvider
导入io.keepcoding.androidfinalproject.R
导入io.keepcoding.androidfinalproject.domain.Product
导入io.keepcoding.androidfinalproject.repository.DataManager
导入io.keepcoding.androidfinalproject.utils.CustomViewModelFactory
导入io.keepcoding.androidfinalproject.utils.Status
导入android.Manifest
导入android.util.Log
导入android.widget.Toast
导入androidx.recyclerview.widget.DividerItemDecoration
导入androidx.recyclerview.widget.GridLayoutManager
导入io.keepcoding.androidfinalproject.ui.main.ClientActivity
导入kotlinx.android.synthetic.main.fragment\u产品*
/**
*一个简单的[片段]子类。
*使用[Products.newInstance]工厂方法
*创建此片段的实例。
*/
类ProductsFragment:Fragment(){
private val viewModel:ProductsFragmentViewModel by lazy{
val factory=CustomViewModelFactory(require()。应用程序,
DataManager())
ViewModelProvider(这个,工厂).get(ProductsFragmentViewModel::class.java)
}
私有val位置\权限\请求\代码=2000
私人变量纬度:双=51.507351
私有变量经度:Double=-0.127759
私有变量selectedRadius:Int=1000
私有变量productViewModels:列表?=null
私有变量productsAdapter:productsAdapter?=null
var productsInteractionListener:ProductInteractionListener?=null
重写转速表(上下文:上下文){
super.onAttach(上下文)
if(上下文为ProductInteractionListener){
productsInteractionListener=上下文
}否则
抛出IllegalArgumentException(“上下文不实现产品交互侦听器”)
}
覆盖创建视图(
充气器:布局充气器,容器:视图组?,
savedInstanceState:捆绑?
):查看{
//为该碎片膨胀布局
返回充气机。充气(R.layout.fragment_产品,容器,假)
}
覆盖已创建的视图(视图:视图,保存状态:捆绑?){
super.onViewCreated(视图,savedInstanceState)
this.setUpRecyclerView()
此.prerequestlocationupdates()文件
this.fetchData()
}
覆盖创建选项菜单(菜单:菜单,充气机:菜单充气机){
充气机。充气(右菜单。产品搜索,菜单)
活动?.let{activity->
val searchViewItem=menu.findItem(R.id.action\u search)
val searchManager=activity.getSystemService(Context.SEARCH\u服务)作为searchManager
val searchView=searchViewItem.actionView作为searchView
searchView.queryHint=“搜索产品”
searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.componentName))
searchView.isIconifiedByDefault=false
searchView.setOnQueryTextListener(对象:searchView.OnQueryTextListener{
重写onQueryTextSubmit(p0:String?):布尔值{
返回错误
}
重写onQueryTextChange(newText:String?):布尔值{
var text=newText?.toLowerCase()
短信?,让我来{
viewModel.searchProducts(选择半径、纬度、经度、it)
}
返回真值
}
})
}
super.onCreateOptions菜单(菜单,充气机)
}
覆盖选项ItemSelected(项:菜单项):布尔值{
selectedRadius=when(item.itemId){
R.id.1_半径->1000
R.id.2_半径->2000
R.id.three_半径->3000
R.id.four_半径->4000
其他->{
5000
}
}
返回super.onOptionsItemSelected(项目)
}
私人娱乐设置适配器(){
上下文?.let{context->
productsAdapter=productsAdapter(上下文){
此.productsInteractionListener?.goToProductDetail()为
}
productViewModels?.let{products->
productsAdapter?.setData(产品)
}
}
}
私人娱乐设置回收视图(){
productsList.layoutManager=GridLayoutManager(上下文,3)
productsList.addItemDecoration(DividerItemDecoration(上下文,GridLayoutManager.VERTICAL))
}
私人娱乐中心(){
viewModel.getProducts().observe(viewLifecycleOwner,Observer{products->
何时(产品状态){
Status.SUCCESS->{
productViewModels=products.data
Log.d(“产品”,productViewModels.toString())
这个文件名为setUpAdapter()
productsList.visibility=View.VISIBLE
loadingView.visibility=View.INVISIBLE
retry.visibility=View.INVISIBLE
}
Status.LOADING->{
productsList.visibility=View.INVISIBLE
loadingView.visibility=View.VISIBLE
retry.visibility=View.INVISIBLE
}
Status.ERROR->{
productsList.visibility=View.INVISIBLE
loadingView.visibility=View.INVISIBLE
package io.keepcoding.androidfinalproject.ui.products

import android.content.Context
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.androidfinalproject.R
import io.keepcoding.androidfinalproject.domain.Product
import kotlinx.android.synthetic.main.product_item.view.*

class ProductsAdapter(val context: Context, val productClickListener: ((Product) -> Unit)? = null) : RecyclerView.Adapter<ProductsAdapter.ProductHolder>(){

    private var productItems : List<Product> = listOf<Product>()

    private var productsInteractionListener: ((View) -> Unit)? = {
        if(it.tag is Product){
            productClickListener?.invoke(it.tag as Product)
        } else {
            throw IllegalArgumentException("Product item view's tag is not set to a Product object")
        }
    }

    fun setData(productItems: List<Product>){
        this.productItems = productItems
        notifyDataSetChanged()
    }

    inner class ProductHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
        var product: Product? = null
            set(value) {
                field = value
                itemView.tag = field
                field?.let {
                    Glide.with(context)
                        .load(it.photos?.get(0))
                        .apply {
                            RequestOptions()
                                .placeholder(R.drawable.ic_launcher_background)
                        }.into(itemView.productImage)
                    itemView.productTitle.text = it.name
                    itemView.productDesc.text = it.description
                    itemView.productPrice.text = (it.price?.toDouble()?.div(100)).toString()
                }
            }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.product_item, parent, false)
        return ProductHolder(view)    }

    override fun getItemCount(): Int {
        return this.productItems.size
    }

    override fun onBindViewHolder(holder: ProductHolder, position: Int) {
        val product = productItems.get(position)
        holder.product = product
        holder.itemView.setOnClickListener(productsInteractionListener)
    }

}

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".ui.products.ProductsFragment"
     android:layout_height="match_parent"
    android:layout_width="wrap_content"
    >
        <include
            android:id="@+id/loadingView"
            layout="@layout/view_loading"
            />
        <include
            android:id="@+id/retry"
            layout="@layout/view_retry"
            android:visibility="invisible"
            />
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/productsList"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible"
            />

</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    >
    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            >
            <ImageView
                android:id="@+id/productImage"
                android:layout_width="match_parent"
                android:layout_height="250dp"
                />
            <TextView
                android:id="@+id/productTitle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:maxLines="1"
                />
            <TextView
                android:id="@+id/productDesc"
                android:layout_width="match_parent"
                android:layout_height ="wrap_content"
                android:maxLines="4"
                />
            <TextView
                android:id="@+id/productPrice"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:maxLines="1"
                android:layout_gravity="bottom|end"
                />

        </LinearLayout>
    </androidx.cardview.widget.CardView>
</GridLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:gravity="center"
    android:layout_height="wrap_content">

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="@string/loading"
        android:textSize="20sp"
        />
</LinearLayout>
productsList.adapter = productsAdapter