Как реализовать Android Pull-to-Refresh
В приложениях Android, таких как Twitter (официальное приложение), когда вы сталкиваетесь с ListView, вы можете опустить его (и он отскочит назад после освобождения), чтобы обновить содержимое.
Интересно, как, по вашему мнению, лучше всего это реализовать?
Некоторые возможности, о которых я мог подумать:
- Элемент поверх ListView - однако я не думаю, что прокрутка назад к позиции элемента 1 (на основе 0) с анимацией в ListView - простая задача.
- Другой вид за пределами ListView - но мне нужно позаботиться о перемещении позиции ListView вниз, когда он тянется, и я не уверен, сможем ли мы определить, действительно ли при касании перетаскиванием в ListView действительно прокручиваются элементы в ListView.
Любые рекомендации?
PS Интересно, когда будет опубликован официальный исходный код приложения Twitter. Было упомянуто, что это будет выпущено, но прошло 6 месяцев, и мы не слышали об этом с тех пор.
18 ответов
Наконец, Google выпустил официальную версию библиотеки для обновления!
Это называется SwipeRefreshLayout
, внутри библиотеки поддержки, и документация здесь:
1) Добавить SwipeRefreshLayout
как родительское представление, которое будет обрабатываться как обновление для обновления макета. (Я взял ListView
Например, это может быть любой View
лайк LinearLayout
, ScrollView
так далее.)
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/pullToRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
2) Добавить слушателя в свой класс
protected void onCreate(Bundle savedInstanceState) {
SwipeRefreshLayout pullToRefresh = findViewById(R.id.pullToRefresh);
pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshData(); // your code
pullToRefresh.setRefreshing(false);
}
});
}
Вы также можете позвонить pullToRefresh.setRefreshing(true/false);
согласно вашему требованию
Я предпринял попытку реализовать компонент "Обновление до обновления", он далек от завершения, но демонстрирует возможную реализацию, https://github.com/johannilsson/android-pulltorefresh.
Основная логика реализована в PullToRefreshListView
это расширяет ListView
, Внутренне это управляет прокруткой представления заголовка, используя smoothScrollBy
(Уровень API 8). Теперь виджет обновлен с поддержкой 1.5 и более поздних версий, пожалуйста, прочтите README для поддержки 1.5.
В ваших макетах вы просто добавляете это так.
<com.markupartist.android.widget.PullToRefreshListView
android:id="@+id/android:list"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
/>
Я также реализовал надежную библиотеку PullToRefresh для Android с открытым исходным кодом, простую в использовании и настраиваемую. Вы можете заменить свой ListView на PullToRefreshListView, как описано в документации на странице проекта.
Я думаю, что самый простой способ обеспечивается библиотекой поддержки 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>
Я предполагаю, что вы используете представление рециркулятора вместо просмотра списка. Тем не менее, просмотр списка по-прежнему работает, поэтому вам просто нужно заменить recyclerview на просмотр списка и обновить ссылки в коде 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);
}
}
Надеюсь, это поможет массам
По этой ссылке вы можете найти развилку знаменитого 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;
}
}
Вам просто нужно реализовать ListViewTouchEventListener в вашей деятельности, где вы хотите использовать этот ListView и установить слушателя
У меня это реализовано в 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");
}
}
Меня устраивает:)
Никто не упомянул новый тип "Подтянуть для обновления", который отображается в верхней части панели действий, как в приложении Google Now или Gmail.
Есть библиотека https://github.com/chrisbanes/ActionBar-PullToRefresh, которая работает точно так же.
Обратите внимание, что при реализации на Android и WP возникают проблемы с UX.
"Отличный показатель того, почему дизайнеры / разработчики не должны реализовывать функцию" pull-to-refresh "в стиле приложений iOS, - это то, как Google и их команды никогда не используют функцию" pull-to-refresh "на Android, в то время как они используют ее в iOS".
https://plus.google.com/109453683460749241197/posts/eqYxXR8L4eb
Я написал здесь, чтобы обновить компонент: https://github.com/guillep/PullToRefresh Это событие работает, если в списке нет элементов, и я протестировал его на>=1.6 телефонах Android.
Любое предложение или улучшение приветствуется:)
Если вы не хотите, чтобы ваша программа выглядела как программа для iPhone, которая принудительно встроена в Android, стремитесь к более естественному внешнему виду и сделайте что-то похожее на Gingerbread:
Я думаю, что лучшая библиотека: https://github.com/chrisbanes/Android-PullToRefresh.
Работает с:
ListView
ExpandableListView
GridView
WebView
ScrollView
HorizontalScrollView
ViewPager
Сначала мы должны знать, что такое Pull, чтобы обновить макет в android. мы можем вызвать pull для обновления в android как swipe-to-refresh. когда вы проводите пальцем по экрану сверху вниз, он выполняет какое-то действие на основе setOnRefreshListener.
Вот руководство, которое демонстрирует, как реализовать запрос на обновление Android. Надеюсь, это поможет.
Очень интересный Pull-to-Refresh от Yalantis. Gif для iOS, но вы можете проверить это:)
<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" />
Чтобы получить последнюю версию Lollipop Pull-To Refresh:
- Загрузите последнюю версию библиотеки поддержки Lollipop SDK и Extras/Android
- Установите для Project Build Target значение 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()
в комментариях;)
Если вы хотите использовать pull для обновления в веб-просмотре или где-то еще, просто скопируйте и вставьте этот код в свой проект, он отлично работает.
Исходный код XML:
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swiperefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/WebView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true">
</WebView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
JAVA-код:
final SwipeRefreshLayout pullToRefresh = findViewById(R.id.swiperefresh);
pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh()
{
Toast.makeText(WebBrowser.this,"pull to refresh",Toast.LENGTH_LONG).show();
mwebView.loadUrl(mainUrl);
pullToRefresh.setRefreshing(false);//if false then refresh progress dialog will disappear when page loading finish, but if it is true then always the refresh load dialog show even after the page load or not
}
});
Исходный код Build.gradle:
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
Те, кто хочет реализовать функцию "тянуть для обновления" для RecyclerView, могут следовать моему простому руководству " Как реализовать" тянуть для обновления "для RecyclerView в 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
});
Вы все сделали