SwipeRefreshLayout + WebView, когда позиция прокрутки вверху
Я пытаюсь использовать SwipeRefreshLayout с WebView.
Я сталкиваюсь с проблемой, когда в середине страницы, когда пользователь прокручивает страницу вниз, появляется нежелательное обновление.
Как сделать так, чтобы событие обновления происходило только тогда, когда позиция прокрутки веб-просмотра находится сверху. (то есть он смотрит на верхнюю часть страницы)?
17 ответов
Мне удалось решить это без необходимости что-либо расширять. Посмотрите на этот фрагмент (специфичный для фрагмента):
private ViewTreeObserver.OnScrollChangedListener mOnScrollChangedListener;
@Override
public void onStart() {
super.onStart();
swipeLayout.getViewTreeObserver().addOnScrollChangedListener(mOnScrollChangedListener =
new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
if (mWebView.getScrollY() == 0)
swipeLayout.setEnabled(true);
else
swipeLayout.setEnabled(false);
}
});
}
@Override
public void onStop() {
swipeLayout.getViewTreeObserver().removeOnScrollChangedListener(mOnScrollChangedListener);
super.onStop();
}
Для более широкого контекста взгляните на мой ответ для Android - SwipeRefreshLayout с пустым textview.
Я думаю, что рекомендуемый способ сделать это, чтобы расширить SwipeRefreshLayout
и переопределить canChildScrollUp()
- https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html
Вот как это делается в приложении Google IO 2014 Schedule - https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/MultiSwipeRefreshLayout.java#L76
CustomSwipeRefreshLayout
вероятно будет выглядеть так:
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
private CanChildScrollUpCallback mCanChildScrollUpCallback;
public interface CanChildScrollUpCallback {
boolean canSwipeRefreshChildScrollUp();
}
public void setCanChildScrollUpCallback(CanChildScrollUpCallback canChildScrollUpCallback) {
mCanChildScrollUpCallback = canChildScrollUpCallback;
}
@Override
public boolean canChildScrollUp() {
if (mCanChildScrollUpCallback != null) {
return mCanChildScrollUpCallback.canSwipeRefreshChildScrollUp();
}
return super.canChildScrollUp();
}
}
И в твоем Activity
это имеет WebView
,
public class FooActivity
implements CustomSwipeRefreshLayout.CanChildScrollUpCallback {
private CustomSwipeRefreshLayout mRefreshLayout;
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// after initialization
mRefreshLayout.setCanChildScrollUpCallback(this);
}
@Override
public boolean canSwipeRefreshChildScrollUp() {
return mWebview.getScrollY() > 0;
}
}
Просто реализуй свой Fragment
или же Activity
с ViewTreeObserver.OnScrollChangedListener
затем установите Listener как webview.getViewTreeObserver().addOnScrollChangedListener(this);
В onScrollChanged()
метод сделать так
@Override
public void onScrollChanged() {
if (webview.getScrollY() == 0) {
swipeLayout.setEnabled(true);
} else {
swipeLayout.setEnabled(false);
}
}
Самые последние версии библиотеки поддержки имеют SwipeRefreshLayout#setOnChildScrollUpCallback
метод, который позволяет избежать подклассов SwipeRefreshLayout
,
mBinding.swipeRefreshLayout.setOnChildScrollUpCallback((parent, child) ->
mBinding.webView.getScrollY() > 10);
Вы можете попробовать это:
Он работает для API>= 23 Android Marshmallow
webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
swipeRefreshLayout.setEnabled(scrollY == 0);
}
});
Изменить 1: это не работает, если веб-страница имеет вложенный прокручиваемый контент. Поэтому вместо SwipeRefreshLayout я решил использовать простой ImageButton для перезагрузки веб-страницы.
Вы можете решить эту проблему, расширив SwipeRefreshLayout с помощью пользовательского SwipeRefreshLayout, который принимает WebView и использует WebView для проверки позиции scrollY.
Примечание. Это будет работать для прокрутки всей страницы, но может не работать с вложенными прокрутками. Также, если у вас горизонтальная прокрутка, вы также можете добавить логику, найденную в этом ответе: /questions/9253698/horizontalscrollview-vnutri-swiperefreshlayout/9253717#9253717
public class CustomSwipeToRefresh extends SwipeRefreshLayout {
private final int yScrollBuffer = 5;
private WebView mWebView;
public CustomSwipeToRefresh(Context context, WebView webView) {
super(context);
mWebView = webView;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mWebView.getScrollY() > yScrollBuffer)
return false;
return super.onInterceptTouchEvent(event);
}
}
Я отвечаю на тот же вопрос, но использую Kotlin. Включая swipeRefreshLayout
когда свиток webView
получает 0
:
Сделайте эту переменную в вашем фрагменте или деятельности:
private val onScrollChangedListener = ViewTreeObserver.OnScrollChangedListener{ swipeRefreshLayout.isEnabled = webView.scrollY == 0 }
затем onViewCreated
:
swipeRefreshLayout.viewTreeObserver.addOnScrollChangedListener(onScrollChangedListener)
затем onStop
:
swipeRefreshLayout.viewTreeObserver.removeOnScrollChangedListener(onScrollChangedListener)
Отсюда все будет хорошо работать. Но если используемая вами веб-страница имеет фиксированный заголовок, который не прокручивается scrollY
всегда будет равен 0
, И это решение вместе с другим решением на этой странице может не работать.
Я столкнулся с аналогичной проблемой, но ни один из ответов, приведенных здесь, не работает для меня. Итак, я получил решение из этого ответа.
Шаг 1: Создать класс CustomWebView
public class CustomWebView extends WebView{
private OnScrollChangedCallback mOnScrollChangedCallback;
public CustomWebView(final Context context)
{
super(context);
}
public CustomWebView(final Context context, final AttributeSet attrs)
{
super(context, attrs);
}
public CustomWebView(final Context context, final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
}
@Override
protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt)
{
super.onScrollChanged(l, t, oldl, oldt);
if(mOnScrollChangedCallback != null) mOnScrollChangedCallback.onScroll(l, t);
}
public OnScrollChangedCallback getOnScrollChangedCallback()
{
return mOnScrollChangedCallback;
}
public void setOnScrollChangedCallback(final OnScrollChangedCallback onScrollChangedCallback)
{
mOnScrollChangedCallback = onScrollChangedCallback;
}
/**
* Impliment in the activity/fragment/view that you want to listen to the webview
*/
public static interface OnScrollChangedCallback
{
public void onScroll(int horizontal, int vertical);
}}
Шаг 2: Использование XML- файла webview
как это-
<com.yourpackagename.CustomWebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Шаг 3: В вашем MainActivity
использование setOnScrollChangedCallback
-
mWebView.setOnScrollChangedCallback(new CustomWebView.OnScrollChangedCallback(){
public void onScroll(int horizontal, int vertical){
System.out.println("=="+horizontal+"---"+vertical);
//this is to check webview scroll behaviour
if (vertical<50){
swipeRefreshLayout.setEnabled(true);
}else{
swipeRefreshLayout.setEnabled(false);
}
}
});
}
// swipe - active only when WebView Y==0
swipeView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
if (webView.getScrollY() == 0) {
Log.d(TAG, "onScrollChanged - y=0 - make swipe refresh active");
swipeView.setEnabled(true);
}else {
swipeView.setEnabled(false);
Log.d(TAG, "onScrollChanged - y is not null - swipe refresh not active");
}
}
});
// swipe - after refresh do work (see above for condition)
swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Log.d(TAG, "Swipe - refresh");
// do work ... your work method here
getSchedule();
// hide spinner of swiperefreshlayout
swipeView.setRefreshing(false);
}
});
Самый надежный подход к разрешению конфликта между
SwipeRefreshLayout
и
WebView
- привязать жест касания / смахивания к определенному месту в верхней части экрана.
Предыдущие ответы основаны на обнаружении
webview.scrollY
может быть ненадежным, потому что некоторые страницы имеют фиксированное положение заголовка, поэтому даже при наличии прокручиваемого списка
webview.scrollY
всегда будет возвращать 0
- Создать индивидуальный
SwipeRefreshLayout
class MySwipeRefreshLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {
private var mDeclined = false
private var mPrevY: Float = 0f
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
mPrevY = event.y
mDeclined = false
if (mPrevY > resources.getDimension(R.dimen.disable_swipe_after)){
mDeclined = true
}
}
MotionEvent.ACTION_MOVE -> {
if (mDeclined) {
return false
}
}
}
return super.onInterceptTouchEvent(event)
}
}
- Применить его в файле просмотра
<yourpackage.MyLimitedSwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:windowSoftInputMode="adjustResize"
>
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
</yourpackage.MySwipeRefreshLayout>
Этот фрагмент кода Kotlin настраивает SwipeRefreshLayout, чтобы включить функцию обновления по запросу для WebView в приложении Android. Он проверяет положение прокрутки WebView и включает или отключает действие обновления в зависимости от того, прокручивается ли WebView вверх. Когда пользователь инициирует обновление, он перезагружает содержимое WebView и останавливает анимацию обновления по завершении.
binding.swipeRefreshLayout.viewTreeObserver.addOnScrollChangedListener {
binding.swipeRefreshLayout.isEnabled = binding.webView.scrollY === 0
}
binding.swipeRefreshLayout.setOnRefreshListener {
binding.swipeRefreshLayout.isRefreshing = false
binding.webView.reload()
}
Меня устраивает :
swipeRefreshLayout.getViewTreeObserver().addOnScrollChangedListener(() -> {
swipeRefreshLayout.setEnabled(webView.getScrollY() == 0);
});
У меня ничего из вышеперечисленного не работало. Вот мой подход
@Override
public boolean dispatchTouchEvent(MotionEvent event)
{
super.dispatchTouchEvent(event);
int x = (int)event.getX();
int y = (int)event.getY();
if(y>800){
swipeRefreshLayout.setEnabled(false);
}else {
swipeRefreshLayout.setEnabled(true);
}
Log.e("yy",""+y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
}
return false;
}
Ответ, предоставленный hiBrianLee, почти сработал для меня, но, как писал Джон Шелли, getScrollY
всегда возвращал 0 в моем случае (я использовал ListView
как вложенный прокручиваемый ребенок). Что сразу сработало, так это использование canScrollVertically
вместо этого (который является View
метод, поэтому он доступен как для ListView
с и WebView
с, а также для любого другого вида прокрутки ребенка).
Мой класс макета выглядит следующим образом:
public class NestedScrollSwipeLayout extends SwipeRefreshLayout {
private ListView scrollChild;
public NestedScrollSwipeLayout(Context context) {
super(context);
}
public NestedScrollSwipeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollChild(ListView child) {
this.scrollChild = child;
}
@Override
public boolean canChildScrollUp() {
return (scrollChild != null && scrollChild.canScrollVertically(-1)) ||
super.canChildScrollUp();
}
}
(Как уже упоминалось, canScrollVertically
это View
метод, поэтому ListView
может быть безопасно заменено общим View
или любой другой специализированный класс представления.)
Я обнаружил, что отключение вложенной прокрутки в веб-представлении (не самого SwipeRefreshLayout) решило эту проблему для меня. Это не будет возможно сделать во всех ситуациях, но было в моей.
webView.setNestedScrollingEnabled(false);
Я нашел решение для этого. Просто используйте CoordinatorLayout в качестве корневого макета и добавьте layout_behavior="@string/appbar_scrolling_view_behavior" в свой SwipeRefreshLayout. Это идеально для меня.
Используйте SwipeRefreshLayout как обычно, как и в других представлениях, но включите вложенную прокрутку.
swipeRefreshLayout.setNestedScrollingEnabled(true);