14

I'm back at trying out some Android dev again. I have an "old" HTC Hero phone lying around, so I booted that one up, did some updates and are now up n running again with Eclipse and the rest.

I have Android 2.1 running on the device.

I have made a very simple test app that doesn't do anything at all except for showing some Activities and such. Even though there is no database connection, no data fetched from any network the app is very slow.

For example, I have a ListView with some custom layout items. When adding only 6-7 items (so that I get the scrolling) it is very slow when scrolling. Also, I have some buttons that changes the Activity and also that is very very slow:

mButtonListenerUPP = new OnClickListener() {
    @Override
    public void onClick(View v)
    {
        Intent myIntent = new Intent(BaseActivity.this, ActivityMain.class);
        BaseActivity.this.startActivity(myIntent);
    }
};

I cannot figure out why.

The Adapter, NodeRowAdapter

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.view.*;
import android.widget.ArrayAdapter;

import android.widget.TextView;

public class NodeRowAdapter extends ArrayAdapter 
{
    private Activity context;
    private ArrayList<Node> mList;
    private LayoutInflater inflater;

    NodeRowAdapter(Activity context, ArrayList<Node> objects) 
    {
        super(context, R.layout.nodepickup, objects);
        this.context=context;
        mList = objects;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    class ViewHolder 
    {
        TextView name;
        TextView time;
        TextView road;
        Node node;
    }

    public Node getNode(int position)
    {
        if (mList != null && mList.size() > position)
            return mList.get(position);
        else
            return null;
    }
    public View getView(int position, View convertView, ViewGroup parent) 
    {
        View view = convertView;
        ViewHolder holder;
        if (view == null)
        {
            view = inflater.inflate(R.layout.nodepickup, parent, false);
            holder = new ViewHolder();
            holder.name =(TextView)view.findViewById(R.id.name);
            holder.time =(TextView)view.findViewById(R.id.time);
            holder.road =(TextView)view.findViewById(R.id.road);
            view.setTag(holder);
        }
        else
        {
            holder = (ViewHolder) convertView.getTag();
        }

        Node node = mList.get(position);
        holder.name.setText(node.name);
        holder.time.setText(node.time);
        holder.road.setText(node.road);

        return(view);
    }
}

The main activity, ActivityMain

public class ActivityMain extends BaseActivity 
{
    private NodeRowAdapter _nodeRowAdapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        SICApplication._myContext = this;
        SICApplication._myContext = this;

        _nodeRowAdapter = new NodeRowAdapter(this, SICApplication.dataHolder_activityMain._nodes);
        ListView listView1 = (ListView) findViewById(R.id.ListViewNodes);
        listView1.setOnItemClickListener(new OnItemClickListener() 
        {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
            {
                Node node = _nodeRowAdapter.getNode(position);
                Log.v("MyApp", "Node=" + node.name);
            } 
        });
        listView1.setAdapter(_nodeRowAdapter);  

    }

    /* Handles item selections */
    public boolean onOptionsItemSelected(MenuItem item) 
    {
        switch (item.getItemId()) 
        {
            case R.id.add_item:
                addNodeItem();
                return true;
        }
        return false;
    }



    private void addNodeItem()
    {
        _nodeRowAdapter.add(new Node("Test", "asd asd ", "14:00", 1));

    }
}

The custom list item, NodePickup

public class NodePickup extends LinearLayout
{
    public NodePickup(Context context, AttributeSet attributeSet)
    {
        super(context, attributeSet);

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.nodepickup, this);

        this.setOnClickListener(new OnClickListener() 
        {
            @Override
            public void onClick(View v)
            {
                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
                builder.setMessage("Ajabaja!")
                .setCancelable(true)
                .setPositiveButton("JA!", new DialogInterface.OnClickListener() 
                {
                   public void onClick(DialogInterface dialog, int id) 
                   {
                       dialog.cancel();
                   }
                });
                builder.show();
            }

        });
    }
}

And lastly, the NodePickup XML layout

<LinearLayout 
    android:id="@+id/LinearLayout01" 
    android:layout_width="fill_parent" 
    android:layout_height="64dip"
    android:orientation="horizontal" 
    android:background="@drawable/stateful_background"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <ImageView 
        android:id="@+id/ImageView01" 
        android:layout_width="40dip" 
        android:layout_height="40dip"
        android:src="@drawable/arrow_up_green"
        android:background="@android:color/transparent">
    </ImageView>

    <LinearLayout 
        android:id="@+id/LinearLayout02"
        android:background="@android:color/transparent"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:orientation="vertical"
        xmlns:android="http://schemas.android.com/apk/res/android">

        <TextView
            android:text="14:46 (15 min)"
            android:id="@+id/time"
            android:textSize="12dip"
            android:textColor = "#000000"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent">
        </TextView>

        <TextView
            android:text="test"
            android:id="@+id/road"
            android:textSize="12dip"
            android:textColor = "#000000"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent">
        </TextView>

        <TextView
            android:text="test test"
            android:id="@+id/name"
            android:textSize="12dip"
            android:textColor = "#000000"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent">
        </TextView>
    </LinearLayout>
</LinearLayout>

Update

If I remove the image in the NodePickup, the lagginess is gone. The question is - why?

halfer
  • 19,824
  • 17
  • 99
  • 186
Ted
  • 19,727
  • 35
  • 96
  • 154
  • Use Traceview to determine where your time is being taken up. – CommonsWare Aug 29 '11 at 06:16
  • Thx, I tried that. Didnt tell me anything at all. Maybe I am missing somethuing. However, it seem it is the ImageView that does it. If I remove that from NodePickup, its fast again – Ted Aug 29 '11 at 09:24

10 Answers10

17

UPDATE 2011-08-29 If I remove the image in the NodePickup, the lagginess is gone.

The view has a hard time figuring how the layout should be rendered. The xml you posted don't help much. If you remove the ImageView then the LinearLayout02 will take all the width of the parent. But having the imageView with standar dimentions and the layout to the right will fill_parent confuses the view a lot. Requests the size of the imageView again to "push the margins to the right" (kind of). Take a look at my suggestions below

Tip1

use the LinearLayout property weight. Make the imageView fill_parent and the LinearLayout too (for the width) and play with the weight properties. Do that also for the vertical layout with the TextViews. The best Solution whould be to put a fixed size to the height of the TextViews thought. Also consider to change the top view to RelativeLayout. Make the image with standar dimentions , AlignParentLeft and put the LinearLayout02 toRightOf imageView. You will relief the onMeasure of the ViewGroup a lot.

Tip2

It seems like when the text changes height the whole view need to be reinflated.A common technic to avoid that it to make list Item fixed in height. So the listview can reuse the recycled views without reinflating.

Tip3

Give your LinearLayout02 a fixed height of 64dp or Fill_Parent since you don't have any left space, but the Layout don't know that and try to rearrange it self every time since the text is also Wrap_content.

Also you said that if you remove the ImageView everything is fast again.If the above don't have any effect can you please try this? Since you know that imageView size is fixed.

Extend your imageView and override requestLayout() method.

public class MyImageView extends ImageView {

public PImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

}

public PImageView(Context context, AttributeSet attrs) {
    super(context, attrs);

}

public PImageView(Context context) {
    super(context);

}

@Override
    public void requestLayout() {
        /*
         * Do nothing here
         */
    }
}

Now include the MyImageView widget to your XML like that.

<com.package_name.MyImageView 
        android:id="@+id/ImageView01" 
        android:layout_width="40dip" 
        android:layout_height="40dip"
        android:src="@drawable/arrow_up_green"
        android:background="@android:color/transparent">
 </com.package_name.MyImageView >
weakwire
  • 9,284
  • 8
  • 53
  • 78
  • Thx. Im not sure, the lagginess sort of disappeared even though I didnt do anything to the code. A bit weird, but... I in any case took some of your pointers/tips =) – Ted Sep 05 '11 at 08:51
  • It's incredible hwo smooth my ListView scrolls since I'm using you "Tip 3". How did you figure this out? / Could you explain WHY it is so or if there are any drawbacks? – Johannes Staehlin Dec 14 '13 at 18:21
  • @JohannesStaehlin the view that knows its dimentions doesn't try to figure them out. with wrap_content it is trying to do it in runtime. – weakwire Dec 14 '13 at 22:14
  • tip 3 fixed 80% of my lag. – grantespo Nov 15 '16 at 18:05
16

After optimizing my getView() method to use a holder and to reuse convertView if it's not null, my listview was still pretty laggy. Then, I've tried

listView.setScrollingCacheEnabled(false);

and it solved it at once.

Hope this helps someone.

yunusual
  • 325
  • 3
  • 9
6

I just discovered this and I wanna share it with you guys.

I tried ALL the solutions provided but nothing worked. What was causing the problem is the length of the text I am feeding one of my TextView views because i'm using

mTextView.SetText(theLongString)

in my adapter in my getView method. Now that I shrinked my String, the listview is VERY smooth :)

AlAsiri
  • 717
  • 7
  • 19
1

It took a while! I tried everything. Disabling the scroll cache, viewHolder, cacheColorHint ... but nothing worked!

After searching many hours I found the root of all evil!

In my themes.xml I had a scaling background image:

<item name="android:windowBackground">@drawable/window_bg</item>

After removing the beackground everything was butter smooth.

I hope this helps someone!

Felix
  • 1,097
  • 1
  • 10
  • 16
1

To improve performance of listview use both or any one of the two - (Simple implementation)

  • ViewHolder
  • AsyncTask (a separate thread)

If you have moderately long lists I recommend ViewHolder otherwise for large lists (like in the case of a music player) I recommend using ViewHolder along with AsyncTask. The key to smooth ListView scrolling is to reduce memory consumption as much as possible.

To implement ViewHolder, is simple. Just create a static class in your custom Adapter that holds all the views that you find via findViewById. So this is how it should look -

static class ViewHolder
{
TextView text;
TextView timestamp;
ImageView icon;
ProgressBar progress;
int position;
}

Then, the next time you need to findViewById any of these views, just reference the ViewHolder like this-

ViewHolder holder = new ViewHolder();
holder.icon = (ImageView) yourView.findViewById(R.id.listitem_image);
holder.text = (TextView) yourView.findViewById(R.id.listitem_text);
holder.timestamp = (TextView) yourView.findViewById(R.id.listitem_timestamp);
holder.progress = (ProgressBar) yourView.findViewById(R.id.progress_spinner);

This is tested code and taken from one of my projects. However, the original source is here - http://developer.android.com/training/improving-layouts/smooth-scrolling.html

The lists become smoother using ViewHolder. Using AsyncTask is a little more complex, but do check out the link if you wish to implement the AsyncTask along with ViewHolder for a much smoother scrolling experience. Good Luck!

Advait Saravade
  • 3,029
  • 29
  • 34
0

This Might help some one

If you have an image in your list Item, you have to remember to reduce the quality of that Image. It's allot faster to load in a few Kb's than a few megabytes.

This helped me

 public Bitmap MakeFileSmaller_ToBitmap(File f) {
        try {
            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f), null, o);

            // The new size we want to scale to
            final int REQUIRED_SIZE=200;

            // Find the correct scale value. It should be the power of 2.
            int scale = 1;
            while(o.outWidth / scale / 2 >= REQUIRED_SIZE &&
                    o.outHeight / scale / 2 >= REQUIRED_SIZE) {
                scale *= 2;
            }

            // Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e) {
            Log.d(TAG, "FILE NOT FOUND " );
        }
        Log.d(TAG, "OTHER EXCEPTION");

        return null;
    }
Ruan
  • 3,969
  • 10
  • 60
  • 87
0

I had the same issue before while i was using different layout like LinearLayout, RelativeLayout, CardView as parent of different child in same list view item. I solved that issue by changing all view inside RelativeLayout.

Using RelativeLayout as main and it's child layout may increase the speed of loading each item. So scrolling will be smooth.

<RelativeLayout
android:id="@+id/LinearLayout01"
android:layout_width="fill_parent"
android:layout_height="64dip"
android:orientation="horizontal"
android:background="@drawable/stateful_background"
xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
    android:layout_width="40dip"
    android:layout_height="40dip"
    android:layout_alignParentTop="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:id="@+id/relativeLayout">
    <ImageView
        android:id="@+id/ImageView01"
        android:layout_width="40dip"
        android:layout_height="40dip"
        android:src="@drawable/arrow_up_green"
        android:background="@android:color/transparent">
    </ImageView>
</RelativeLayout>
<TextView
    android:text="14:46 (15 min)"
    android:id="@+id/time"
    android:textSize="12dip"
    android:textColor = "#000000"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:layout_alignParentTop="true"
    android:layout_toRightOf="@+id/relativeLayout"
    android:layout_toEndOf="@+id/relativeLayout" />
<TextView
    android:text="test"
    android:id="@+id/road"
    android:textSize="12dip"
    android:textColor = "#000000"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:layout_below="@+id/time"
    android:layout_toRightOf="@+id/relativeLayout"
    android:layout_toEndOf="@+id/relativeLayout" />
<TextView
    android:text="test test"
    android:id="@+id/name"
    android:textSize="12dip"
    android:textColor = "#000000"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:layout_below="@+id/road"
    android:layout_toRightOf="@+id/relativeLayout"
    android:layout_toEndOf="@+id/relativeLayout"/></RelativeLayout>
Vinil Chandran
  • 1,563
  • 1
  • 19
  • 33
0

You can use listview.setScrollingCacheEnabled(false).When scrolling listview application hold area then throwing Out Of Memory(OOM) exception.My solution is worked for me.

0

Load the image once as Bitmap and apply it to the ImageView programmatically after inflating it. If you need different images per item you should create the Bitmaps asynchronously like described in Android: How to optimize ListView with ImageView + 3 TextViews?

Community
  • 1
  • 1
mattlaabs
  • 486
  • 3
  • 14
  • I will take a look at your suggestion soon =) – Ted Sep 03 '11 at 13:37
  • I ahve different images to be shown, depending on the state of my object. But to fetch them "asynch" seems totally unnecessary. They are packaged with the app, dont need to download them... – Ted Sep 03 '11 at 13:54
  • Even if images are local they might need quite some time to load, e.g., when you want to display contact photos: they can potentially be very large and need to be scaled down. In a ListView this would lead to noticable lag. Packaged images should probably be fine unless you have a very slow device. – mattlaabs Sep 03 '11 at 21:54
0

Try using android:cacheColorHint="#00000000" for your listview. To improve drawing performance during scrolling operations, the Android framework reuses the cache color hint.

Reference: developer.android.com article.

Ron
  • 24,175
  • 8
  • 56
  • 97