Подтвердить что ты не робот

ListView очень медленный при прокрутке (с использованием ViewHolder/recycling)

Я снова попробую Android-разработчик. У меня валяется "старый" телефон HTC Hero, поэтому я загрузил его, сделал некоторые обновления и теперь снова работаю с Eclipse и остальными.

У меня работает Android 2.1 на устройстве.

Я сделал очень простое тестовое приложение, которое ничего не делает, кроме показа некоторых действий и тому подобного. Несмотря на то, что нет соединения с базой данных, данные не выбираются из любой сети, приложение работает очень медленно.

Например, у меня есть ListView с некоторыми пользовательскими элементами макета. При добавлении только 6-7 элементов (чтобы получить прокрутку), при прокрутке происходит очень медленно. Кроме того, у меня есть несколько кнопок, которые изменяют активность, а также очень и очень медленно:

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

Я не могу понять, почему.

Адаптер 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);
    }
}

Основная деятельность, 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));

    }
}

Пользовательский элемент списка, 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();
            }

        });
    }
}

И, наконец, макет NodePickup XML

<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>

Обновление

Если я удалю изображение в NodePickup, отставание исчезнет. Вопрос - почему?

4b9b3361

Ответ 1

UPDATE 2011-08-29 Если я удалю изображение в NodePickup, отставание исчезнет.

В представлении сложно определить способ отображения макета. Xml, который вы опубликовали, не очень помогают. Если вы удалите ImageView, то LinearLayout02 примет всю ширину родителя. Но наличие imageView со стандартными размерами и макетом справа будет fill_parent, много смущает вид. Запросы размера imageView снова, чтобы "надавить поля вправо" (вид). Взгляните на мои предложения ниже

Tip1

используйте вес свойства LinearLayout. Сделайте imageView fill_parent и LinearLayout тоже (для ширины) и играйте с весовыми свойствами. Сделайте это также для вертикальной компоновки с TextViews. Лучшее решение, которое должно было бы установить фиксированный размер на высоту мысли TextView. Также подумайте о том, чтобы изменить вид сверху на RelativeLayout. Сделайте изображение со стандартными размерами, AlignParentLeft и поместите изображение LinearLayout02 toRightOf. Вы значительно облегчите onMeasure ViewGroup.

Tip2

Похоже, что когда текст меняет высоту, весь вид нужно переустановить. Обычная техника, чтобы избежать того, чтобы она создавала элемент списка, фиксированный по высоте. Таким образом, listview может повторно использовать переработанные представления без повторного включения.

Tip3

Дайте LinearLayout02 фиксированную высоту 64dp или Fill_Parent, так как у вас нет свободного пространства, но макет не знает этого и каждый раз перестраивает его самостоятельно, так как текст также является Wrap_content.

Кроме того, вы сказали, что если вы удалите ImageView, все будет быстро снова. Если приведенное выше действие не имеет эффекта, можете ли вы попробовать это? Поскольку вы знаете, что размер imageView исправлен.

Расширьте свой ImageView и переопределите метод requestLayout().

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
         */
    }
}

Теперь включите виджет MyImageView в ваш XML.

<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 >

Ответ 2

После оптимизации моего метода getView() для использования держателя и повторного использования convertView, если он не null, мой список был по-прежнему довольно лагги. Затем я пробовал

listView.setScrollingCacheEnabled(false);

и он решил это сразу.

Надеюсь, это поможет кому-то.

Ответ 3

Я только что открыл это, и я хочу поделиться им с вами, ребята.

Я попробовал ВСЕ предлагаемые решения, но ничего не получилось. В результате возникла проблема с длиной текста, который я кормлю одним из своих представлений TextView, потому что я использую

mTextView.SetText(theLongString)

в моем адаптере в моем методе getView. Теперь, когда я сжал свою строку, список очень ОЧЕНЬ гладкий:)

Ответ 4

Прошло немало времени! Я все пробовал. Отключение кеша прокрутки, viewHolder, cacheColorHint... но ничего не получилось!

После долгих поисков я нашел корень всего зла!

В моем theme.xml у меня было масштабируемое фоновое изображение:

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

После того, как вы удалили стопку, все было смазано маслом.

Надеюсь, это поможет кому-то!

Ответ 5

Чтобы повысить производительность списка, используйте обе или одну из двух - (простая реализация)

  • ViewHolder
  • AsyncTask (отдельный поток)

Если у вас умеренно длинный список, я рекомендую ViewHolder иначе для больших списков (например, в случае с музыкальным проигрывателем), я рекомендую использовать ViewHolder вместе с AsyncTask. Ключом к сглаживанию прокрутки ListView является сокращение потребления памяти в максимально возможной степени.

Для реализации ViewHolder просто. Просто создайте статический класс в своем пользовательском адаптере, который содержит все виды, которые вы найдете через findViewById. Так вот как это должно выглядеть -

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

Затем, в следующий раз, когда вам нужно найтиПросмотр любого из этих представлений, просто ссылайтесь на ViewHolder так:

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);

Это проверенный код и взятый из одного из моих проектов. Однако исходный источник здесь - http://developer.android.com/training/improving-layouts/smooth-scrolling.html

Списки становятся более плавными, используя ViewHolder. Использование AsyncTask немного сложнее, но посмотрите ссылку, если вы хотите реализовать AsyncTask вместе с ViewHolder для более плавного прокрутки. Удачи!

Ответ 6

Загрузите изображение один раз в виде растрового изображения и примените его к ImageView программно после его раздувания. Если вам нужны разные изображения для каждого элемента, вы должны создать битмапы асинхронно, как описано в Android: как оптимизировать ListView с ImageView + 3 TextViews?

Ответ 7

Попробуйте использовать android:cacheColorHint="#00000000" для своего списка. Чтобы улучшить производительность чертежа во время операций прокрутки, платформа Android повторно использует подсказку о кеше.

Ссылка: developer.android.com статья.

Ответ 8

Это может помочь кому-то

Если у вас есть изображение в элементе списка, вы должны помнить о снижении качества этого изображения. Он быстрее загружает загрузку в несколько килобайт, чем несколько мегабайт.

Это помогло мне

 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;
    }

Ответ 9

У меня была такая же проблема, пока я использовал другой макет, такой как LinearLayout, RelativeLayout, CardView как родительский элемент другого дочернего элемента в том же элементе списка. Я решил эту проблему, изменив все представление внутри RelativeLayout.

Использование RelativeLayout как основного, а его дочерний макет может увеличить скорость загрузки каждого элемента. Таким образом, прокрутка будет гладкой.

<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>

Ответ 10

Вы можете использовать listview.setScrollingCacheEnabled(false).При прокрутке области хранения приложения listview, выбрасывая исключение из памяти (OOM). Мое решение работает для меня.