首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >RecyclerView滚动过程中的java.lang.OutOfMemoryError

RecyclerView滚动过程中的java.lang.OutOfMemoryError
EN

Stack Overflow用户
提问于 2018-10-10 02:00:21
回答 1查看 619关注 0票数 0

我有RecyclerView,它里面还有其他的东西。每个RecyclerView项都有其他项的列表,如果我在中单击 item,这些项将显示为下面的子项。为了避免嵌套RecyclerView的事情,我在onBindViewHolder()中迭代了这些项,并将它们添加到膨胀子项布局中。

当我在我的应用程序中向下滚动,because there can be 1000 items and each item could have 1000 subitems.其订单列表时,出现OutOfMemory错误,如果我单击此列表中的项目,已订购部件的列表将逐个显示。

如何解决此问题。滚动也变得迟缓。我正在使用Glide API缓存图像,但仍然出现此错误。

recyclerView = view.findViewById<RecyclerView>(R.id.order_recycler_view).apply {

            setHasFixedSize(true)

            // use a linear layout manager
            layoutManager = viewManager

            // specify an viewAdapter (see also next example)
            adapter = viewAdapter

            //set cache for rv
            setItemViewCacheSize(50)
            isDrawingCacheEnabled = true
            drawingCacheQuality = View.DRAWING_CACHE_QUALITY_HIGH


        }

在RVAdapter onBindViewHolder()内部:

for(orderItem in it.itemList){
                    val contentLayout: View = LayoutInflater.from(holder.itemView.context).inflate(R.layout.ordered_list_item_layout, null, false)

                    fillItemView(contentLayout, orderItem, res)

                holder.orderContentLayout.addView(contentLayout)
            }

FillItemView方法:

    private fun fillItemView(contentLayout: View, orderItem: OrderedItem, res: Resources){
            val orderItemImage: ImageView = contentLayout.findViewById(R.id.orderPartImage)
            val orderItemName: TextView = contentLayout.findViewById(R.id.orderPartName)
            val orderItemWeight: TextView = contentLayout.findViewById(R.id.orderPartWeight)
val orderPartPhoto: String? = orderItem.item.itemPhoto.optString(ctx.getString(R.string.part_photo))

            setOrderedPartLogo(orderItemImage, res, orderPartPhoto)

            val itemPrice: String = String.format("%.2f", orderItem.item.itemPrice)

            val spanText = SpannableString(foodPrice + " €" + "  " + "(" + orderItem.quantity.toString() + "x" + ")" +  orderItem.item.itemName)
            spanText.setSpan(ForegroundColorSpan(res.getColor(R.color.colorRed)), 0, itemPrice.length + 2, 0)

            orderItemName.text = spanText
            orderItemWeight.text = orderItem.item.itemWeigth
        }



private fun setOrderedPartLogo(orderImageView: ImageView, resources: Resources, imageURL: String?) {

        val bitmap: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.no_photo)
        val roundedBitMapDrawable: RoundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, bitmap)
        roundedBitMapDrawable.isCircular = true

        val requestOptions: RequestOptions = RequestOptions()
                .circleCrop()
                .placeholder(roundedBitMapDrawable)
                .error(roundedBitMapDrawable)
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .priority(Priority.HIGH)

        Glide.with(orderImageView.context)
                .load(imageURL)
                .apply(requestOptions)
                .into(orderImageView)
    }

整个适配器:

class OrderListAdapter(private var mActivity: FragmentActivity,
                       private  var orderList: ArrayList<Order>, private var fragment: OrderListFragment):
        RecyclerView.Adapter<RecyclerView.ViewHolder>() {


    private var expandedPosition = -1
    private lateinit var mRecyclerView: RecyclerView

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        //order item views
        val orderName: TextView = itemView.findViewById(R.id.orderName)
        val orderWaitTime: TextView = itemView.findViewById(R.id.orderWaitTime)
        val orderAddress: TextView = itemView.findViewById(R.id.orderAddress)
        val orderDate: TextView = itemView.findViewById(R.id.orderDate)

        //details expandable layout
        val orderDetailsExpandable: LinearLayout = itemView.findViewById(R.id.orderDetails)
        val orderContentLayout: LinearLayout = itemView.findViewById(R.id.contentLayout)
        val orderLayout: ConstraintLayout = itemView.findViewById(R.id.mainLayout)

    }

    override fun onCreateViewHolder(parent: ViewGroup,
                                    viewType: Int): RecyclerView.ViewHolder {

        // create a new view
        val itemView = LayoutInflater.from(parent.context)
                .inflate(R.layout.order_recyclerview_item_layout, parent, false)


        return ViewHolder(itemView)
    }

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)

        mRecyclerView = recyclerView
    }


    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

        if (holder is ViewHolder) {
            val res = holder.itemView.context.resources
            val ctx = holder.itemView.context
            orderList[position].let {

                val orderPrice: Int = it.orderJSON.getInt(ctx.getString(R.string.order_total_price))
                val orderSupplierName: String? = it.orderSupplier?.json?.getString(ctx.getString(R.string.sup_name))
                val orderDate: String = it.orderJSON.getString(ctx.getString(R.string.order_date))
                val orderPaymentType: Int = it.orderJSON.getInt(ctx.getString(R.string.order_payment_type))
                var orderPaymentTypeString = "unknown"
                val orderDeliveryType: Int = it.orderJSON.getInt(ctx.getString(R.string.order_delivery_type))
                val orderDeliveryPrice: Int = it.orderJSON.getInt(ctx.getString(R.string.order_delivery_price))
                val orderJSONObject: JSONObject = it.orderJSON
                val orderItemList: ArrayList<OrderedItem> = it.partsList

                //OrderDate -> hours, minutes, day, month, year
                val formattedOrderDate: OrderDate = getOrderDate(orderDate)

                when(orderPaymentType){
                    1 -> orderPaymentTypeString = "credit"
                    2 -> orderPaymentTypeString = "credit"
                    3 -> orderPaymentTypeString = "money"
                    4 -> orderPaymentTypeString = "voucher"
                }


                //set order price, name and type
                val orderPriceString: String = convertCentsToFloat(orderPrice)
                if (orderSupplierName == null){
                    val spannableText = SpannableString(orderPriceString + " €  " + ctx.getString(R.string.default_sup_name) + " - " + orderPaymentTypeString)
                    spannableText.setSpan(ForegroundColorSpan(res.getColor(R.color.colorRed)), 0, orderPriceString.length + 3, 0)
                    holder.orderName.text = spannableText
                } else {
                    val spannableText = SpannableString(orderPriceString + " €  " + orderSupplierName + " - " + orderPaymentTypeString)
                    spannableText.setSpan(ForegroundColorSpan(res.getColor(R.color.colorRed)), 0, orderPriceString.length + 3, 0)
                    holder.orderName.text = spannableText
                }

                //set order wait time
                holder.orderWaitTime.text = formattedOrderDate.dateHours + ":" + formattedOrderDate.dateMinutes

                //set order address
                //holder.orderAddress.text = it.orderAddress

                //set order date
                holder.orderDate.text = formattedOrderDate.dateDay + "." + formattedOrderDate.dateMonth + "." + formattedOrderDate.dateYear

                holder.orderContentLayout.removeAllViews()

                //create layout for order items
                for(orderItem in it.itemList){
                    val contentLayout: View = LayoutInflater.from(holder.itemView.context).inflate(R.layout.ordered_list_item_layout, null, false)

                    fillItemView(contentLayout, orderItem, res, ctx)

                    holder.orderContentLayout.addView(contentLayout)
                }

                //create footer delivery
                val deliveryLayout: View = LayoutInflater.from(holder.itemView.context).inflate(R.layout.order_delivery_footer_layout, null, false)
                fillDeliveryFooter(deliveryLayout, orderDeliveryType, orderDeliveryPrice, res, ctx)
                holder.orderContentLayout.addView(deliveryLayout)

                //create footer orderRepeat Button
                val orderRepeatLayout: View = LayoutInflater.from(holder.itemView.context).inflate(R.layout.order_repeat_order_layout, null, false)
                holder.orderContentLayout.addView(orderRepeatLayout)

                orderRepeatLayout.setOnClickListener {
                    fragment.switchToOrderCartActivity(orderItemList)
                }

                //expanding order view on click
                val isExpanded = position == expandedPosition
                holder.orderDetailsExpandable.visibility = if (isExpanded) View.VISIBLE else View.GONE
                holder.itemView.isActivated = isExpanded

                holder.orderLayout.setOnClickListener {
                    createLog("expPos ", position.toString())
                    orderList[position].let {
                        if(expandedPosition != position){
                            if(expandedPosition != -1){
                                val myLayout: View? = mRecyclerView.layoutManager.findViewByPosition(expandedPosition)
                                createLog("myLayout", myLayout.toString())
                                createLog("OrderExp", "Expanding layout")
                                if(myLayout != null){
                                    myLayout.findViewById<LinearLayout>(R.id.orderDetails).visibility = View.GONE
                                }
                            }
                            createLog("expPosSet ", position.toString())
                            expandedPosition = position

                        } else {
                            expandedPosition = -1
                        }
                        notifyItemChanged(position)
                        scrollToTop(holder.itemView)
                    }
                }
            }
        }
    }


    override fun getItemCount() = orderList.size

    private fun scrollToTop(v: View) {
        val itemToScroll = mRecyclerView.getChildAdapterPosition(v)
        val centerOfScreen = mRecyclerView.width / 2 - v.width / 2
        fragment.getRecyclerViewManager().scrollToPositionWithOffset(itemToScroll, centerOfScreen)
    }

    private fun fillItemView(contentLayout: View, orderItem: OrderedItem, res: Resources){
            val orderItemImage: ImageView = contentLayout.findViewById(R.id.orderPartImage)
            val orderItemName: TextView = contentLayout.findViewById(R.id.orderPartName)
            val orderItemWeight: TextView = contentLayout.findViewById(R.id.orderPartWeight)
            val orderPartPhoto: String? = orderItem.item.itemPhoto.optString(ctx.getString(R.string.part_photo))

            setOrderedPartLogo(orderItemImage, res, orderPartPhoto)

            val itemPrice: String = String.format("%.2f", orderItem.item.itemPrice)

            val spanText = SpannableString(foodPrice + " €" + "  " + "(" + orderItem.quantity.toString() + "x" + ")" +  orderItem.item.itemName)
            spanText.setSpan(ForegroundColorSpan(res.getColor(R.color.colorRed)), 0, itemPrice.length + 2, 0)

            orderItemName.text = spanText
            orderItemWeight.text = orderItem.item.itemWeigth
        }

    private fun fillDeliveryFooter(deliveryLayout: View, deliveryType: Int, deliveryPrice: Int, res: Resources, ctx: Context){
        val deliveryImageIcon: ImageView = deliveryLayout.findViewById(R.id.deliveryIconImage)
        val deliveryPriceTextView: TextView = deliveryLayout.findViewById(R.id.deliveryLabelText)

        //set delivery icon
        when(deliveryType){
            1 -> deliveryImageIcon.setImageResource(R.drawable.ico_summary_delivery)
            2 -> deliveryImageIcon.setImageResource(R.drawable.ico_summary_pickup)
            else -> deliveryImageIcon.setImageResource(R.drawable.restauracia_no_photo)
        }

        //set delivery price, name label
        val deliveryPriceString: String = convertCentsToFloat(deliveryPrice)

        val deliverySpannable = SpannableString(deliveryPriceString + " €  Doprava / Vyzdvihnutie")
        deliverySpannable.setSpan(ForegroundColorSpan(res.getColor(R.color.colorPrice)), 0, deliveryPriceString.length + 2, 0)

        deliveryPriceTextView.text = deliverySpannable
    }

    private fun setOrderedPartLogo(orderImageView: ImageView, resources: Resources, imageURL: String?) {

        val bitmap: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.no_photo)
        val roundedBitMapDrawable: RoundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, bitmap)
        roundedBitMapDrawable.isCircular = true

        val requestOptions: RequestOptions = RequestOptions()
                .circleCrop()
                .placeholder(roundedBitMapDrawable)
                .error(roundedBitMapDrawable)
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .priority(Priority.HIGH)

        Glide.with(orderImageView.context)
                .load(imageURL)
                .apply(requestOptions)
                .into(orderImageView)
    }

    private fun convertCentsToFloat(centPrice: Int): String {
        val centOnlyPrice: Int = centPrice % 100
        val euroPrice: Int = (centPrice - centOnlyPrice) / 100

        if (centOnlyPrice < 10) {
            val finalPrice: String = euroPrice.toString() + ".0" + centOnlyPrice.toString()
            return finalPrice
        } else {
            val finalPrice: String = euroPrice.toString() + "." + centOnlyPrice.toString()
            return finalPrice
        }
    }

    private fun getOrderDate(date: String): OrderDate{
        val rawDate: List<String> = date.split("T")

        val dateOnly: String = rawDate[0]
        val dateFormat: List<String> = dateOnly.split("-")

        val timeOnly: String = rawDate[1]
        val timeFormat: List<String> = timeOnly.split(":")

        val finalDate = OrderDate(timeFormat[0], timeFormat[1], dateFormat[2], dateFormat[1], dateFormat[0])

        return finalDate

    }


    fun createLog(tag: String, msg: String){
        Log.i(tag, msg)
    }

    fun refreshOrder(orderListRefreshed: ArrayList<Order>){
        orderList = orderListRefreshed
        notifyDataSetChanged()
        if(AndroidAssets.getInstance(mActivity).orderList.isEmpty()){
            mRecyclerView.visibility = View.GONE
            fragment.showFooterLayout()
        } else{
            mRecyclerView.visibility = View.VISIBLE
            fragment.hideFooterLayout()
        }
        fragment.hideProgressBar()
    }
}

来自AndroidProfiler的关于内存使用率的图片(内存使用量增加了大约25秒-这是我开始滚动RecyclerView的地方……在那之后,它下降了)。

更新:通过更好的分析,我发现一台SubItem的内存为2.5MB。如果我有5个订单,每个订单包含20个项目,它将在RAM中分配250MB的空间。这就是Glide缓存图像。

更新2:有没有办法只加载可见的视图?因此,当用户滚动时,它将加载新的视图,顶部显示的视图将从内存中删除。我认为回收视图默认是通过回收项目布局视图来做到这一点的。

更新3:我为内部列表实现了新的回收器视图和适配器初始化。当onBindViewHolder()中的视图被标记为扩展时,这个rv和适配器被初始化,如果它不是扩展的,则RV和适配器设置为空。所以我实现了嵌套的RecyclerView。问题是我的内部回收器视图根本没有滚动。我必须设置滚动并将RV高度设置为固定大小(例如400dp),因为如果我将其保留为match_parent或wrap_content,如果->中的项目超过20个,它将抛出OutOfMemoryError,它不是回收视图。如何实现两个回收站视图的垂直滚动?

布局可视化:

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52726864

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档