Android: что делать, если производительности ListView по-прежнему недостаточно?

Ну, эта тема действительно много обсуждалась и до сих пор обсуждается, и я уже прочитал много руководств, советов и видел разговоры об этом. Но у меня все еще возникают проблемы с моей реализацией настраиваемого BaseAdapter для ListView всякий раз, когда я достигаю определенной сложности моих строк. Итак, в основном у меня есть некоторые объекты, которые я получаю путем синтаксического анализа xml, поступающего из сети. Вдобавок я получаю некоторые изображения и т. Д., И все это делается в AsyncTask. Я использую подход ViewHandler, оптимизирующий производительность, в моем методе getView () и повторно использую convertView, как это предлагают все. Т.е. Я надеюсь, что я использую ListView, как и предполагалось, и он действительно отлично работает, когда я просто показываю один ImageView и два TextView, стилизованных с помощью SpannableStringBuilder (я вообще не использую HTML.fromHTML).

И вот оно. Всякий раз, когда я расширяю макет строки с помощью нескольких небольших ImageView, Button и еще нескольких TextView, стилизованных по-разному с помощью SpannableStringBuilder, я получаю остановку прокрутки. Строка состоит из RelativeLayout в качестве родителя, а все остальные элементы упорядочены с параметрами макета, поэтому я не могу сделать строку более простой в ее макете.Должен признать, что я никогда не видел примеров реализации ListView со строками, содержащими такое количество элементов пользовательского интерфейса.

Однако, когда я использую TableLayout в ScrollView и заполняю его вручную с помощью AsyncTask (новые строки постоянно добавляются с помощью onProgressUpdate ()), он ведет себя идеально гладко даже с сотнями строк в нем. Он просто немного спотыкается, когда добавляются новые строки, если их прокрутить до конца списка. В остальном это намного более плавно, чем со ListView, где он всегда спотыкается при прокрутке.

Есть ли какие-нибудь предложения, что делать, если ListView просто не хочет работать хорошо? Следует ли мне придерживаться подхода TableLayout или рекомендуется поиграть с ListView, чтобы немного оптимизировать производительность?

Вот реализация моего адаптера:

protected class BlogsSeparatorAdapter extends BaseAdapter {

        private LayoutInflater inflater;
        private final int SEPERATOR = 0;
        private final int BLOGELEMENT = 1;

        public BlogsSeparatorAdapter(Context context) {
                inflater = LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
                return blogs.size();
        }

        @Override
        public Object getItem(int position) {
                return position;
        }

        @Override
        public int getViewTypeCount() {
                return 2;
        }

        @Override
        public int getItemViewType(int position) {
                int type = BLOGELEMENT;
                if (position == 0) {
                        type = SEPERATOR;
                } else if (isSeparator(position)) {
                        type = SEPERATOR;
                }
                return type;
        }

        @Override
        public long getItemId(int position) {
                return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                UIBlog blog = getItem(position);
            ViewHolder holder;
            if (convertView == null) {
            holder = new ViewHolder();

            convertView = inflater.inflate(R.layout.blogs_row_layout, null);
            holder.usericon = (ImageView) convertView.findViewById(R.id.blogs_row_user_icon);
            holder.title = (TextView) convertView.findViewById(R.id.blogs_row_title);
            holder.date = (TextView) convertView.findViewById(R.id.blogs_row_date);
            holder.amount = (TextView) convertView.findViewById(R.id.blogs_row_cmmts_amount);
            holder.author = (TextView) convertView.findViewById(R.id.blogs_row_author);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }           
        holder.usericon.setImageBitmap(blog.icon);
        holder.title.setText(blog.titleTxt);
        holder.date.setText(blog.dateTxt);
        holder.amount.setText(blog.amountTxt);
        holder.author.setText(blog.authorTxt);          

                    return convertView;
        }

        class ViewHolder {
                TextView separator;
                ImageView usericon;
                TextView title;
                TextView date;
                TextView amount;
                TextView author;
        }

        /**
         * Check if the blog on the given position must be separated from the last blogs.
         * 
         * @param position
         * @return
         */
        private boolean isSeparator(int position) {
                boolean separator = false;
                // check if the last blog was created on the same date as the current blog
                if (DateUtility.getDay(
                                DateUtility.createCalendarFromUnixtime(blogs.get(position - 1).getUnixtime() * 1000L), 0)
                                .getTimeInMillis() > blogs.get(position).getUnixtime() * 1000L) {
                        // current blog was not created on the same date as the last blog --> separator necessary
                        separator = true;
                }
                return separator;
        }
}

Это xml для строки (без кнопки, все еще спотыкается ):


    
    
    
    
    
    

** * ** * ** * * ОБНОВЛЕНИЕ * ** * ** * ** * ** *

Как оказалось, проблема была просто решена с помощью ArrayAdapter вместо BaseAdapter. Я использовал тот же код с ArrayAdapter, и разница в производительности ГИГАНТНАЯ! Он работает так же гладко, как и TableLayout.

Поэтому всякий раз, когда я использую ListView, я определенно избегаю использования BaseAdapter, поскольку он значительно медленнее и менее оптимизирован для сложных макетов. Это довольно интересный вывод, потому что я не читал об этом ни слова в примерах и руководствах. Или, возможно, я не читал точно.; -)

Что ж, однако это код, который работает без сбоев (как вы можете видеть, в моем решении используются разделители для группировки списка):

protected class BlogsSeparatorAdapter extends ArrayAdapter {

    private LayoutInflater inflater;

    private final int SEPERATOR = 0;
    private final int BLOGELEMENT = 1;

    public BlogsSeparatorAdapter(Context context, List rows) {
        super(context, R.layout.blogs_row_layout, rows);
        inflater = LayoutInflater.from(context);
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        int type = BLOGELEMENT;
        if (position == 0) {
            type = SEPERATOR;
        } else if (isSeparator(position)) {
            type = SEPERATOR;
        }
        return type;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final UIBlog blog = uiblogs.get(position);
        int type = getItemViewType(position);

        ViewHolder holder;
        if (convertView == null) {
            holder = new ViewHolder();
            if (type == SEPERATOR) {
                convertView = inflater.inflate(R.layout.blogs_row_day_separator_item_layout, null);
                View separator = convertView.findViewById(R.id.blogs_separator);
                separator.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        // do nothing
                    }
                });
                holder.separator = (TextView) separator.findViewById(R.id.blogs_row_day_separator_text);
            } else {
                convertView = inflater.inflate(R.layout.blogs_row_layout, null);
            }
            holder.usericon = (ImageView) convertView.findViewById(R.id.blogs_row_user_icon);
            holder.title = (TextView) convertView.findViewById(R.id.blogs_row_title);
            holder.date = (TextView) convertView.findViewById(R.id.blogs_row_date);
            holder.amount = (TextView) convertView.findViewById(R.id.blogs_row_author);
            holder.author = (TextView) convertView.findViewById(R.id.blogs_row_author);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        if (holder.separator != null) {
            holder.separator
                    .setText(DateUtility.createDate(blog.blog.getUnixtime() * 1000L, "EEEE, dd. MMMMM yyyy"));
        }
        holder.usericon.setImageBitmap(blog.icon);
        holder.title.setText(createTitle(blog.blog.getTitle()));
        holder.date.setText(DateUtility.createDate(blog.blog.getUnixtime() * 1000L, "'um' HH:mm'Uhr'"));
        holder.amount.setText(createCommentsAmount(blog.blog.getComments()));
        holder.author.setText(createAuthor(blog.blog.getAuthor()));
        return convertView;
    }

    class ViewHolder {
        TextView separator;
        ImageView usericon;
        TextView title;
        TextView date;
        TextView amount;
        TextView author;
    }

    /**
     * Check if the blog on the given position must be separated from the last blogs.
     * 
     * @param position
     * @return
     */
    private boolean isSeparator(int position) {
        boolean separator = false;
        // check if the last blog was created on the same date as the current blog
        if (DateUtility.getDay(
                DateUtility.createCalendarFromUnixtime(blogs.get(position - 1).getUnixtime() * 1000L), 0)
                .getTimeInMillis() > blogs.get(position).getUnixtime() * 1000L) {
            // current blog was not created on the same date as the last blog --> separator necessary
            separator = true;
        }
        return separator;
    }
}

++++++++++++++++ ВТОРОЕ РЕДАКТИРОВАНИЕ С ДОПОЛНЕНИЯМИ +++++++++++++++++++++ {{ 1}} Просто чтобы показать, что BaseAdapter ДЕЛАЕТ нечто иное, чем ArrayAdapter. Это всего лишь вся трассировка, поступающая от метода getView () с ТОЧНЫМ одинаковым кодом в обоих адаптерах.

Сначала количество звонков http://img845.imageshack.us/img845/5463/tracearrayadaptercalls.png

http://img847.imageshack.us/img847/7955/tracebaseadaptercalls.png

Исключительное потребление времени http://img823.imageshack.us/img823/6541/tracearrayadapterexclus.png

http://img695.imageshack.us/img695/3613/tracebaseadapterexclusi.png

Включенное потребление времени http://img13.imageshack.us/img13/4403/tracearrayadapterinclus.png

http://img831.imageshack.us/img831/1383/tracebaseadapterinclusi.png

Как видите, между этими двумя адаптерами существует ОГРОМНАЯ разница (ArrayAdapter в четыре раза быстрее в методе getView ()). И я действительно не понимаю, почему это так драматично. Я могу только предположить, что ArrayAdapter имеет какое-то лучшее кеширование или дополнительную оптимизацию.

+++++++++++++++++++++++++ ДРУГОЕ ОБНОВЛЕНИЕ ++++++++++++++++ Чтобы показать вам, как построен мой текущий класс UIBlog :

private class UIBlog {
    Blog blog;
    CharSequence seperatorTxt;
    Bitmap icon;
    CharSequence titleTxt;
    CharSequence dateTxt;
    CharSequence amountTxt;
    CharSequence authorTxt;
}

Чтобы прояснить, я использую это для ОБЕИХ адаптеров.

9
задан emlai 15 July 2015 в 01:09
поделиться