0
\$\begingroup\$

I need to my spinner supported endless scrolling. As rule the user will have only 1 - 5 items but in very rare cases some users can have 100+ items that will be loaded from server by pages (15 per page). So I tried to write a base class that will implement the view holder pattern and offer convenient methods for me to build an endless spinner list (I wrote it basing on ArrayAdapter and RecyclerView.Adapter):

abstract class JukeSpinnerAdapter<VH : JukeSpinnerAdapter.ViewHolder>(
    val context: Context,
    @IdRes val fieldId: Int = android.R.id.text1,
    @LayoutRes val dropDownResource: Int = android.R.layout.simple_spinner_dropdown_item
) : BaseAdapter(), ThemedSpinnerAdapter {

    private val helper: ThemedSpinnerAdapter.Helper = ThemedSpinnerAdapter.Helper(context)

    abstract fun createViewHolder(parent: ViewGroup?, itemType: Int): VH
    abstract fun bindViewHolder(holder: VH, position: Int)


    private fun createViewHolder(position: Int, parent: ViewGroup?, itemType: Int): VH {
        val holder = createViewHolder(parent, itemType)
        holder.itemId = getItemId(position)
        holder.position = position
        holder.itemType = itemType
        holder.root.tag = holder

        return holder
    }

    @Suppress("UNCHECKED_CAST")
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

        val holder = if (convertView == null) {
            createViewHolder(position, parent, getItemViewType(position))
        } else {
            convertView.tag as VH
        }

        bindViewHolder(holder, position)

        holder.itemId = getItemId(position)

        return holder.root
    }

    override fun getItemViewType(position: Int): Int {
        return INVALID_TYPE
    }

    override fun getItemId(position: Int): Long {
        return NO_ID
    }

    override fun getDropDownViewTheme(): Resources.Theme? {
        return helper.dropDownViewTheme
    }

    override fun setDropDownViewTheme(theme: Resources.Theme?) {
        helper.dropDownViewTheme = theme
    }

    override fun getDropDownView(position: Int, view: View?, parent: ViewGroup?): View {
        return createViewFromResource(helper.dropDownViewInflater, position, view, parent, dropDownResource)
    }

    private fun createViewFromResource(inflater: LayoutInflater, position:Int, convertView: View?,
                                       parent: ViewGroup?, resource: Int):View {
        val text:TextView?
        val view = convertView ?: inflater.inflate(resource, parent, false)

        try {

            if (fieldId == 0) {
                text = view as TextView
            } else {
                text = view.findViewById(fieldId)

                if (text == null) {
                    throw RuntimeException("Failed to find view with ID "
                        + context.resources.getResourceName(fieldId)
                        + " in item layout")
                }
            }
        } catch (e:ClassCastException) {
            Log.e("JukeSpinnerAdapter", "You must supply a resource ID for a TextView")
            throw IllegalStateException("JukeSpinnerAdapter requires the resource ID to be a TextView", e)
        }

        val item = getItem(position)

        text.text = if (item is CharSequence) item else item.toString()

        return view
    }

    abstract class ViewHolder(val root: View) {
        var position: Int = NO_POSITION
            internal set
        var itemId: Long = NO_ID
            internal set
        var itemType: Int = INVALID_TYPE
            internal set
    }

    companion object {
        private const val NO_POSITION = -1
        private const val NO_ID = -1L
        private const val INVALID_TYPE = -1
    }
}

Can anyone visually check my adapter on memory leaks (maybe I wrote stupid code somewhere) or slow places? If I call notifyDataSetChanged() will convertView in getView method be null to all holders were recreated?

In short, I just wanna know if it's a good implementation of the view holder pattern or not? If not how can I improve?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.