我已将SwipeRefreshLayout
实施到我的应用中,但它只能容纳一个应该是列表视图的直接子项。我正在试图弄清楚如何将空文本视图添加到以下工作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>
将其包装在线性/相对布局中会使其失灵,因为当您想要向后滑动列表视图时,列表视图将始终更新。我能想到的一种方法是以编程方式进行此操作,但我猜这不是最佳选择。
您可以使用本教程了解如何实现它:Swipe to refresh GUIDE
所以基本上它一切正常,但我想添加一个空视图,当listview为空时显示一条消息。
答案 0 :(得分:49)
我不喜欢对一个孩子的限制。 此外,SwipeRefreshLayout的当前实现具有ScrollView,ListView和GridView的硬编码“魔术”处理,仅当视图是您自己视图的直接子视图时才触发。
这说好消息是它是开源的,所以你可以复制代码并适应你的需求,或者你可以做我做的事情:
使用两个不同的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>
然后告诉列表视图空列表视图是空视图的滑动刷新布局。
现在,当您有数据时,列表视图将自动隐藏空刷新布局,并在列表为空时显示。
列表的滑动刷新布局不应该接收触摸事件,因为列表是隐藏的。
祝你好运。答案 1 :(得分:31)
无需任何解决方法。
您可以简单地使用此视图层次结构:
<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>
然后,在代码中,您只需调用:
_listView.setEmptyView(findViewById(android.R.id.empty));
就是这样。
编辑:如果您希望能够在显示空视图时刷卡到刷新,您将不得不以某种方式避免隐藏ListView,因此您可以使用具有此功能的自定义ListView:
@Override
public void setVisibility(final int visibility)
{
if(visibility!=View.GONE||getCount()!=0)
super.setVisibility(visibility);
}
与我编写的解决方案一起,无论您展示多少项目,都会显示刷卡到刷新。
当然,如果你真的想要隐藏ListView,你应该更改代码。也许添加“setVisibilityForReal(...)”:)
答案 2 :(得分:11)
实际上,您唯一想到的就是将空TextView
包裹着可滚动容器 - 例如ScrollView
。有关详细信息,请查看SwipeRefreshLayout.canChildScrollUp()方法及其用法。
无论如何,回到原点。这是一个成功的实施:
<强> activity_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>
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>
现在,为了摆脱着名的SwipeRefreshLayout
仅限刷新时的问题,必要时切换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();
}
就是这样!希望能帮助到你! ;)
顺便问一下,为什么你会以SwipeRefreshLayout
这种方式使用FrameLayout
?因为这样您可以执行平滑过渡动画,例如 crossfade 效果,并且任何州视图都可以可滑动(如果您需要统一获取/刷新/重试机制)。
答案 3 :(得分:8)
这是我做的: 我禁用滑动刷新,除非我的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);
}
});
我的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>
像魅力一样。
答案 4 :(得分:6)
我遇到了同样的问题。令人讨厌的是, SwipeRefreshLayout 只能有一个 AdpaterView 子项。
如果为 AdpaterView 设置了空视图,则在没有可用数据的情况下,它将被设置为隐藏。但是, SwipeRefreshLayout 需要 AdpaterView 才能运行。因此,我扩展了 AdpaterView 以使其仍然显示,即使它是空的。
@Override
public void setVisibility(int visibility) {
if (visibility == View.GONE && getCount() == 0) {
return;
}
super.setVisibility(visibility);
}
也许您需要将适配器视图的背景设置为透明。但在我的情况下,它不是必需的,因为空视图是一个简单的 TextView 。
答案 5 :(得分:4)
基于这里的一些答案和SwipeRefreshLayout的源代码,我已经将视图子类化为专门处理具有RecyclerView(或ListView)以及&#34;空&#34;查看作为孩子的容器内部。
它需要一个布局,如
<SwipeRefreshLayoutWithEmpty ...>
<FrameLayout ...>
<TextView android:text="List is Empty" ...>
<RecyclerView ...>
</FrameLayout>
</SwipeRefreshLayoutWithEmpty>
代码是:
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;
}
}
全部要点:https://gist.github.com/grennis/16cb2b0c7f798418284dd2d754499b43
答案 6 :(得分:2)
我试过以下的事情。在空视图和列表的情况下,滑动将刷新数据,fastscroll也正常工作,而不需要在活动中进行任何代码更改。 我已将emptyview放在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>
答案 7 :(得分:1)
将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>
答案 8 :(得分:1)
为什么不将所有内容都包装在SwipeRefreshLayout
中<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"/>
<RelativeLayout
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent" >
...
</RelativeLayout>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
答案 9 :(得分:1)
可能迟到了,但如果您仍然面临这个问题,那么这是一个干净的解决方案!无需创建另一个SwipeRefreshLayout
。
将empty view
打包成ScrollView
。将AdapterView
和ScrollView
同时添加到ViewGroup
并将其放入SwipeRefreshLayout
<强>示例:强>
<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>
修改强>
这是更简单的方法。
检查您的清单是否有问题。如果是,那么让它看不见。样品:
if(lst.size() > 0) {
mRecyclerView.setVisibility(View.VISIBLE);
//set adapter
}else
{
mRecyclerView.setVisibility(View.INVISIBLE);
findViewById(R.id.txtNodata).setVisibility(View.VISIBLE);
}
这将使mRecyclerView
仍然存在,即使没有数据,所有滑动也会正常工作!
答案 10 :(得分:0)
在您的空视图不需要任何交互的情况下,另一个选项可以很好地工作。例如,这是一个简单的文本视图,说“这里没有数据。”
<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"
>
<TextView
android:id="@+id/emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="visible"
android:layout_centerInParent="true"
android:text="@string/no_earnable_available"
android:textSize="18dp"
android:textColor="@color/black"
android:background="@color/white"
/>
<some.app.RecyclerViewSwipeToRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="4dp"
android:background="@android:color/transparent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
/>
</some.app.RecyclerViewSwipeToRefreshLayout>
</RelativeLayout>
这将空视图放在SwipeToRefreshLayout后面,该视图是透明的,并且还包含透明的RecyclerView。
然后,在代码中,在将项目添加到回收站视图适配器的位置,检查适配器是否为空,如果是,则将空视图的可见性设置为“可见”。反之亦然。
诀窍是视图位于透明回收器视图的后面,这意味着回收器视图和他的父视图SwipeToRefreshLayout将在回收器中没有项目时处理滚动。后面的空视图甚至不会被触摸,因此SwipeTorefreshLayout将消耗触摸事件。
自定义RecyclerSwipeTorefreshLayout应按以下方式处理canChildScrollUp方法
@Override
public boolean canChildScrollUp() {
if (recycler == null) throw new IllegalArgumentException("recycler not set!");
else if (recycler.getAdapter().getItemCount() == 0){ // this check could be done in a more optimised way by setting a flag from the same place where you change the visibility of the empty view
return super.canChildScrollUp();
} else {
RecyclerView.LayoutManager layoutManager = recycler.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager) recycler.getLayoutManager()).findFirstVisibleItemPosition() != 0 ||
(((LinearLayoutManager) recycler.getLayoutManager()).findFirstVisibleItemPosition() == 0
&& recycler.getChildAt(0) != null && recycler.getChildAt(0).getY() < 0);
//...
这样就可以了。
<强>更新强> 当然,回收者视图不一定要透明。只有在适配器为空时,才能将透明度更新为活动状态。
干杯!
答案 11 :(得分:0)
这对我来说是一个非常令人沮丧的问题,但经过几个小时的尝试和失败,我想出了这个解决方案。
有了这个,即使空视图可见,我也可以刷新(当然还有RecyclerView)
在我的布局文件中,我有这样的结构:
SwipeRefreshLayout
FrameLayout
RecyclerView
my_empty_layout // Doesnt have to be ScrollView it can be whatever ViewGroup you want, I used LinearLayout with a single TextView child
在代码中:
...
adapter.notifyDataSetChanged()
if (adapter.getItemCount() == 0) {
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
}
else {
recyclerView.setVisibility(View.VISIBLE);
emptyView.setVisibility(View.GONE);
}
答案 12 :(得分:0)
这对我有用
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/lighter_white">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@color/silver"
android:layout_marginTop="10dp"
android:divider="@android:color/transparent"
android:dividerHeight="8dp"
android:padding="4dp"/>
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="@+id/empty"
android:text="@string/strNoRecordsFound"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@android:color/black"
android:alpha="0.5"
android:gravity="center">
</TextView>
</FrameLayout>
答案 13 :(得分:0)
您可能只在单个容器的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>
答案 14 :(得分:-1)
正如我提到的here,我已经为RecyclerView
:
我使用了clickable="true"
,如下所示,空RecyclerView
:
mRecyclerView.setVisibility(View.VISIBLE);
mRecyclerView.setClickable(true);
myText.setVisibility(View.VISIBLE);
xml layout:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshKartvizitKisilerim"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="@+id/myText"
android:visibility="gone"
android:text="@string/noListItem"
android:textSize="18sp"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
RecyclerView
的身高为match_parent
,SwipeRefresh
的身高为wrap_content
。当列表中有项目时,请不要忘记创建文本gone
。