1

Like the title say, i create list view which each item display image, three line text, and a button, but the view is so lagging even in samsung galaxy mega dual core, here my item view xml

<RelativeLayout
    android:gravity="center_vertical"
    android:padding="5.0dip"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:listPreferredItemHeight"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <ImageView
        android:id="@+id/compositeListViewItemThumbnailImage"
        android:layout_width="60.0dip"
        android:layout_height="60.0dip"
        android:src="@drawable/ic_launcher"
        android:scaleType="fitCenter"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_alignWithParentIfMissing="true"
        android:contentDescription="@string/_empty_strings" />
    <LinearLayout
        android:orientation="vertical"
        android:id="@+id/compositeListViewItemFirstLinearLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/compositeListViewItemThumbnailImage"
        android:layout_alignTop="@+id/compositeListViewItemThumbnailImage"
        android:layout_alignBottom="@+id/compositeListViewItemThumbnailImage"
        android:layout_alignParentRight="true">
        <TextView
            android:textSize="16.0sp"
            android:gravity="center_vertical"
            android:id="@+id/compositeListViewItemFirstLineText"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello_world"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:lines="1"
            android:inputType="textNoSuggestions" />
        <TextView
            android:textSize="12.0sp"
            android:id="@+id/compositeListViewItemSecondLineText"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello_world"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:lines="1"
            android:inputType="textNoSuggestions" />
        <TextView
            android:textSize="14.0sp"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:lines="1"
            android:layout_gravity="center_vertical"
            android:id="@+id/compositeListViewItemThirdLineText"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello_world"
            android:inputType="textNoSuggestions" />
    </LinearLayout>
    <Button
        android:id="@+id/compositeListViewItemActionButton"
        android:paddingLeft="5.0dip"
        android:paddingTop="5.0dip"
        android:paddingRight="5.0dip"
        android:paddingBottom="5.0dip"
        android:focusable="false"
        android:layout_width="wrap_content"
        android:layout_height="30.0dip"
        android:layout_marginTop="30.0dip"
        android:layout_marginRight="0.0dip"
        android:text="@string/hello_world"
        android:layout_alignParentRight="true"
        style="?android:attr/buttonStyleSmall" />
</RelativeLayout>

as it might / might not be affected by the adapter view, i also put it here

public class SimpleAdapter extends ArrayAdapter implements Serializable {
    private ArrayList<?> items;
    private int itemLayout;
    private String[] itemFieldsName;
    private int[] itemElements;
    private Context ctx;

    public SimpleAdapter(Context ctx, ArrayList<?> items, int itemLayout, int[] itemElements, String[] itemFieldsName) {
        super(ctx, itemLayout, items);
        this.items = items;
        this.itemElements = itemElements;
        this.itemLayout = itemLayout;
        this.itemFieldsName = itemFieldsName;
        this.ctx = ctx;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        View itemView = convertView;
        if (itemView == null) {
            itemView = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(this.itemLayout, null);
        }
        Object item = this.items.get(position);
        for (int i = 0; i < this.itemElements.length; i++) {
            Object value = getValue(item, Character.toUpperCase(this.itemFieldsName[i].charAt(0)) + this.itemFieldsName[i].substring(1));
            View elementView = null;
            if (itemView != null) {
                elementView = itemView.findViewById(this.itemElements[i]);
                if ((elementView instanceof TextView)) {
                    ((TextView) elementView).setText(value.toString());
                } else if ((elementView instanceof SmartImageView)) {
                    ((SmartImageView) elementView).setImageUrl(value.toString());
                } else if ((elementView instanceof Button)) {
                    ((Button) elementView).setText(value.toString());
                }
            }
        }
        return itemView;
    }

    private Object getValue(Object obj, String fieldName) {
        ArrayList<String> fieldsName = new ArrayList<String>();
        int length;
        String str = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
        Object value = new Object();
        try {
            value = obj.getClass().getMethod("get" + str, new Class[0]).invoke(obj);
        } catch (Exception ex) {
            value = fieldName;
        }
        return value;
    }

    private String trimString(String string, int length, boolean soft) {
        if(string == null || string.trim().isEmpty()){
            return string;
        }
        StringBuffer sb = new StringBuffer(string);
        int actualLength = length - 3;
        if(sb.length() > actualLength){
            if(!soft)
                return sb.insert(actualLength, "...").substring(0, actualLength+3);
            else {
                int endIndex = sb.indexOf(" ",actualLength);
                return sb.insert(endIndex,"...").substring(0, endIndex+3);
            }
        }
        return string;
    }
}

Can someone tell me where i was wrong?,

tell me if you need more explanation / code resource

DeckyFx
  • 1,308
  • 3
  • 11
  • 32
  • what is actual problem ? – Mukesh Kumar Singh Dec 23 '13 at 09:43
  • 6
    Checkout [holder pateren](http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder) – Blaz Dec 23 '13 at 09:43
  • @Kumar i edited my question, sorry for being not clear, plese re-read them – DeckyFx Dec 23 '13 at 09:53
  • 1
    It seams that you're `getView()` is doing a lot of extra actions (calculations). As #blazsolar said try using the ViewHolder pattern. And try to refactor you're code, to avoid using `instanceof` checks and getting methods via reflection. This only increases the time of every `getView()` method. – Serj Lotutovici Dec 23 '13 at 10:03

2 Answers2

3

Finding an inner view inside an inflated layout is among the most common operations in Android. This is usually done through a View method called findViewById(). This method will recursively go through the view tree looking for a child with a given IDcode. Using findViewById() on static UI layouts is totally fine but, as you’ve seen, ListView calls the adapter’s getView() very frequently when scrolling. findViewById() might perceivably hit scrolling performance in ListViews—especially if your row layout is non-trivial.

The View Holder pattern is about reducing the number of findViewById() calls in the adapter’s getView(). In practice, the View Holder is a lightweight inner class that holds direct references to all inner views from a row. You store it as a tag in the row’s view after inflating it. This way you’ll only have to use findViewById() when you first create the layout. Here’s the code sample with View Holder pattern applied:

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;

if (convertView == null) {
    convertView = mInflater.inflate(R.layout.your_layout, null);

    holder = new ViewHolder();
    holder.text = (TextView) convertView.findViewById(R.id.text);

    convertView.setTag(holder);
} else {
    holder = convertView.getTag();
}

holder.text.setText("Position " + position);

return convertView;

}

private static class ViewHolder {
public TextView text;

}

M D
  • 47,665
  • 9
  • 93
  • 114
  • Beside using viewholder, string length displayed in listview likely affect this problem too. Before, i put a very long string directly acquired from network, now after i trim the string the view is much lighter. Thanks, i mark your answer as correct – DeckyFx Dec 24 '13 at 06:16
  • View Holder makes ListView scrooling fast and easier. best way into android – M D Dec 24 '13 at 06:18
0

There is one known issue with android:singleLine where it kills performance on ListViews drastically. Please see this question Why does android:singleLine="true" make ListView scrolling very laggy?

Remove it from your layout and use maxLines instead.

Community
  • 1
  • 1
MLProgrammer-CiM
  • 17,231
  • 5
  • 42
  • 75