Search code examples
androiddrop-down-menulineline-breaksautocompletetextview

Line break in AutoCompleteTextView


I have looked through plenty of sources trying to find how to break the text in a dropdown item into two lines, so that is could be shown fully, but unfortunately I haven't found any solution.

Instead, only the beginning of the string is shown, and it ends with ellipsis.

Here is my code in LoginActivity:

autoSuggestAdapter = AutoSuggestAdapter(
        this,
        R.layout.abc
    )
    autoCompleteTextView.threshold = 2
    autoCompleteTextView.setAdapter(autoSuggestAdapter)
    autoCompleteTextView.onItemClickListener =
        OnItemClickListener { parent, view, position, id ->
            selectedText.text = autoSuggestAdapter!!.getObject(position)
        }

    handler = Handler(Looper.getMainLooper(), Handler.Callback { msg ->
        if (msg.what === TRIGGER_AUTO_COMPLETE) {
            if (!TextUtils.isEmpty(autoCompleteTextView.text)) {
                makeApiCall(autoCompleteTextView.text.toString())
            }
        }
            false
        }
    )

class AutoSuggestAdapter

class AutoSuggestAdapter(context: Context, resource: Int) :
ArrayAdapter<String>(context, resource), Filterable {
private val mlistData: MutableList<String>
fun setData(list: List<String>?) {
    mlistData.clear()
    mlistData.addAll(list!!)

}

override fun getCount(): Int {
    return mlistData.size
}

@Nullable
override fun getItem(position: Int): String {
    return mlistData[position]
}

fun getObject(position: Int): String {
    return mlistData[position]
}

override fun getFilter(): Filter {
    return object : Filter() {
        protected override fun performFiltering(constraint: CharSequence?): FilterResults {
            val filterResults = FilterResults()
            if (constraint != null) {
                filterResults.values = mlistData
                filterResults.count = mlistData.size
            }
            return filterResults
        }

        protected override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
            if (results != null && results.count > 0) {
                notifyDataSetChanged()
            } else {
                notifyDataSetInvalidated()
            }
        }
    }
}

init {
    mlistData = ArrayList()
}

abc.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:ellipsize="end"
android:maxLines="2"
android:lines="2"
android:textAppearance="?attr/textAppearanceSubtitle1"
/>

Solution

  • I managed to solve the issue by overriding getView function in my AutoSuggestAdapter.

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view: TextView = convertView as TextView? ?: LayoutInflater.from(context).inflate(layoutResource, parent, false) as TextView
        var r = mlistData[position]
        // ...
        return view
    }
    

    For line break, there is a specific android class, but I couldn't use it in Kotlin. Besides, it is supported only in API 29 and higher. So I wrote the following code that inspects spaces across the string and checks whether the current line exceeds the width of the screen or not:

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view: TextView = convertView as TextView? ?: LayoutInflater.from(context).inflate(layoutResource, parent, false) as TextView
        var r = mlistData[position]
    
        paint.textSize = scaledPx
    
        var k = -1
        var n = -1
        var str = ""
        while (k < r.length) {
            if (r.indexOf(" ", k+1) == -1) {
                if (paint.measureText(r.substring(n+1, r.length)) > widthPixels) {
                    var line1 = r.substring(n+1, k)
                    var line2 = r.substring(k+1, r.length)
                    str = "$str$line1\n$line2"
                } else {
                    var line = r.substring(n+1, r.length)
                    str = "$str$line"
                }
                break
            } else {
                if (paint.measureText(r.substring(n+1, r.indexOf(" ", k+1))) > widthPixels) {
                    var line = r.substring(n+1, k)
                    n = k
                    str = "$str$line\n"
                } else {
                    k = r.indexOf(" ", k+1)
                }
            }
        }
        view.text = str.substring(0, str.length)
        return view
    }
    

    Future improvements might be implementing some kind of word wrap. Any suggestions are appreciated.