সোয়াইপ রিফ্রেশলয়আউট + ভিউপ্যাজার, কেবল অনুভূমিক স্ক্রোলকে সীমাবদ্ধ করবেন?


96

আমি প্রয়োগ করেছি SwipeRefreshLayoutএবং ViewPagerআমার অ্যাপ্লিকেশনটিতে এসেছি তবে একটি বড় সমস্যা রয়েছে: যখনই আমি পৃষ্ঠাগুলির মধ্যে বাম / ডানদিকে স্যুইচ করতে যাচ্ছি তখন খুব বেশি সংবেদনশীল sensitive কিছুটা সোয়াইপ ডাউন রিফ্রেশটিও ট্রিগার করবে SwipeRefreshLayout

যখন অনুভূমিক সোয়াইপ শুরু হবে তখন আমি একটি সীমা নির্ধারণ করতে চাই, তারপরে সোয়াইপিং শেষ না হওয়া অবধি অনুভূমিক জোর করে। অন্য কথায়, আঙ্গুলের অনুভূমিকভাবে চলতে থাকলে আমি উল্লম্ব সুইপিং বাতিল করতে চাই।

এই সমস্যাটি কেবল তখনই ঘটে থাকে ViewPager, যদি আমি সোয়াইপ ডাউন করি এবং SwipeRefreshLayoutরিফ্রেশ ফাংশনটি ট্রিগার করা হয় (বারটি প্রদর্শিত হয়) এবং তারপরে আমি আমার আঙুলটি অনুভূমিকভাবে সরান, এটি এখনও কেবল উল্লম্ব সোয়াইপগুলিকে অনুমতি দেয়।

আমি ViewPagerক্লাসটি বাড়ানোর চেষ্টা করেছি কিন্তু এটি মোটেই কার্যকর হচ্ছে না:

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context ctx, AttributeSet attrs) {
        super(ctx, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean in = super.onInterceptTouchEvent(ev);
        if (in) {
            getParent().requestDisallowInterceptTouchEvent(true);
            this.requestDisallowInterceptTouchEvent(true);
        }
        return false;
    }

}

লেআউট এক্সএমএল:

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/viewTopic"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.myapp.listloader.foundation.CustomViewPager
        android:id="@+id/topicViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>

যে কোন সহযোগীতা গ্রহনযোগ্য, ধন্যবাদ


ভিউপ্যাজারের ভিতরে যদি আপনার কোনও টুকরো থাকে তবে কি একই দৃশ্য কাজ করে SwipeRefreshLayout?
জাপানোলজিকা

উত্তর:


161

আপনার কাছে এখনও এই সমস্যাটি রয়েছে কিনা তা সম্পর্কে আমি নিশ্চিত নই তবে গুগল আই / হে অ্যাপ্লিকেশনটি এইভাবে এই সমস্যার সমাধান করে:

    viewPager.addOnPageChangeListener( new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled( int position, float v, int i1 ) {
        }

        @Override
        public void onPageSelected( int position ) {
        }

        @Override
        public void onPageScrollStateChanged( int state ) {
            enableDisableSwipeRefresh( state == ViewPager.SCROLL_STATE_IDLE );
        }
    } );


private void enableDisableSwipeRefresh(boolean enable) {
    if (swipeContainer != null) {
            swipeContainer.setEnabled(enable);
    }
}

আমি একই ব্যবহার করেছি এবং বেশ ভাল কাজ করি।

সম্পাদনা: setOnPageChangeListener () এর পরিবর্তে addOnPageChangeListener () ব্যবহার করুন।


4
এটি সেরা উত্তর কারণ এটি ভিউপ্যাজারের অবস্থা বিবেচনা করে। এটি ভিউপ্যাজারে উত্সিত নীচের দিকে টানতে বাধা দেয় না, যা স্পষ্টভাবে সতেজ করার একটি অভিপ্রায় প্রকাশ করছে।
অ্যান্ড্রু গ্যালাস্যাচ

4
সেরা উত্তর, তবে ডিজেবলসাইপাইফ্রিফ্রেস সক্ষম করার জন্য কোডটি পোস্ট করা ভাল হতে পারে (হ্যাঁ এটি ফাংশনটির নাম থেকে স্পষ্টতই .. তবে নিশ্চিতভাবেই এটি গুগল করতে হয়েছিল ...)
গ্রেগ এনিস

5
নিখুঁতভাবে কাজ করে তবে সেটঅনপেজ চ্যাঞ্জলিস্টেনারটি এখন অবচয় করা হয়েছে। পরিবর্তে addOnPageChangeListener ব্যবহার করুন।
ইয়োন করনিলভ

@ নাহসান সাম্প্রতিক আপডেট হিসাবে এই উত্তরটি আর কাজ করে না। সোয়াইপ্রেফ্রেশের সক্ষম রাষ্ট্রটিকে মিথ্যা হিসাবে সেট করা সোয়েপ্রেফ্রেশকে পুরোপুরি সরিয়ে দেয়, আগে যদি সোয়েপ্রেফ্রেশ রিফ্রেশ করার সময় ভিউপজারের স্ক্রোলের স্থিতি পরিবর্তিত হয় তবে এটি লেআউটটি সরবে না, তবে এটি আগে যেমন রিফ্রেশ অবস্থায় রেখেছিল তা অক্ষম করে।
মাইকেল টেডলা

4
গুগল আই / ও অ্যাপ্লিকেশনটিতে 'সক্ষমযোগ্য ডিসপ্লেসওয়াইপ রিফ্রেশ' এর উত্স কোডের সাথে লিঙ্ক করুন: android.googlesource.com/platform/extern/iosched/+/HEAD/…
jpardogo

37

কিছু না বাড়িয়েই খুব সহজে সমাধান করা

mPager.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mLayout.setEnabled(false);
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                mLayout.setEnabled(true);
                break;
        }
        return false;
    }
});

একটি কবজ মত কাজ


ঠিক আছে তবে মনে রাখবেন যে আপনার ভিতরে থাকা কোনও স্ক্রোলবলের জন্য আপনার একই সমস্যাটি Viewথাকবে ViewPager, SwipeRefreshLayoutকেবলমাত্র এটি শীর্ষ স্তরের সন্তানের জন্য উল্লম্ব স্ক্রোলিংকে অনুমতি দেয় (এবং আইসিএসের চেয়ে কম এপিআইগুলিতে এটি ঘটে তবেই ListView) ।
corsair992

@ corsair992less আপনার টিপসের জন্য ধন্যবাদ
ব্যবহারকারীর 3896501

@ corsair992 সমস্যাটির মুখোমুখি। আমার ViewPagerভিতরে আছে SwipeRefrestLayoutএবং ভিউপ্যাজারটি আছে Listview! SwipeRefreshLayoutআমাকে নীচে স্ক্রোল করতে দাও তবে স্ক্রোল আপ এ রিফ্রেশ অগ্রগতি ট্রিগার করে। যেকোনো পরামর্শ?
মুহাম্মদ বাবর

4
এমএলআউট কী তা সম্পর্কে আপনি আরও কিছুটা বিকাশ করতে পারেন?
desgraci

4
ভিউপেজ.সিআরটসনআউটচলাইস্টেনার {_, ইভেন্ট -> সোয়েপআরফ্রেশলয়আউট.আইএসইনড = ইভেন্ট.অ্যাকশন == মোশনএভেন্ট.এ্যাকশন_আপ ভুয়া}
অ্যাক্সরক্সো'জা যোডগোরোভ

22

আমি আপনার সমস্যা পূরণ করেছি। সোয়াইপ রিফ্রেশলয়আউটটি কাস্টমাইজ করুন সমস্যার সমাধান হবে।

public class CustomSwipeToRefresh extends SwipeRefreshLayout {

private int mTouchSlop;
private float mPrevX;

public CustomSwipeToRefresh(Context context, AttributeSet attrs) {
    super(context, attrs);

    mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mPrevX = MotionEvent.obtain(event).getX();
            break;

        case MotionEvent.ACTION_MOVE:
            final float eventX = event.getX();
            float xDiff = Math.abs(eventX - mPrevX);

            if (xDiff > mTouchSlop) {
                return false;
            }
    }

    return super.onInterceptTouchEvent(event);
}

রেফ: লিঙ্কটি দেখুন


সনাক্তকরণে opeালু অন্তর্ভুক্ত করার জন্য এটি সেরা সমাধান।
নাফসাকা

দুর্দান্ত সমাধান +1
ট্রাম এনগুইন

12

আমি এটি পূর্ববর্তী উত্তরটির ভিত্তিতে তৈরি করেছি তবে এটি আরও ভালভাবে কাজ করতে পেল। গতিটি ACTION_MOVE ইভেন্ট দিয়ে শুরু হয় এবং আমার অভিজ্ঞতায় ACTION_UP বা ACTION_CANCEL এর মধ্যে শেষ হয়।

mViewPager.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mSwipeRefreshLayout.setEnabled(false);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mSwipeRefreshLayout.setEnabled(true);
                break;
        }
        return false;
    }
});

সমাধানের জন্য থএনএক্সএক্স
হিতেশ কুশওয়াহ

9

কোনও কারণেই কেবল তাদের কাছে সর্বাধিক পরিচিত, সমর্থন লাইব্রেরি বিকাশকারী দল বাচ্চাদের শিশু বিন্যাস থেকে সমস্ত উল্লম্ব ড্র্যাগ মোশন ইভেন্টগুলিকে জোর করে আটকাতে উপযুক্ত বলে মনে করেছিল SwipeRefreshLayout, এমনকি যদি কোনও শিশু নির্দিষ্টভাবে ইভেন্টটির মালিকানা অনুরোধ করে। কেবলমাত্র তারা যাচাই করেন যে এটির প্রধান সন্তানের উল্লম্ব স্ক্রোলের অবস্থা শূন্যের উপরে রয়েছে (যদি এটির ক্ষেত্রে শিশুটি উল্লম্বভাবে স্ক্রোলযোগ্য হয়)। requestDisallowInterceptTouchEvent()পদ্ধতি একটি খালি শরীরের সঙ্গে ওভাররাইড হয়েছে, এবং (তাই) আলোকজ্জ্বল মন্তব্য "নাঃ"।

এই সমস্যাটি সমাধানের সবচেয়ে সহজ উপায় হ'ল কেবল আপনার প্রকল্পে সমর্থন লাইব্রেরি থেকে শ্রেণিটি অনুলিপি করা এবং পদ্ধতিটি ওভাররাইড সরানো। ViewGroupএর বাস্তবায়ন হ্যান্ডলিংয়ের জন্য অভ্যন্তরীণ অবস্থা ব্যবহার করে onInterceptTouchEvent(), সুতরাং আপনি কেবল পদ্ধতিটিকে আবার ওভাররাইড করতে এবং এটির সদৃশ করতে পারবেন না। আপনি যদি সমর্থন লাইব্রেরি বাস্তবায়নটিকে সত্যই অগ্রাহ্য করতে চান , তবে আপনাকে কলগুলির উপরে একটি কাস্টম পতাকা সেট আপ করতে হবে এবং তার উপর ভিত্তি করে requestDisallowInterceptTouchEvent()ওভাররাইড onInterceptTouchEvent()এবং onTouchEvent()(অথবা সম্ভবত হ্যাক canChildScrollUp()) আচরণ করতে হবে।


মানুষ, মোটামুটি। আমি সত্যই কামনা করি তারা এগুলি না করত। আমার একটি তালিকা রয়েছে যা আমি রিফ্রেশ করতে টান সক্ষম করতে চাই এবং সারি আইটেমগুলিকে সোয়াইপ করার ক্ষমতা রাখি। তারা যেভাবে সোয়াইপ রিফ্রেশলয়আউট তৈরি করেছে তা কিছুটা পাগল না করে প্রায় অসম্ভব করে তোলে।
জেসি এ। মরিস

3

নাসন সমাধানে একটি সমস্যা রয়েছে:

যদি অনুভূমিক সোয়াইপ যে ট্রিগার setEnabled(false)উপর কল SwipeRefreshLayoutমধ্যে OnPageChangeListenerঘটে যখন SwipeRefreshLayoutইতিমধ্যে স্বীকৃতি দিয়েছে খিঁচ-টু-পুনঃলোড কিন্তু এখনো প্রজ্ঞাপন কলব্যাক, অ্যানিমেশন অদৃশ্য কিন্তু অভ্যন্তরীণ স্থিতি ডেকেছেন SwipeRefreshLayoutএ "রিফ্রেশ" সব সময় প্রবেশ করুন কোন থাকার বিষয়টি মতেই বিজ্ঞপ্তি কলব্যাকগুলি বলা হয় যা রাষ্ট্রকে পুনরায় সেট করতে পারে। ব্যবহারকারীর দৃষ্টিকোণ থেকে এর অর্থ হ'ল পুল-টু-রিলোডটি আর কাজ করছে না কারণ সমস্ত টান ইশারা স্বীকৃত নয়।

এখানে সমস্যাটি হ'ল disable(false)কলটি স্পিনারটির অ্যানিমেশন সরিয়ে দেয় এবং সেই স্পিনারটির জন্য onAnimationEndএকটি অভ্যন্তরীণ অ্যানিমেশনলিস্টনার পদ্ধতি থেকে বিজ্ঞপ্তি কলব্যাক কল করা হয় যা সেভাবে অর্ডার অফ হয়ে যায়।

এই পরিস্থিতি উস্কে দেওয়ার জন্য দ্রুততম আঙ্গুল দিয়ে আমাদের পরীক্ষককে স্বীকার করে নিল তবে বাস্তবিক পরিস্থিতিতেও এটি একবারে ঘটতে পারে।

এটির সমাধানের একটি সমাধান নিম্নলিখিত onInterceptTouchEventপদ্ধতিতে ওভাররাইড করা SwipeRefreshLayout:

public class MySwipeRefreshLayout extends SwipeRefreshLayout {

    private boolean paused;

    public MySwipeRefreshLayout(Context context) {
        super(context);
        setColorScheme();
    }

    public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setColorScheme();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (paused) {
            return false;
        } else {
            return super.onInterceptTouchEvent(ev);
        }
    }

    public void setPaused(boolean paused) {
        this.paused = paused;
    }
}

MySwipeRefreshLayoutআপনার লেআউট ইন - ফাইলটি ব্যবহার করুন এবং কোডটি মুহসানের সমাধানে পরিবর্তন করুন

...

@Override
public void onPageScrollStateChanged(int state) {
    swipeRefreshLayout.setPaused(state != ViewPager.SCROLL_STATE_IDLE);
}

...

4
আপনারও আমার একই সমস্যা ছিল। এই পেস্টবিন. com/ এক্সএমএফএনএসডিকিকিউ নোটের সাথে সবেমাত্র রিফ্রেশ লেআউটটি দিয়ে নাশানের সমাধানটি পরিবর্তিত হয়েছে যখন তা রিফ্রেশ হয় তখন সমস্যা তৈরি হয় না, এজন্য চেক করুন।
অমিত জয়ন্ত

3

আমি ভিউপ্যাজার 2 এর জন্য একটি সমাধান পেয়েছি। আমি ড্র্যাগ সংবেদনশীলতা হ্রাস করার জন্য প্রতিবিম্বটি ব্যবহার করি:

/**
 * Reduces drag sensitivity of [ViewPager2] widget
 */
fun ViewPager2.reduceDragSensitivity() {
    val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
    recyclerViewField.isAccessible = true
    val recyclerView = recyclerViewField.get(this) as RecyclerView

    val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
    touchSlopField.isAccessible = true
    val touchSlop = touchSlopField.get(recyclerView) as Int
    touchSlopField.set(recyclerView, touchSlop*8)       // "8" was obtained experimentally
}

এটি আমার জন্য কবজির মতো কাজ করে।


0

যখন ভিউ পেজারটি উল্লম্ব-স্ক্রোলযোগ্য ধারকটিতে স্থাপন করা হয় তখন ঘুউ ডিউয়ের উত্তরের সাথে সমস্যা হতে পারে যা ঘুরে দেখা যায়, সুইপারআরফ্রেশলয়েটে স্থাপন করা হয় যদি সামগ্রী স্ক্রোলযোগ্য ধারক সম্পূর্ণ স্ক্রোলড আপ না হয় তবে এটি সম্ভব নাও হতে পারে একই স্ক্রোল-আপ অঙ্গভঙ্গিতে সোয়াইপ-থেকে-রিফ্রেশ সক্রিয় করুন। প্রকৃতপক্ষে, আপনি যখন অভ্যন্তরীণ পাত্রে স্ক্রোলিং শুরু করেন এবং আঙুলটি অনুভূমিকভাবে আরও বেশি সরান তখন অবিচ্ছিন্নভাবে mTouchSlop (যা ডিফল্টরূপে 8dp হয়), প্রস্তাবিত কাস্টমসুইপটোরিফ্রেস এই অঙ্গভঙ্গিটি অস্বীকার করে। তাই একজন ব্যবহারকারীকে রিফ্রেশ শুরু করার জন্য আরও একবার চেষ্টা করতে হবে। এটি ব্যবহারকারীর পক্ষে অদ্ভুত লাগতে পারে। আমি সোর্স কোড এফটি সোয়াইপরিফ্রেশলাইআউটটি সমর্থন লাইব্রেরি থেকে আমার প্রকল্পে সরিয়ে নিয়েছি এবং অনারসেপ্টটিউচ এভেন্ট () পুনরায় লিখেছি।

private float mInitialDownY;
private float mInitialDownX;
private boolean mGestureDeclined;
private boolean mPendingActionDown;

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    ensureTarget();
    final int action = ev.getActionMasked();
    int pointerIndex;

    if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
        mReturningToStart = false;
    }

    if (!isEnabled() || mReturningToStart || mRefreshing ) {
        // Fail fast if we're not in a state where a swipe is possible
        if (D) Log.e(LOG_TAG, "Fail because of not enabled OR refreshing OR returning to start. "+motionEventToShortText(ev));
        return false;
    }

    switch (action) {
        case MotionEvent.ACTION_DOWN:
            setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop());
            mActivePointerId = ev.getPointerId(0);

            if ((pointerIndex = ev.findPointerIndex(mActivePointerId)) >= 0) {

                if (mNestedScrollInProgress || canChildScrollUp()) {
                    if (D) Log.e(LOG_TAG, "Fail because of nested content is Scrolling. Set pending DOWN=true. "+motionEventToShortText(ev));
                    mPendingActionDown = true;
                } else {
                    mInitialDownX = ev.getX(pointerIndex);
                    mInitialDownY = ev.getY(pointerIndex);
                }
            }
            return false;

        case MotionEvent.ACTION_MOVE:
            if (mActivePointerId == INVALID_POINTER) {
                if (D) Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
                return false;
            } else if (mGestureDeclined) {
                if (D) Log.e(LOG_TAG, "Gesture was declined previously because of horizontal swipe");
                return false;
            } else if ((pointerIndex = ev.findPointerIndex(mActivePointerId)) < 0) {
                return false;
            } else if (mNestedScrollInProgress || canChildScrollUp()) {
                if (D) Log.e(LOG_TAG, "Fail because of nested content is Scrolling. "+motionEventToShortText(ev));
                return false;
            } else if (mPendingActionDown) {
                // This is the 1-st Move after content stops scrolling.
                // Consider this Move as Down (a start of new gesture)
                if (D) Log.e(LOG_TAG, "Consider this move as down - setup initial X/Y."+motionEventToShortText(ev));
                mPendingActionDown = false;
                mInitialDownX = ev.getX(pointerIndex);
                mInitialDownY = ev.getY(pointerIndex);
                return false;
            } else if (Math.abs(ev.getX(pointerIndex) - mInitialDownX) > mTouchSlop) {
                mGestureDeclined = true;
                if (D) Log.e(LOG_TAG, "Decline gesture because of horizontal swipe");
                return false;
            }

            final float y = ev.getY(pointerIndex);
            startDragging(y);
            if (!mIsBeingDragged) {
                if (D) Log.d(LOG_TAG, "Waiting for dY to start dragging. "+motionEventToShortText(ev));
            } else {
                if (D) Log.d(LOG_TAG, "Dragging started! "+motionEventToShortText(ev));
            }
            break;

        case MotionEvent.ACTION_POINTER_UP:
            onSecondaryPointerUp(ev);
            break;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            mIsBeingDragged = false;
            mGestureDeclined = false;
            mPendingActionDown = false;
            mActivePointerId = INVALID_POINTER;
            break;
    }

    return mIsBeingDragged;
}

গিথুবে আমার উদাহরণ প্রকল্প দেখুন ।


0

2020-10-17

নিখুঁত উত্তর @ নাসান একটি সংক্ষিপ্ত যোগ ।

আপনার কাছ থেকে হিজরত যদি ViewPagerকরতে ViewPager2, ব্যবহার

registerOnPageChangeCallback স্ক্রোল ইভেন্ট শোনার পদ্ধতি

mPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
    @Override
    public void onPageScrollStateChanged(int state) {
        super.onPageScrollStateChanged(state);
        swipe.setEnabled(state == ViewPager2.SCROLL_STATE_IDLE);
    }
}); 
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.