【问题标题】:How to display an empty view, progress view and datas in a ListView?如何在 ListView 中显示空视图、进度视图和数据?
【发布时间】:2013-10-03 17:13:14
【问题描述】:

这主要是一个概念问题。我在我的活动中使用 ListFragment。其列表必须显示以下信息之一:

  • 项目列表
  • 如果没有项目,则为空视图
  • 从数据库加载数据时的“加载”视图

空视图由框架自动管理,因为我在 onCreateView 上的 ListFragment 正在创建这样的自定义视图:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance){
    if(Globals.DEBUG_LIFECYCLE)
        Log.d("Fragment", "ONCREATEVIEW");

    return inflater.inflate(R.layout.kanji_list, null);
}

kanji_list的布局是这样的:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<ListView android:id="@id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:listSelector="@android:color/transparent" />

    <ch.shibastudio.kanjinotepad.list.KanjiListEmptyView android:id="@id/android:empty"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="@string/no_kanji"
        android:visibility="gone"/>

数据存储在我的应用程序中嵌入的 SQLite 数据库中。当应用程序要更新到 ListView 时,使用自定义的 Loader 来查询数据库并返回一个 Cursor 给 ListFragment。我使用自定义加载器而不是 CursorLoader,因为我不想创建 ContentProvider。

所以,我想做的是:当应用程序开始从数据库中查询数据时,将显示“正在加载”视图,显示进度图片和一段文字,如“获取数据”。然后,当 Loader 完成它的工作时,我想在 ListView 中显示查询到的数据,或者如果没有数据,则显示“空”视图。

出于某些原因,我不想使用 ProgressDialog。所以我的问题是:如何提供“加载”视图?这是我的第一个粗略想法:

  1. 在加载数据期间将 ListView 替换为自定义视图,并在 Loader 完成其工作时显示 List

  1. 使用一个适配器显示数据
  2. 使用另一个适配器,其中仅包含一项“正在加载”视图
  3. 在正确的时间设置正确的 Adapter(Loader 开始工作时:myList.setAdapter(mLoadingAdapter),Loader 完成工作时:myList.setAdapter(mDataAdapter))

还有其他想法吗?为了实现这一目标,您有什么“好的做法”提示给我吗?


我找到了解决方案,谢谢 Kasra Rahjerdi 和 Harikris! 这是我的做法:

首先,为 ListView、空视图和“加载”视图创建一个布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

    <LinearLayout android:id="@+id/listcontainer"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >

        <ListView android:id="@id/android:list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:listSelector="@android:color/transparent"
            />

        <ch.shibastudio.kanjinotepad.list.KanjiListEmptyView android:id="@id/android:empty"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:text="@string/no_kanji"
            android:visibility="gone"/>
    </LinearLayout>

    <ch.shibastudio.kanjinotepad.customviews.LoadingView android:id="@+id/loading"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:visibility="gone"
    />
</LinearLayout>

为ListView 空视图创建一个容器(这里是listContainer)是非常重要的。我尝试将 ListView 的 Visibility 设置为 GONE,它会自动将空视图的 Visibility 设置为 Visible。看起来有一些内部代码为 ListView 执行此操作,因此解决方案是将它们放入容器中并完全显示/隐藏它。

所以基本上,这个想法是:当数据加载时,我想显示加载视图,为此,我通过将其 Visibility 设置为 GONE 来隐藏 ListView 容器(如果我将 Visibility 设置为 INVISIBLE,它会保留视图中列表的大小,使用 GONE 就像将其完全删除,因此“加载”视图可以占据所有屏幕)并通过将其可见性设置为可见来显示“加载”视图。

在我的 ListFragment 中,我得到了这些视图的引用,如下所示:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance){
    View v = inflater.inflate(R.layout.kanji_list, null);
    mLoadingView = (LoadingView)v.findViewById(R.id.loading);
    mListContainer = (LinearLayout)v.findViewById(R.id.listcontainer);
    return v;
}

我通过调用这个方法来显示/隐藏“加载”视图:

private void setLoadingViewVisible(boolean visible){
    if(null != mLoadingView && null != mListContainer){
        mListContainer.setVisibility(visible ? View.GONE : View.VISIBLE);
        mLoadingView.setVisibility(visible ? View.VISIBLE : View.GONE);
    }
}

【问题讨论】:

    标签: android listview


    【解决方案1】:

    查看支持库附带的ListFragment,复制of its source code is here

    这个类与常规的ListFragment 不同,在onCreateView 函数中可以看到不同之处。如果您查看它,它提供的正是您所描述的内容。

    1. 它在数据绑定到它之前提供了一个动画加载微调器。
    2. 它显示您想在适配器中显示的任何数据,就像普通的ListFragment
    3. 它有一个可选的空字段,您可以通过调用setEmptyText() 来设置它

    所以我建议您使用支持库的 ListFragment 副本而不是普通版本,或者使用我上面链接的源代码作为制作您自己的课程版本的垫脚石。

    【讨论】:

    • 谢谢 Kasra Rahjerdi,我会看看这个源代码以获得灵感。
    • 我通过查看您提供的链接中的源代码找到了解决方案(现在将发布)。谢谢 Kasra Rahjerdi!
    【解决方案2】:

    这是一个与您的应用程序工作相关的非常具体的问题。话虽如此,我认为在你的情况下最好的办法是在你的 fragment 布局文件中有 3 个视图。 ListView 在查询完成且项目计数非零时显示项目列表。查询正在进行时使用的另一个ProgressBar 视图。通过设置进度号(默认为 0 到 100),您可以控制此 ProgressBar 的可见性,或者如果您使用 ProgressBar 的 indeterminate 样式,您可以使用视图的可见性来控制它的 visibility。最后是LabelTextView,用于在要显示零个项目时使用 - 控制此视图的可见性。 ListViewTextView 在您的情况下是互斥的,这意味着当 ListView 显示时,TextView 被隐藏。高温

    【讨论】:

    • 谢谢你,Harikris,你的想法也为我指明了正确的方向。我在问题的末尾发布了我的解决方案。
    • @Shibakaneki 如果对您有帮助,请在有机会时投票。它还将帮助跟随您的其他人。谢谢。
    【解决方案3】:

    听起来您希望列表包含加载消息而不阻止用户。我想你已经想出了一些方法来在数据提取完成时异步更新列表。

    无论如何,我想到了几种方法。

    最简单的可能是在你的线性布局中拥有三个视图并隐藏其中两个,只暴露有东西要显示的那个。例如,在加载时,您将隐藏 id/android:list 并公开显示“正在加载...”的视图。加载完成后,您将隐藏“正在加载...”并取消隐藏列表或“空”视图。

    另一种方法是使用三种列表项。看看Adapter。子类适配器(或您正在使用的任何变体)并定义 getViewTypeCount 以返回 3。然后定义 getItemViewType 以返回三个值之一,具体取决于您要显示的内容。最后,增强getView(你必须已经有这个函数的版本)返回三种类型的视图。

    【讨论】:

    • 我喜欢Layout中另一个视图的想法,并在加载过程中隐藏ListView,谢谢你的想法,我会试试看!
    【解决方案4】:

    我建议您在 onCreateView 中创建包含 ListView 的空视图。
    然后在 onStart 中显示您的 ProgressBar。现在启动一个线程,否则您的 ProgressBar 可能不会显示,因为以下代码在主线程上运行。在此线程中,您加载数据、显示数据并关闭 ProgressBar。

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
    
            View view = inflater.inflate(R.layout.list_view, container, false);
    
            return view;
    }
    
    public void onStart() {
        super.onStart();
    
        // ProgressBar
        new ProgressDialog(getActivity());
        final ProgressDialog dialog = ProgressDialog.show(getActivity(),
                getString(R.string.loading_title),
                getString(R.string.loading_msg), true, false);
    
        // get ListView
        final ListView myListView = (ListView) getView().findViewById(
                    R.id.listView1);
    
        Thread thread = new Thread() {
            @Override
            public void run() {
                //load your data here (fill Adapter)!
                loadData();
    
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // fill ListView, dismiss Dialog
                        myListView.setAdapter(yourAdapterWithData);
                        dialog.dismiss();
                        }
                    });
                }
            };
    
        thread.start();
    }
    

    这只是示例代码,它不会正常工作...

    【讨论】:

    • 感谢 Manu 的回答。但是,正如我在问题中所说,我不想使用对话框。但是您提出的解决方案可能是另一个应用程序的好主意。
    • 哦,抱歉,我读到您想使用对话框...我的错。这个想法仍然是一样的:创建一个视图(在你的情况下是图片),启动一个线程,一旦它完成你就可以更改视图。 [br] 这可能会有所帮助
    • 一种解决方案是在 2 个 xml 文件中声明 2 个视图,然后切换它们。描述于this website
    猜你喜欢
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 2020-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多