ListView of WebViews - повторное использование элементов разной высоты не выполняется;запрашивает все представления перед отображением любого - PullRequest
8 голосов
/ 27 января 2011

У меня есть активность, которая отображает ListView. Каждый элемент в ListView является LinearLayout, состоящим из одного WebView. В списке потенциально могут быть сотни предметов, каждый из которых имеет разную высоту.

Первая проблема заключается в том, что при повторном использовании переработанного вида в getView() новый вид всегда равен высоте исходного вида, хотя я установил layout_height для LinearLayout и WebView. до wrap_content.

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

Итак ... Мне нужно выяснить, как принудительно переработать WebViews, чтобы отобразить их новое содержимое, чтобы можно было рассчитать их высоту, а не просто использовать предыдущую высоту. И я хотел бы знать, почему система запрашивает у меня ВСЕ мои товары в этом случае.

Вот необходимые фрагменты кода:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <ListView 
        android:id="@+id/topPane"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dividerHeight="1.0px"
        android:divider="#FFFFFF"
        android:smoothScrollbar="false"
        />
</LinearLayout>

Ряды построены из:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
    <WebView 
        android:id="@+id/rowWebView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="0.0px"
        android:scrollbars="none"
        />
</LinearLayout>

Это getView () в моем адаптере. На данный момент фрагменты HTML взяты из массива строк.

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
        {
        String item = (String) getItem(position);
        if (convertView == null)
            {
            convertView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.rowview, parent, false);
            }
        WebView wv = (WebView) convertView.findViewById(R.id.rowWebView);
        wv.loadDataWithBaseURL(null, item, "text/html", "utf-8", "about:blank");
        return convertView;
        }

Ответы [ 4 ]

4 голосов
/ 19 мая 2011

Думаю, проблема в том, что с помощью написанного вами кода система сначала измеряет высоту контейнера (строка linear_layout), а затем содержимое (WebView). Но вторая мера не влияет на первую, пока вы не вызовете invalidate или какой-либо метод, который заставляет контейнер пересчитать его размер.

О том, почему ваш метод getView вызывается много раз, проверьте метод getViewTypeCount () вашего адаптера.

 <code>@Override
 public int getViewTypeCount()
 {
     return NumberOfTypeOfViewsInYourList;
 }

Документация гласит:

"этот метод возвращает число типы представлений, которые будут созданы getView (int, View, ViewGroup). каждый Тип представляет собой набор представлений, которые можно преобразовать в getView (int, View, ViewGroup). Если адаптер всегда возвращает один и тот же тип просмотра для всех элементы, этот метод должен вернуть 1. Этот метод будет вызываться только когда когда адаптер установлен на AdapterView. "

У меня была проблема с этим методом, из-за большого числа (например, 5000) мое приложение зависало, похоже, оно используется внутри для некоторых вычислений ListView.

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

Кстати, очень хороший разговор о мире ListViews

3 голосов
/ 14 ноября 2013

Я пробовал решение sirFunkenstine, которое прекрасно работает.Только клиенту, для которого я создаю это приложение, не понравилось исполнение.И я должен был признать, что это было не так гладко.

Казалось, что добавление и удаление предварительно загруженных WebViews, где проблема.Пользовательский интерфейс зависал на долю секунды каждый раз, когда приложение добавляло / удаляло новый WebView для следующей строки.

Решение

Поэтому я подумал, что будет лучшенастраивайте только содержимое и высоту WebView вместо добавления / удаления.Таким образом, у нас есть только количество веб-представлений, которые в данный момент видны в памяти.И мы можем использовать функцию повторного использования ListView / Adapter.

Поэтому я добавил один WebView за ListView, который постоянно вычисляет высоту WebView следующих элементов.Вы можете получить высоту с помощью метода WebView getContentHeight.Вам нужно будет сделать это в методе onPageFinished, чтобы получить окончательную высоту.Это подняло еще одну проблему, потому что метод возвращает ноль, когда содержимое загружается методами loadDataWithBaseURL или loadData.Кажется, что значение устанавливается в конце концов, но еще не во время вызова onPageFinished.Чтобы преодолеть это, вы можете добавить поток, который постоянно проверяет, является ли getContentHeight больше не равным нулю.Если оно не равно нулю, тогда значение установлено, и вы можете загрузить следующее.

Это решение выглядит несколько странно, но оно дало мне приятный и плавный ListView, включая WebViews с разной высотой.

Пример кода:

1: поставить в очередь позиции строк, которые вы хотите предварительно загрузить:

private SparseArray<Integer> mWebViewHeights = new SparseArray<Integer>();
private LinkedBlockingQueue mQueue;
{
    mQueue = new LinkedBlockingQueue();
    try {
        mQueue.put(position);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

2: запустить Runnableчтобы постоянно загружать новые элементы из очереди и проверять / добавлять высоту в массив:

Handler h;
Runnable rowHeightCalculator = new Runnable() {
    h = new Handler();
    rowHeightCalculator.run();
}

3: загрузить новое содержимое HTML и проверить высоту:

Handler h;
Runnable rowHeightCalculator = new Runnable() {

    int mCurrentIndex = -1;
    boolean mLoading = false;

    // Read the webview height this way, cause oncomplete only returns 0 for local HTML data
    @Override
    public void run() {
        if(Thread.interrupted())
            return;

        if (mCurrentIndex == -1 && mQueue.size() > 0) {
            try {
                mCurrentIndex = (Integer)mQueue.take();
                String html = mItems.get(mCurrentIndex).getPart().getText();

                mDummyWebView.clearView();
                mDummyWebView.loadDataWithBaseURL("file:///android_asset/reader/", "THE HTML HERE", "text/html", "UTF-8", null);
                    mLoading = true;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        if(mDummyWebView.getContentHeight() != 0 && mCurrentIndex >= 0) {
            int contentHeight = mDummyWebView.getContentHeight();
            mWebViewHeights.append(mCurrentIndex, contentHeight);
            mCurrentIndex = -1;
            mLoading = false;
        }

        // Reload view if we loaded 20 items
        if ((mQueue.size() == 0 && (mItemsCount - mQueue.size()) < 20) || (mItemsCount - mQueue.size()) == 20) {
            notifyDataSetChanged();
        }

        if (mQueue.size() > 0 || mLoading) {
            h.postDelayed(this, 1);
        }
    }
};
3 голосов
/ 13 августа 2013

У меня была такая же проблема с моим ExpandableListView.Судя по другим постам, это постоянная проблема с несколькими веб-просмотрами в одном списке.Обход, который я нашел, заключался в создании и массиве wewViews при первом вызове getView, а затем возвращении webView с правильным индексом.Это немного больше памяти, но это не приводит к изменению размера представления.

SparseArray<WebView> wvGroup = new SparseArray<WebView>();
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    WebView wv = null;

    if (wvGroup.size() < 1) {
        for (int i = 0; i < sectionItems.size(); i++) {
            WebView webViewItem = new WebView(context);
            String htmlData = "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + sectionItems.get(i).child_content;
            webViewItem.loadDataWithBaseURL("file:///android_assets/", htmlData, "text/html", "UTF-8", null);
            wvGroup.append(i, webViewItem);
        }


    }
    wv = wvGroup.get(groupPosition);
    wv.setPadding(30, 10, 10, 10);

return wv
1 голос
/ 21 мая 2011

Мое решение (даже несмотря на то, что я увеличил вознаграждение) для моей проблемы состояло в том, чтобы поместить ImageView в FrameLayout, он разумно вырос.контейнер framelayout для viewview, казалось, решил все.Я предполагаю, что это поведение ошибочно, но обходной путь был достаточно простым.

Может быть, то же самое будет применяться к веб-просмотру ОП.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...