在 Twitter(官方应用)等 Android 应用程序中,当遇到 ListView 时,可以将其拉下(释放时会弹回)以刷新内容。
我想知道在您看来,实现它的最佳方式是什么?
我能想到的一些可能性:
ListView 顶部的一个项目 - 但是我认为使用 ListView 上的动画滚动回项目位置 1(基于 0)并不是一件容易的事。 ListView 之外的另一个视图 - 但我需要注意在拉动 ListView 时将其位置向下移动,而且我不确定我们是否可以检测到对 ListView 的拖动触摸是否仍然真的滚动 ListView 上的项目。
有什么建议吗?
PS我想知道官方Twitter应用程序源代码何时发布。已经提到它会发布,但是6个月过去了,从那以后我们就没有听说过它。
终于,谷歌发布了正式版的pull-to-refresh库!
它在支持库中称为 SwipeRefreshLayout
,文档为 here:
添加 SwipeRefreshLayout 作为视图的父级,这将被视为刷新布局的拉动。 (我以 ListView 为例,它可以是任何 View,如 LinearLayout、ScrollView 等)
您也可以根据需要调用 pullToRefresh.setRefreshing(true/false);
。
更新
Android 支持库已被弃用,并已被 AndroidX 取代。可以找到指向新库的链接 here。
此外,您需要将以下依赖项添加到您的项目中:
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
或者
你可以去 Refactor>>Migrate to AndroidX 和 Android Studio 会为你处理依赖。
我已尝试实现一个拉动刷新组件,它远未完成,但演示了一个可能的实现,https://github.com/johannilsson/android-pulltorefresh。
主要逻辑在扩展 ListView
的 PullToRefreshListView
中实现。 在内部,它使用 小部件现已更新,支持 1.5 及更高版本,但请阅读 README 以获得 1.5 支持。
smoothScrollBy
(API 级别 8)控制标题视图的滚动。
在您的布局中,您只需像这样添加它。
<com.markupartist.android.widget.PullToRefreshListView
android:id="@+id/android:list"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
/>
我还为 Android 实现了一个强大的、开源的、易于使用且高度可定制的 PullToRefresh 库。您可以按照项目页面上的文档中的说明将 ListView 替换为 PullToRefreshListView。
https://github.com/erikwt/PullToRefresh-ListView
我认为最简单的方法是由 android 支持库提供:
android.support.v4.widget.SwipeRefreshLayout;
导入后,您可以将布局定义如下:
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_height="match_parent"
android:layout_width="match_parent">
<android.support.v7.widget.RecyclerView
xmlns:recycler_view="http://schemas.android.com/apk/res-auto"
android:id="@android:id/list"
android:theme="@style/Theme.AppCompat.Light"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/button_material_light"
>
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
我假设您使用回收站视图而不是列表视图。但是,listview 仍然有效,因此您只需将 recyclerview 替换为 listview 并更新 java 代码(片段)中的引用。
在您的活动片段中,您首先实现接口 SwipeRefreshLayout.OnRefreshListener
:即
public class MySwipeFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener{
private SwipeRefreshLayout swipeRefreshLayout;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item, container, false);
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh);
swipeRefreshLayout.setOnRefreshListener(this);
}
@Override
public void onRefresh(){
swipeRefreshLayout.setRefreshing(true);
refreshList();
}
refreshList(){
//do processing to get new data and set your listview's adapter, maybe reinitialise the loaders you may be using or so
//when your data has finished loading, cset the refresh state of the view to false
swipeRefreshLayout.setRefreshing(false);
}
}
希望对大众有所帮助
setRefreshing
的一件事:如 developer.android.com/reference/android/support/v4/widget/… 中所述,“当通过滑动手势触发刷新时不要调用它”
在此链接中,您可以找到著名的 PullToRefresh
视图的分支,该视图具有新的有趣实现,例如 PullTorRefreshWebView
或 PullToRefreshGridView
,或者可以在列表的底部边缘添加 PullToRefresh
。
https://github.com/chrisbanes/Android-PullToRefresh
最好的是在 Android 4.1 中完美运行(正常的 PullToRefresh
不起作用)
要实现 android Pull-to-Refresh 试试这段代码,
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/pullToRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
活动类:
ListView lv = (ListView) findViewById(R.id.lv);
SwipeRefreshLayout pullToRefresh = (SwipeRefreshLayout) findViewById(R.id.pullToRefresh);
lv.setAdapter(mAdapter);
pullToRefresh.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh() {
// TODO Auto-generated method stub
refreshContent();
}
});
private void refreshContent(){
new Handler().postDelayed(new Runnable() {
@Override public void run() {
pullToRefresh.setRefreshing(false);
}
}, 5000);
}
我有非常简单的方法来做到这一点,但现在确定它是万无一失的方法有我的代码 PullDownListView.java
package com.myproject.widgets;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
/**
* @author Pushpan
* @date Nov 27, 2012
**/
public class PullDownListView extends ListView implements OnScrollListener {
private ListViewTouchEventListener mTouchListener;
private boolean pulledDown;
public PullDownListView(Context context) {
super(context);
init();
}
public PullDownListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PullDownListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setOnScrollListener(this);
}
private float lastY;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
lastY = ev.getRawY();
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
float newY = ev.getRawY();
setPulledDown((newY - lastY) > 0);
postDelayed(new Runnable() {
@Override
public void run() {
if (isPulledDown()) {
if (mTouchListener != null) {
mTouchListener.onListViewPulledDown();
setPulledDown(false);
}
}
}
}, 400);
lastY = newY;
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
lastY = 0;
}
return super.dispatchTouchEvent(ev);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
setPulledDown(false);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public interface ListViewTouchEventListener {
public void onListViewPulledDown();
}
public void setListViewTouchListener(
ListViewTouchEventListener touchListener) {
this.mTouchListener = touchListener;
}
public ListViewTouchEventListener getListViewTouchListener() {
return mTouchListener;
}
public boolean isPulledDown() {
return pulledDown;
}
public void setPulledDown(boolean pulledDown) {
this.pulledDown = pulledDown;
}
}
您只需要在要使用此 ListView 的活动上实现 ListViewTouchEventListener 并设置侦听器
我在 PullDownListViewActivity 中实现了它
package com.myproject.activities;
import android.app.Activity;
import android.os.Bundle;
/**
* @author Pushpan
*
*/
public class PullDownListViewActivity extends Activity implements ListViewTouchEventListener {
private PullDownListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listView = new PullDownListView(this);
setContentView(listView);
listView.setListViewTouchListener(this);
//setItems in listview
}
public void onListViewPulledDown(){
Log.("PullDownListViewActivity", "ListView pulled down");
}
}
这个对我有用 :)
请注意,在 Android 和 WP 上实施时需要解决 UX 问题。
“为什么设计师/开发人员不应该以 iOS 应用程序的方式实现下拉刷新的一个很好的指标是,谷歌和他们的团队从不在 Android 上使用下拉刷新,而在 iOS 中使用它。”
https://plus.google.com/109453683460749241197/posts/eqYxXR8L4eb
如果您不希望您的程序看起来像强制安装到 Android 中的 iPhone 程序,请瞄准更原生的外观和感觉并执行类似于 Gingerbread 的操作:
https://i.stack.imgur.com/SPejA.png
我在这里写了一个拉动刷新组件:https://github.com/guillep/PullToRefresh 如果列表没有项目,它会起作用,并且我已经在 >=1.6 android 手机上对其进行了测试。
任何建议或改进表示赞赏:)
我认为最好的库是:https://github.com/chrisbanes/Android-PullToRefresh。
适用于:
ListView
ExpandableListView
GridView
WebView
ScrollView
HorizontalScrollView
ViewPager
我们首先要知道 android 中的 Pull to refresh layout 是什么。我们可以在 android 中调用 pull 来刷新为 swipe-to-refresh。当您从上到下滑动屏幕时,它会根据 setOnRefreshListener 执行一些操作。
这里的 tutorial 演示了如何实现 android pull to refresh。我希望这有帮助。
要获得最新的 Lollipop Pull-To Refresh:
下载最新的 Lollipop SDK 和 Extras/Android 支持库 将项目的构建目标设置为 Android 5.0(否则支持包可能会出现资源错误) 将您的 libs/android-support-v4.jar 更新到 21 版本 使用 android.support.v4.widget .SwipeRefreshLayout 加上 android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener
可在此处找到详细指南:http://antonioleiva.com/swiperefreshlayout/
对于 ListView,我建议在评论中阅读有关 canChildScrollUp()
的信息;)
Yalantis 的Pull-to-Refresh 非常有趣。适用于 iOS 的 Gif,但您可以检查它:)
<com.yalantis.pulltorefresh.library.PullToRefreshView
android:id="@+id/pull_to_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:divider="@null"
android:dividerHeight="0dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
那些希望为 RecyclerView 实现拉取刷新功能的人可以按照我的简单教程How to implement Pull To Refresh for RecyclerView in Android进行操作。
要导入的库
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
XML 代码
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
活动JAVA代码
import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.os.Bundle;
import android.os.Handler;
public class MainActivity extends AppCompatActivity {
private SwipeRefreshLayout swipeRefreshLayout;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
swipeRefreshLayout = findViewById(R.id.swipe_layout);
initializeRefreshListener();
}
void initializeRefreshListener() {
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
// This method gets called when user pull for refresh,
// You can make your API call here,
// We are using adding a delay for the moment
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if(swipeRefreshLayout.isRefreshing()) {
swipeRefreshLayout.setRefreshing(false);
}
}
}, 3000);
}
});
}
在你的布局中添加这个
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
现在只需在您的代码中添加以下这些行
mSwipeRefreshLayout.setOnRefreshListener(() -> {
// your refresh code here
});
你都完成了