The tricky part is that the view you're working with in getView
will sometimes have been laid out, and sometimes not, so you have to handle both cases.
When it hasn't been laid out, you set a view tree observer to check on the ellipsis once it has been. In the case of recycled views, the layout will already be there and you can check for the ellipsis immediately after setting the text.
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder vh;
if (convertView == null) {
vh = new ViewHolder();
... // create/inflate view and populate the ViewHolder
}
vh = (ViewHolder) convertView.getTag();
// Set the actual content of the TextView
vh.textView.setText(...);
// Hide the (potentially recycled) expand button until ellipsizing checked
vh.expandBtn.setVisibility(GONE);
Layout layout = vh.textView.getLayout();
if (layout != null) {
// The TextView has already been laid out
// We can check whether it's ellipsized immediately
if (layout.getEllipsisCount(layout.getLineCount()-1) > 0) {
// Text is ellipsized in re-used view, show 'Expand' button
vh.expandBtn.setVisibility(VISIBLE);
}
} else {
// The TextView hasn't been laid out, so we need to set an observer
// The observer fires once layout's done, when we can check the ellipsizing
ViewTreeObserver vto = vh.textView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Layout layout = vh.textView.getLayout();
if (layout.getEllipsisCount(layout.getLineCount()-1) > 0) {
// Text is ellipsized in newly created view, show 'Expand' button
vh.expandBtn.setVisibility(VISIBLE);
}
// Remove the now unnecessary observer
// It wouldn't fire again for reused views anyways
ViewTreeObserver obs = vh.textView.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
}
});
return convertView;
}