android - AppBarLayout में टूलबार स्क्रॉल करने योग्य है, हालांकि RecyclerView में स्क्रॉल करने के लिए पर्याप्त सामग्री नहीं है




android-xml android-coordinatorlayout (6)

अपने Toolbar में scroll फ्लैग को हटा दें, केवल enterAlways ध्वज को छोड़ दें और आपको अपना इच्छित प्रभाव प्राप्त करना चाहिए। पूर्णता के लिए, आपका लेआउट ऐसा दिखना चाहिए:

<android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="enterAlways"
            app:theme="@style/ToolbarStyle" />
    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

क्या यह वास्तव में है कि AppBarLayout में टूलबार स्क्रॉल करने योग्य है, हालांकि "appbar_scrolling_view_behavior" वाले मुख्य कंटेनर में वास्तव में स्क्रॉल करने के लिए पर्याप्त सामग्री नहीं है?

मैंने अब तक क्या परीक्षण किया है:
जब मैं एक NestedScrollView ("wra_content" विशेषता के साथ) का उपयोग मुख्य कंटेनर और एक TextView बच्चे के रूप में करता हूं, तो AppBarLayout ठीक से काम करता है और स्क्रॉल नहीं करता है।

हालाँकि, जब मैं केवल कुछ प्रविष्टियों और "wra_content" विशेषता (ताकि स्क्रॉल करने की कोई आवश्यकता नहीं है) के साथ एक RecyclerView का उपयोग करता हूं, तो AppBarLayout में टूलबार स्क्रॉल करने योग्य होता है, भले ही RecyclerView एक स्क्रॉल इवेंट (OnScrollChangeListenerist के साथ परीक्षण) प्राप्त करता है )।

यहाँ मेरा लेआउट कोड है:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:theme="@style/ToolbarStyle" />
    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

निम्नलिखित प्रभाव के साथ कि टूलबार स्क्रॉल करने योग्य है, हालांकि यह आवश्यक नहीं है:

मैंने यह भी जाँचने का एक तरीका ढूँढ लिया है कि क्या सभी RecyclerView आइटम दिखाई दे रहे हैं और RecyclerView के सेटNestedScrollingEnabled () विधि का उपयोग कर रहे हैं।
फिर भी, यह मेरे लिए एक बग की तरह अधिक प्रतीत होता है। कोई राय? : डी

EDIT # 1:

उन लोगों के लिए जो मेरे वर्तमान समाधान में दिलचस्पी ले सकते हैं, मुझे setNestedScrollingEnabled () तर्क को PostDelayed () में हैंडलर की विधि में 5 मिनट की देरी के साथ LayoutManager की वजह से लगाना पड़ा जो हमेशा -1 का पता लगाने के तरीकों को कॉल करते समय करता था। क्या पहला और अंतिम आइटम दिखाई दे रहा है।
मैं इस कोड का उपयोग ऑनस्टार्ट () विधि में (मेरे RecyclerView को इनिशियलाइज़ करने के बाद) और हर बार RecyclerView के कंटेंट बदलने के बाद करता हूँ।

final LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //no items in the RecyclerView
        if (mRecyclerView.getAdapter().getItemCount() == 0)
            mRecyclerView.setNestedScrollingEnabled(false);
        //if the first and the last item is visible
        else if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0
                && layoutManager.findLastCompletelyVisibleItemPosition() == mRecyclerView.getAdapter().getItemCount() - 1)
            mRecyclerView.setNestedScrollingEnabled(false);
        else
            mRecyclerView.setNestedScrollingEnabled(true);
    }
}, 5);

EDIT # 2:

मैं बस एक नए ऐप के साथ खेलता हूं और ऐसा लगता है कि यह (अनपेक्षित) व्यवहार समर्थन लाइब्रेरी संस्करण 23.3.0 (या पहले भी) में तय किया गया है। इस प्रकार, अब वर्कअराउंड की कोई आवश्यकता नहीं है!


कुछ इस तरह के एक LayoutManager उपवर्ग में वांछित व्यवहार के परिणामस्वरूप लगता है:

@Override
public boolean canScrollVertically() {
    int firstCompletelyVisibleItemPosition = findFirstCompletelyVisibleItemPosition();
    if (firstCompletelyVisibleItemPosition == RecyclerView.NO_POSITION) return false;

    int lastCompletelyVisibleItemPosition = findLastCompletelyVisibleItemPosition();
    if (lastCompletelyVisibleItemPosition == RecyclerView.NO_POSITION) return false;

    if (firstCompletelyVisibleItemPosition == 0 &&
            lastCompletelyVisibleItemPosition == getItemCount() - 1)
        return false;

    return super.canScrollVertically();
}

canScrollVertically() लिए प्रलेखन कहता है:

/**
 * Query if vertical scrolling is currently supported. The default implementation
 * returns false.
 *
 * @return True if this LayoutManager can scroll the current contents vertically
 */

ध्यान दें "वर्टिकल कंटेंट को वर्टिकली स्क्रॉल कर सकते हैं" का शब्दांकन, जिसका मेरा मानना ​​है कि वर्तमान स्थिति को रिटर्न वैल्यू से परिलक्षित होना चाहिए।

हालाँकि, यह v7 recyclerview पुस्तकालय (23.1.1) के माध्यम से प्रदान किए गए LayoutManager उपवर्गों में से किसी के द्वारा नहीं किया गया है, जो मुझे कुछ हिचकिचाता है कि क्या यह एक सही समाधान है; यह इस प्रश्न में चर्चा की तुलना में अन्य स्थितियों में अवांछित प्रभाव पैदा कर सकता है।


धन्यवाद, मैंने RecyclerView का एक कस्टम वर्ग बनाया, लेकिन कुंजी अभी भी setNestedScrollingEnabled() का उपयोग कर setNestedScrollingEnabled() । इसने मेरी तरफ से ठीक काम किया।

public class RecyclerViewCustom extends RecyclerView implements ViewTreeObserver.OnGlobalLayoutListener
{
    public RecyclerViewCustom(Context context)
    {
        super(context);
    }

    public RecyclerViewCustom(Context context, @Nullable AttributeSet attrs)
    {
        super(context, attrs);
    }

    public RecyclerViewCustom(Context context, @Nullable AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    /**
     *  This supports scrolling when using RecyclerView with AppbarLayout
     *  Basically RecyclerView should not be scrollable when there's no data or the last item is visible
     *
     *  Call this method after Adapter#updateData() get called
     */
    public void addOnGlobalLayoutListener()
    {
        this.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    public void onGlobalLayout()
    {
        // If the last item is visible or there's no data, the RecyclerView should not be scrollable
        RecyclerView.LayoutManager layoutManager = getLayoutManager();
        final RecyclerView.Adapter adapter = getAdapter();
        if (adapter == null || adapter.getItemCount() <= 0 || layoutManager == null)
        {
            setNestedScrollingEnabled(false);
        }
        else
        {
            int lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
            boolean isLastItemVisible = lastVisibleItemPosition == adapter.getItemCount() - 1;
            setNestedScrollingEnabled(!isLastItemVisible);
        }

        unregisterGlobalLayoutListener();
    }

    private void unregisterGlobalLayoutListener()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
        {
            getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
        else
        {
            getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    }
}

मैं user3623735 के उत्तर में थोड़ा जोड़ना चाहूंगा । निम्न कोड बिलकुल गलत है।

// Find out if RecyclerView are scrollable, delay required
    final Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            if (rv.canScrollVertically(DOWN) || rv.canScrollVertically(UP)) {
                controller.enableScroll();
            } else {
                controller.disableScroll();
            }
        }
    }, 100);

और यहां तक ​​कि जब यह काम करता है - यह सभी मामलों को कवर नहीं करता है। इस बात की कोई गारंटी नहीं है कि एक डेटा 100 एमएस में प्रदर्शित किया जाएगा, और डेटा इसके साथ काम करने की प्रक्रिया में दृश्य की ऊंचाई को बढ़ा सकता है, न कि ऑनक्रिएट व्यू पद्धति में। इसलिए आपको अगले कोड का उपयोग करना चाहिए और दृश्य ऊंचाई में परिवर्तन ट्रैक करना चाहिए:

view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            if(bottom != oldBottom)
            {
                mActivity.setScrollEnabled(view.canScrollVertically(0) || view.canScrollVertically(1));
            }
        }
    });

इसके अलावा स्क्रॉलिंग स्थिति को नियंत्रित करने के लिए दो अलग-अलग विधि बनाने की आवश्यकता नहीं है, आपको एक सेटस्क्रोल सक्षम विधि का उपयोग करना चाहिए:

public void setScrollEnabled(boolean enabled) {
    final AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams)
            mToolbar.getLayoutParams();

    params.setScrollFlags(enabled ?
            AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS : 0);

    mToolbar.setLayoutParams(params);
}

मैंने सुझाव दिया है कि आप इस नमूने को आज़माएँ, जो सहायक पुस्तकालय तत्वों के लिए है।

इस नमूने में अपने लेआउट की तरह एक लेआउट।

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:layout_scrollFlags="scroll|enterAlways" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</android.support.design.widget.CoordinatorLayout>

यह एक बग नहीं है, एक व्यूग्रुप की सभी घटनाओं को इस तरह से हैंडल किया जाता है। क्योंकि आपका पुनरावर्तन एक समन्वयक का एक बच्चा है, इसलिए जब भी घटना उत्पन्न होती है, तो इसे पहले माता-पिता के लिए जाँच की जाती है और यदि माता-पिता को कोई दिलचस्पी नहीं है, तो इसे बच्चे को दे दिया जाता है। Google documentation देखें





android-appbarlayout