WebViews的ListView - 重用不同高度的项目失败;在显示任何视图之前询问所有视图

时间:2011-01-27 17:07:37

标签: android android-webview android-listview android-linearlayout

我的活动显示ListViewListView中的每个项目都是由LinearLayout组成的WebView。列表中可能有数百个项目,每个项目的高度不同。

第一个问题是,在getView()重新使用循环播放的视图时,新视图始终是原始视图的高度,即使我为{{1}设置layout_height也是如此}和LinearLayoutWebView

第二个问题是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>

这是我的适配器中的getView()。 HTML片段现在来自一系列字符串。

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

4 个答案:

答案 0 :(得分:4)

我认为问题在于,使用您编写的代码系统首先测量容器高度(行linear_layout),然后测量内容(WebView)。但是第二个测量不会影响第一个测量,直到你调用invalidate或一些使容器重新计算其大小的方法。

关于为什么多次调用getView方法,请检查适配器的getViewTypeCount()方法。

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

Documentation声明:

  

“此方法返回的数量   将由其创建的视图类型   getView(int,View,ViewGroup)。每   type表示一组视图   可以在getView中转换(int,View,   ViewGroup中)。如果适配器总是   为所有人返回相同类型的View   项目,此方法应返回1。   此方法仅在调用时调用   当适配器设置在   适配器视图“。

我遇到了这个方法的问题,给了一个很大的数字(比如5000)使我的应用程序崩溃,看起来它在内部用于一些ListView计算。

希望它有所帮助。

顺便说一句,关于the world of ListViews

的讨论非常好

答案 1 :(得分:3)

我的ExpandableListView遇到了同样的问题。从其他帖子看,似乎是一个持续存在的问题,在一个列表中有多个webview。我找到的工作是在第一次调用getView时创建和调用wewViews,然后在正确的索引处返回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

答案 2 :(得分:3)

我尝试过sirFunkenstine的解决方案,效果很好。只有我正在构建此应用程序的客户端不喜欢这种性能。而且我不得不承认它不是那么顺利。

似乎添加和删除预装的WebViews所在的问题。每次应用程序添加/删除下一行的新WebView时,UI都会被冻结一段时间。

<强>解决方案

所以我认为最好只调整WebView的内容和高度而不是添加/删除。这样我们只有大量当前在内存中可见的WebView。我们可以使用ListView / Adapter的重用功能。

所以我在ListView后面添加了一个WebView,它不断计算下一个项目的WebView高度。您可以使用WebView方法getContentHeight获取高度。您必须在onPageFinished方法中执行此操作,以便获得最终高度。这引发了另一个问题,因为当使用loadDataWithBaseURL或loadData方法加载内容时,该方法返回零。似乎最终设置了值,但是在调用onPageFinished时尚未设置。为了解决这个问题,你可以添加一个线程,不断检查getContentHeight是否不再为零。如果它不为零则设置该值,您可以加载下一个。

整个解决方案有点笨拙,但它给了我一个漂亮而流畅的ListView,包括不同高度的WebView。

一些示例代码:

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 :(得分:1)

对于我的问题,我的解决方案(即使我把赏金放在上面)是将​​ImageView放入FrameLayout,而不是聪明地增长。

使视图无效或强制布局计算对我没有任何作用,但imageview的framelayout容器似乎解决了所有问题。我认为这种行为是错误的,但解决方法很简单。

也许同样适用于OP的webview。