所有ViewHolders都会同时创建

时间:2017-08-31 11:26:21

标签: android performance android-recyclerview android-adapter

我有RecyclerView

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_task_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/toolbar">

        <android.support.v4.widget.NestedScrollView
            android:id="@+id/scroll_task_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fillViewport="true">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:focusableInTouchMode="true"
                android:orientation="vertical">

                <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
                    android:id="@+id/rv_task_list"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/background_task_list" />

                <android.support.v4.widget.Space
                    android:layout_width="match_parent"
                    android:layout_height="80dp" />
            </LinearLayout>

        </android.support.v4.widget.NestedScrollView>
    </android.support.v4.widget.SwipeRefreshLayout>

初​​始化:

rv_task_list.layoutManager = LinearLayoutManager(context)
rv_task_list.layoutManager.setAutoMeasureEnabled(true)
rv_task_list.isNestedScrollingEnabled = false
rv_task_list.adapter = adapter

在适配器中设置数据:

    data.addAll(tasksToAdapter)
    notifyDataSetChanged()

(UPD)适配器:

class TaskAdapter(private val view: ITaskList) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

val data: MutableList<ITaskItem> = mutableListOf()
lateinit var context: Context

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    Log.e("TaskAdapter", "onCreateViewHolder")
    context = parent.context
    if (viewType == TITLE_TYPE) {
        val view = LayoutInflater.from(context).inflate(R.layout.card_title, parent, false);
        return TaskTitleViewHolder(view);
    } else {
        val view = LayoutInflater.from(context).inflate(R.layout.card_task, parent, false);
        return TaskViewHolder(view);
    }

}

override fun getItemViewType(position: Int): Int {
    return data[position].getTaskType()
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) 
{
    Log.e("TaskAdapter", "onBindViewHolder(${position})")
    if (getItemViewType(position) == TITLE_TYPE) {
        (holder as TaskTitleViewHolder).bind(data[position] as TaskTitle)

    }
    else {
        val task = data[position] as Task
        (holder as TaskViewHolder).bind(task)
        holder.itemView.setOnClickListener {
            view.onSelectTask(task.id)
        }
    }

}

fun setData(tasks: List<Task>, showAllTasks: Boolean = false) {
    data.clear()

    val tasksToAdapter: MutableList<ITaskItem> = mutableListOf()
    // List creating
    data.addAll(tasksToAdapter)
    notifyDataSetChanged()
}

override fun getItemCount(): Int = data.size


// ViewHolders

class TaskViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    private val tvTaskInfo: TextView = view.findViewById(R.id.tv_task_info)
    private val tvTaskComments: TextView = view.findViewById(R.id.tv_task_comments)
    private val tvTaskTitle: TextView = view.findViewById(R.id.tv_task_title)
    private val viewHeader: View = view.findViewById(R.id.view_header)
    private val tvEstimate: TextView = view.findViewById(R.id.tv_estimate)
    private val layoutExpectedDate: LinearLayout = view.findViewById(R.id.layout_expected_date)
    private val tvExpectedDate: TextView = view.findViewById(R.id.tv_expected_date)

    private val ivIconComments: ImageView = view.findViewById(R.id.iv_comments_icon)
    private val ivFlagComments: ImageView = view.findViewById(R.id.iv_flag_icon)
    private val ivIconClock: ImageView = view.findViewById(R.id.iv_clock_icon)
    private val layoutDateInformation: LinearLayout = view.findViewById(R.id.layout_date_information)

    fun bind(task: Task) {
        tvTaskInfo.text = "${task.projectName} ${task.id} (${task.type.localization})"

        if (task.commentsUnread == 0) {
            tvTaskComments.setTextColor(getColor(R.color.task_list_font_color_alpha))
            ivIconComments.setImageDrawable(ContextCompat.getDrawable(App.get(), R.drawable.ic_with_readed_message))
        } else {
            tvTaskComments.setTextColor(getColor(R.color.task_list_font_color))
            ivIconComments.setImageDrawable(ContextCompat.getDrawable(App.get(), R.drawable.ic_with_unreaded_message))
        }
        tvTaskComments.text = "${task.commentsUnread}/${task.commentsAll}"

        tvTaskTitle.text = Html.fromHtml(task.title)
        viewHeader.setBackgroundColor(getColorToTaskState(task.status))

        layoutDateInformation.visibility = View.GONE
        if (!(task.status == TaskState.InWork || task.status == TaskState.Negotiation)) {
            return
        }

        if (task.getFormattedExpectedDate().isNotBlank()) {
            tvExpectedDate.text = task.getFormattedExpectedDate()
            layoutExpectedDate.visibility = View.VISIBLE
        }
        else {
            layoutExpectedDate.visibility = View.GONE
        }

        layoutDateInformation.visibility = View.VISIBLE
        ivIconClock.visibility = View.GONE
        if (task.type == TaskType.EstimateRework || task.type == TaskType.Design) {
            with (task.getFormattedEstimateTime()) {
                if (isNotBlank()) {
                    tvEstimate.text = "${App.get().getString(R.string.estimated)} ${task.getFormattedEstimateTime()}"
                    tvEstimate.visibility = View.VISIBLE
                }
                else
                    tvEstimate.visibility = View.GONE
            }

            ivIconClock.visibility = View.VISIBLE
            ivIconClock.setImageDrawable(getImage(R.drawable.ic_clock))
            ivIconClock.setColorFilter(getColor(R.color.task_list_clock_estimated), PorterDuff.Mode.SRC_ATOP)
            ivFlagComments.setImageDrawable(null)
        } else {

            tvEstimate.text = task.roughEstimate ?: ""
            ivIconClock.visibility = View.VISIBLE
            ivIconClock.setImageDrawable(getImage(R.drawable.ic_clock))
            ivFlagComments.setImageDrawable(getImage(R.drawable.ic_flag))
        }

    }
}

class TaskTitleViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    private val tvTaskTitle: TextView = view.findViewById(R.id.tv_tasks_title)
    private val layoutCircle: FrameLayout = view.findViewById(R.id.layout_title_circle)
    private val layoutCircleAdditional: FrameLayout = view.findViewById(R.id.layout_title_circle_additional)


    fun bind(taskTitle: TaskTitle) {
        tvTaskTitle.text = taskTitle.content.toUpperCase()

        setCircle(layoutCircle, taskTitle.taskState)
        if (taskTitle.additionalTaskState != null) {
            layoutCircleAdditional.visibility = View.VISIBLE
            setCircle(layoutCircleAdditional, taskTitle.additionalTaskState!!)
        }
    }

    private fun setCircle(layout: FrameLayout, state: TaskState) {

        if (state == TaskState.WaitToWork) {
            layout.background = getImage(R.drawable.ic_waiting)
        } else {
            val drawable = ContextCompat.getDrawable(App.get(), R.drawable.task_header_circle)
            drawable.setColorFilter(getColorToTaskState(state), PorterDuff.Mode.SRC_ATOP)
            layout.background = drawable
        }

    }
}

我有两种类型的View Holder。当我在适配器中设置数据时,所有ViewHolders(超过90个)都是同时创建的。 onCreateViewHolderonBindViewHolder被逐个调用90次。

为什么会发生?

3 个答案:

答案 0 :(得分:3)

我认为您的问题是在RecyclerView内使用NestedScrollViewNestedScrollView扩展为包含所有内部子项(在这种情况下为RecyclerView) 您可以尝试使用app:layout_behavior="@string/appbar_scrolling_view_behavior" 但正如官方文档所述

  

切勿向滚动视图添加RecyclerViewListView。这样做会导致用户界面性能下降和用户体验不佳。

答案 1 :(得分:0)

因为您的RecyclerView上有android:layout_height="wrap_content"

你应该给它一个尺寸。 wrap_content将围绕所有90个项目进行包装,尝试创建所有这些视图。使用match_parent或其他一些设定高度。

答案 2 :(得分:0)

我有类似的问题。我认为我的案例中的解决方案是禁用QuerySet的自定义LayoutManager。我的代码在Kotlin中,但应该很容易适应Java:

supportsPredictiveItemAnimations