Android - 带有空文本视图的 SwipeRefreshLayout
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22964934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android - SwipeRefreshLayout with empty textview
提问by Niels
I've implemented SwipeRefreshLayout
into my app but it can only hold one direct child which should be the listview. I'm trying to figure out how to add an empty textview to the following working XML file:
我已经SwipeRefreshLayout
在我的应用程序中实现了,但它只能容纳一个应该是列表视图的直接子级。我试图弄清楚如何将一个空的 textview 添加到以下工作 XML 文件中:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/listViewConversation"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:dividerHeight="1dp" />
</android.support.v4.widget.SwipeRefreshLayout>
Wrapping it in a Linear/Relative layout makes it buggy because the listview will always update when you want to slide back up the listview. One way I can think of is doing this programmatically but I guess that's not the best option.
将它包装在线性/相对布局中会使其变得有问题,因为当您想要向上滑动列表视图时,列表视图将始终更新。我能想到的一种方法是以编程方式执行此操作,但我想这不是最佳选择。
You can learn how to implement it using this tutorial: Swipe to refresh GUIDE
您可以使用本教程学习如何实现它:滑动以刷新 GUIDE
So basically it all works fine but I would like to add an empty view that shows a message when the listview is empty.
所以基本上一切正常,但我想添加一个空视图,当列表视图为空时显示一条消息。
回答by Daniele Segato
I didn't liked the limitation to a single child. Furthermore the current implementation of the SwipeRefreshLayout has an hardcoded "magic" handling for ScrollView, ListView and GridView that trigger only if the view it's the direct child of your own view.
我不喜欢对一个孩子的限制。此外,SwipeRefreshLayout 的当前实现具有针对 ScrollView、ListView 和 GridView 的硬编码“魔术”处理,仅当视图是您自己视图的直接子视图时才会触发。
That said the good news it's that it is open source, so you can either copy the code and adapt to your needs or you can do what I did:
好消息是它是开源的,所以你可以复制代码并适应你的需求,或者你可以做我所做的:
Use two DIFFERENT SwipeRefreshLayout, one for the Empty view and one for the ListView.
使用两种不同的 SwipeRefreshLayout,一种用于 Empty 视图,一种用于 ListView。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tobrun.example.swipetorefresh.MainActivity">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout_listView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout_emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TextView
android:id="@+id/emptyView"
android:text="@string/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center" />
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
Then tell your listview that the empty list view is the swipe refresh layout of the empty view.
然后告诉你的listview,空的list view就是空view的swipe refresh layout。
Now the empty refresh layout will be automatically hidden by your list view when you have data and will be shown when the list is empty.
现在,当您有数据时,空的刷新布局将被您的列表视图自动隐藏,并在列表为空时显示。
The swipe refresh layout of the list shouldn't receive touch events cause the list is hidden.
列表的滑动刷新布局不应接收触摸事件,因为列表是隐藏的。
Good luck.
祝你好运。
回答by android developer
There is no need for any workaround.
不需要任何解决方法。
You can simply use this view hierarchy :
您可以简单地使用此视图层次结构:
<FrameLayout ...>
<android.support.v4.widget.SwipeRefreshLayout ...>
<ListView
android:id="@android:id/list" ... />
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="@android:id/empty" ...
android:text="@string/empty_list"/>
</FrameLayout>
Then, in code, you just call:
然后,在代码中,您只需调用:
_listView.setEmptyView(findViewById(android.R.id.empty));
That's it.
就是这样。
EDIT: If you wish to be able to swipe-to-refresh even when the empty view is shown, you will have to somehow avoid hiding the ListView, so you could use a customized ListView that has this function inside:
编辑:如果您希望即使在显示空视图时也能够滑动刷新,则必须以某种方式避免隐藏 ListView,因此您可以使用内部具有此功能的自定义 ListView:
@Override
public void setVisibility(final int visibility)
{
if(visibility!=View.GONE||getCount()!=0)
super.setVisibility(visibility);
}
Together with the solution I wrote, the swipe-to-refresh is shown no matter how many items you are showing.
连同我写的解决方案,无论您显示多少项目,都会显示滑动刷新。
Of course, if you really want to hide the ListView, you should change the code. Maybe add "setVisibilityForReal(...)" :)
当然,如果你真的想隐藏ListView,你应该改变代码。也许添加“setVisibilityForReal(...)” :)
回答by vizZ
Actually, the only think you are missing is having that empty TextView
be wrapped with a scrollable container- for example ScrollView
. For details, have a look at SwipeRefreshLayout.canChildScrollUp()method and its usage.
实际上,您唯一缺少的想法是TextView
用可滚动容器包装空容器- 例如ScrollView
. 有关详细信息,请查看SwipeRefreshLayout.canChildScrollUp()方法及其用法。
Anyway, back to the point. Here is a successful implementation:
不管怎样,回到正题。这是一个成功的实现:
activity_some.xml
活动_some.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:measureAllChildren="true">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/empty" />
</FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout>
Where your empty.xml
is basically anything you wish wrapped with a ScrollView
.
您empty.xml
基本上是您希望用ScrollView
.
empty.xml
空文件
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TextView
android:text="Nothing here..."
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ScrollView>
Now in order to get rid of the famous SwipeRefreshLayout
refresh-only-when-at-the-top issue, toggle the SwipeRefreshLayout
when necessary (Fragment-specific):
现在为了摆脱著名的SwipeRefreshLayout
refresh-only-when-at-the-top 问题,SwipeRefreshLayout
在必要时切换(特定于片段):
private ViewTreeObserver.OnScrollChangedListener mOnScrollChangedListener;
@Override
public void onStart() {
super.onStart();
mOnScrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
int scrollY = mWebView.getScrollY();
if (scrollY == 0)
swipeLayout.setEnabled(true);
else
swipeLayout.setEnabled(false);
}
};
swipeLayout.getViewTreeObserver().addOnScrollChangedListener(mOnScrollChangedListener);
}
@Override
public void onStop() {
swipeLayout.getViewTreeObserver().removeOnScrollChangedListener(mOnScrollChangedListener);
super.onStop();
}
That's it! Hope it helps! ;)
就是这样!希望能帮助到你!;)
Btw, why would you use SwipeRefreshLayout
with FrameLayout
this way? Because this way you can do smooth transition animations, like crossfadeeffects, and any of your stateviews can be swipeable(in case you want a unified fetch/refresh/retrymechanism).
顺便说一句,你为什么会使用SwipeRefreshLayout
与FrameLayout
这种方式?因为这样你可以做平滑的过渡动画,比如淡入淡出效果,并且你的任何状态视图都可以滑动(如果你想要一个统一的获取/刷新/重试机制)。
回答by Stanislasdrg Reinstate Monica
Here's what I did : I disabled the swipe to refresh, unless my listView is up.
这就是我所做的:除非我的 listView 已启动,否则我禁用了滑动以刷新。
mBookedProductsListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem == 0)
mSwipeRefreshLayout.setEnabled(true);
else
mSwipeRefreshLayout.setEnabled(false);
}
});
My Xml :
我的 XML :
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:id="@+id/productListLL"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<EditText
android:id="@+id/search"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:background="#a4aeb8"
android:drawableRight="@drawable/search_icon"
android:paddingRight="15dp"
android:textColor="@android:color/white"
android:paddingLeft="15dp"
android:hint="Rechercher"
android:textColorHint="@android:color/white"
android:inputType="textCapWords|textNoSuggestions"
/>
<include android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/filter_by_categories_buttons" android:id="@+id/categoriesTree" android:layout_below="@id/search" />
<ListView
android:id="@+id/productListLV"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/product_listview_divider"
android:dividerHeight="1dp"
android:scrollbars="none"
android:overScrollMode="never"
android:choiceMode="multipleChoiceModal"
android:background="#e7eaef"
android:visibility="gone"
android:layout_below="@id/categoriesTree"
/>
</RelativeLayout>
</android.support.v4.widget.SwipeRefreshLayout>
Works like a charm.
奇迹般有效。
回答by hufeng03
I encountered the same problem. It is annoying that SwipeRefreshLayoutcan only have one AdpaterViewchild.
我遇到了同样的问题。令人讨厌的是SwipeRefreshLayout只能有一个AdpaterView子项。
If an empty view is set for an AdpaterView, it will be set as hidden if no data is available. However, SwipeRefreshLayoutneeds an AdpaterViewto work. So, I extend AdpaterViewto make it still shown, even if it is empty.
如果为AdpaterView设置了一个空视图,如果没有可用数据,它将被设置为隐藏。但是,SwipeRefreshLayout需要AdpaterView才能工作。因此,我扩展了AdpaterView以使其仍然显示,即使它是空的。
@Override
public void setVisibility(int visibility) {
if (visibility == View.GONE && getCount() == 0) {
return;
}
super.setVisibility(visibility);
}
Maybe you need to set the background of adapter view as transparent as well. But in my case, it is not needed, as the empty view is a simple TextView.
也许您还需要将适配器视图的背景设置为透明。但在我的情况下,它是不需要的,因为空视图是一个简单的TextView。
回答by Greg Ennis
Based on some answers here and the source code of SwipeRefreshLayout, I have subclassed the view to specifically handle having a RecyclerView (or ListView) and also an "empty" view inside a container which is the child.
根据此处的一些答案和 SwipeRefreshLayout 的源代码,我将视图子类化为专门处理 RecyclerView(或 ListView)以及容器内的“空”视图,即子容器。
It expects a layout such as
它需要一个布局,例如
<SwipeRefreshLayoutWithEmpty ...>
<FrameLayout ...>
<TextView android:text="List is Empty" ...>
<RecyclerView ...>
</FrameLayout>
</SwipeRefreshLayoutWithEmpty>
The code is:
代码是:
public class SwipeRefreshLayoutWithEmpty extends SwipeRefreshLayout {
private ViewGroup container;
public SwipeRefreshLayoutWithEmpty(Context context) {
super(context);
}
public SwipeRefreshLayoutWithEmpty(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean canChildScrollUp() {
// The swipe refresh layout has 2 children; the circle refresh indicator
// and the view container. The container is needed here
ViewGroup container = getContainer();
if (container == null) {
return false;
}
// The container has 2 children; the empty view and the scrollable view.
// Use whichever one is visible and test that it can scroll
View view = container.getChildAt(0);
if (view.getVisibility() != View.VISIBLE) {
view = container.getChildAt(1);
}
return ViewCompat.canScrollVertically(view, -1);
}
private ViewGroup getContainer() {
// Cache this view
if (container != null) {
return container;
}
// The container may not be the first view. Need to iterate to find it
for (int i=0; i<getChildCount(); i++) {
if (getChildAt(i) instanceof ViewGroup) {
container = (ViewGroup) getChildAt(i);
if (container.getChildCount() != 2) {
throw new RuntimeException("Container must have an empty view and content view");
}
break;
}
}
if (container == null) {
throw new RuntimeException("Container view not found");
}
return container;
}
}
Full gist: https://gist.github.com/grennis/16cb2b0c7f798418284dd2d754499b43
完整要点:https: //gist.github.com/grennis/16cb2b0c7f798418284dd2d754499b43
回答by Gaurav Karwa
I have tried following thing. In both empty view, and case of a list, swipe will refresh the data, also fastscroll is working fine without any code change required in activity. I have put emptyview before the listview and marked its visiblity to gone. and listview is put after that inside SwipeToRefresh block. Sample Code -
我试过以下事情。在空视图和列表的情况下,滑动将刷新数据,而且 fastscroll 工作正常,无需在活动中更改任何代码。我已将空视图放在列表视图之前,并将其可见性标记为消失。并且 listview 放在 SwipeToRefresh 块之后。示例代码 -
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/tv_empty_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="32dp"
android:gravity="center_horizontal"
android:text="No item found"
android:visibility="gone"/>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv_product"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
回答by Fajar Khan
Probably late, But if you're still facing this issue, Here is the clean solution! There is no need of creating another SwipeRefreshLayout
.
可能晚了,但是如果您仍然面临这个问题,这是干净的解决方案!无需创建另一个SwipeRefreshLayout
.
wrap empty view
into a ScrollView
. Stick both AdapterView
and the ScrollView
into a ViewGroup
and put it into the SwipeRefreshLayout
包裹empty view
成一个ScrollView
. 将AdapterView
和都粘贴ScrollView
到 a 中ViewGroup
并将其放入SwipeRefreshLayout
Sample:
样本:
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center" />
</ScrollView>
</FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout>
EDIT
编辑
Here is much more simpler way.
这是更简单的方法。
check if your list is empthy. if yes then make it invisible. Sample:
检查您的列表是否为空。如果是,则使其不可见。样本:
if(lst.size() > 0) {
mRecyclerView.setVisibility(View.VISIBLE);
//set adapter
}else
{
mRecyclerView.setVisibility(View.INVISIBLE);
findViewById(R.id.txtNodata).setVisibility(View.VISIBLE);
}
this will make mRecyclerView
still there and all swipes will be working fine now even there is no data!
这将使 mRecyclerView
仍然存在,即使没有数据,所有滑动现在也可以正常工作!
回答by Jacob
You may be just use NestedScrollView in SwipeRefreshLayout with single container.
Below is the list of buggy use mRecyclerView.setNestedScrollingEnabled(false);
您可能只是在带有单个容器的 SwipeRefreshLayout 中使用 NestedScrollView。以下是越野车使用清单mRecyclerView.setNestedScrollingEnabled(false);
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- some toolbar -->
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/conversationFragment_swipeRefresh"
android:layout_alignParentBottom="true"
android:layout_below="@+id/some_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/conversationFragment_noResultText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/FragmentConversations_empty"
android:layout_centerHorizontal="true" />
<android.support.v7.widget.RecyclerView
android:id="@+id/conversationFragment_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
回答by Lee Chun Hoe
Put SwipeRefreshLayout into a FrameLayout and other views behind it.
将 SwipeRefreshLayout 放入 FrameLayout 和其后的其他视图中。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="@+id/your_message_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="No result"/>
</LinearLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/your_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>