NestedScrollView के अंदर रिसाइकलर दृश्य बीच में शुरू करने के लिए स्क्रॉल का कारण बनता है


129

जब मैं एक NestedScrollView के अंदर एक RecyclerView जोड़ते हैं तो मुझे एक अजीब स्क्रॉलिंग व्यवहार मिल रहा है।

क्या होता है कि जब भी स्क्रॉलव्यू की तुलना में स्क्रीन में अधिक पंक्तियाँ दिखाई जा सकती हैं, जैसे ही गतिविधि शुरू की जाती है, NestedScrollView शीर्ष (छवि 1) से ऑफसेट के साथ शुरू होता है। यदि स्क्रॉल दृश्य में कुछ आइटम हैं, तो वे सभी एक ही बार में दिखाए जा सकते हैं, ऐसा नहीं होता है (छवि 2)।

मैं समर्थन पुस्तकालय के संस्करण 23.2.0 का उपयोग कर रहा हूं।

छवि 1 : गलत - ऊपर से ऑफसेट के साथ शुरू होता है

चित्र 1

चित्र 2 : सही - पुनर्नवीनीकरण दृश्य में कुछ आइटम

चित्र 2

मैं अपने लेआउट कोड के नीचे चिपका रहा हूं:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="fill_vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp">

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="16dp"
                android:orientation="vertical">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Title:"
                    style="@style/TextAppearance.AppCompat.Caption"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:padding="@dimen/bodyPadding"
                    style="@style/TextAppearance.AppCompat.Body1"
                    android:text="Neque porro quisquam est qui dolorem ipsum"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Subtitle:"
                    style="@style/TextAppearance.AppCompat.Caption"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    style="@style/TextAppearance.AppCompat.Body1"
                    android:padding="@dimen/bodyPadding"
                    android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."/>

            </LinearLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:focusable="false"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

क्या मैं कुछ भूल रहा हूँ? किसी को भी यह कैसे तय करने के लिए कोई विचार है?

अपडेट १

यह सही ढंग से काम करता है अगर मैं अपनी गतिविधि को शुरू करते समय निम्नलिखित कोड रखता हूं:

sv.post(new Runnable() {
        @Override
        public void run() {
            sv.scrollTo(0,0);
        }
});

जहां sv NestedScrollView का संदर्भ है, हालांकि यह काफी हैक जैसा दिखता है।

अपडेट २

अनुरोध के अनुसार, यहां मेरा एडेप्टर कोड है:

public abstract class ArrayAdapter<T, VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH> {

    private List<T> mObjects;

    public ArrayAdapter(final List<T> objects) {
        mObjects = objects;
    }

    /**
     * Adds the specified object at the end of the array.
     *
     * @param object The object to add at the end of the array.
     */
    public void add(final T object) {
        mObjects.add(object);
        notifyItemInserted(getItemCount() - 1);
    }

    /**
     * Remove all elements from the list.
     */
    public void clear() {
        final int size = getItemCount();
        mObjects.clear();
        notifyItemRangeRemoved(0, size);
    }

    @Override
    public int getItemCount() {
        return mObjects.size();
    }

    public T getItem(final int position) {
        return mObjects.get(position);
    }

    public long getItemId(final int position) {
        return position;
    }

    /**
     * Returns the position of the specified item in the array.
     *
     * @param item The item to retrieve the position of.
     * @return The position of the specified item.
     */
    public int getPosition(final T item) {
        return mObjects.indexOf(item);
    }

    /**
     * Inserts the specified object at the specified index in the array.
     *
     * @param object The object to insert into the array.
     * @param index  The index at which the object must be inserted.
     */
    public void insert(final T object, int index) {
        mObjects.add(index, object);
        notifyItemInserted(index);

    }

    /**
     * Removes the specified object from the array.
     *
     * @param object The object to remove.
     */
    public void remove(T object) {
        final int position = getPosition(object);
        mObjects.remove(object);
        notifyItemRemoved(position);
    }

    /**
     * Sorts the content of this adapter using the specified comparator.
     *
     * @param comparator The comparator used to sort the objects contained in this adapter.
     */
    public void sort(Comparator<? super T> comparator) {
        Collections.sort(mObjects, comparator);
        notifyItemRangeChanged(0, getItemCount());
    }
}

और यहाँ मेरा ViewHolder है:

public class ViewHolder extends RecyclerView.ViewHolder {
    private TextView txt;
    public ViewHolder(View itemView) {
        super(itemView);
        txt = (TextView) itemView;
    }

    public void render(String text) {
        txt.setText(text);
    }
}

और यहाँ RecyclerView में प्रत्येक आइटम का लेआउट है (यह सिर्फ है android.R.layout.simple_spinner_item- यह स्क्रीन केवल इस बग का उदाहरण दिखाने के लिए है):

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@android:id/text1"
    style="?android:attr/spinnerItemStyle"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textAlignment="inherit"/>

साथ की कोशिश की android:focusableInTouchMode="false"के लिए RecyclerView? smth स्पष्ट रूप से "अपने लेआउट को नीचे जाने के लिए मजबूर कर रहा है ... (जहां यह 1295 आइटम होने पर शुरू होता है? नीचे या सिर्फ पहली स्क्रीन की तरह छोटे शीर्ष ऑफसेट)
स्नैचम

Android सेट करें: clipToPadding = "true" अपने NestedScrollView में।
नातारियो

यह भी: स्क्रॉल करने योग्य दृश्य को एक ही तरह से स्क्रॉल करने योग्य दृश्य के अंदर रखना बहुत अच्छा पैटर्न नहीं है ... आशा है कि आप उचित उपयोग कर रहे हैंLayoutManager
snachmsm

आपके दोनों सुझावों की कोशिश की और दुर्भाग्य से न तो काम किया। @ पुनर्नियुम दृश्य में आइटमों की संख्या से कोई फर्क नहीं पड़ता, ऑफसेट हमेशा समान होता है। कि क्या एक NestedScrollView अंदर एक RecyclerView रखकर एक अच्छा पैटर्न है के रूप में, यह वास्तव में एक गूगल इंजीनियर द्वारा सिफारिश की गई है plus.google.com/u/0/+AndroidDevelopers/posts/9kZ3SsXdT2T
Luccas कोरिया

1
यहां तक ​​कि मेरे पास NiedScrollView के अंदर RecyclerView का उपयोग करते समय एक ही मुद्दा है। यह एक बुरा पैटर्न है क्योंकि रिसाइकलर पैटर्न ही काम नहीं करेगा। सभी दृश्य एक समय में तैयार किए जाएंगे (क्योंकि WRAP_CONTENT को पुनर्नवीनीकरण दृश्य की ऊंचाई की आवश्यकता है)। पृष्ठभूमि में कोई दृश्य पुनर्चक्रण नहीं होगा, इसलिए रिसाइकलर दृश्य का मुख्य उद्देश्य स्वयं काम नहीं कर रहा है। लेकिन रिसाइकलर दृश्य का उपयोग करके डेटा को प्रबंधित करना और लेआउट आकर्षित करना आसान है, यही एकमात्र कारण है कि आप इस पैटर्न का उपयोग कर सकते हैं। जब तक आप इसे सुनिश्चित करने के लिए आवश्यक न हो, तब तक इसका उपयोग न करें।
अशोक वर्मा

जवाबों:


253

मैंने सेटिंग करके इस तरह के मुद्दे को हल किया:

<ImageView ...
android:focusableInTouchMode="true"/>

RecyclerView के ऊपर मेरे विचार से (जो अवांछित स्क्रॉल के बाद छिपा हुआ था)। इस गुण को RecyclerView या LinearLayout के ऊपर अपने LinearLayout में सेट करने का प्रयास करें जो RecyclerView का कंटेनर है (मुझे किसी अन्य मामले में मदद मिली)।

जैसा कि मैंने NestedScrollView स्रोत में देखा है कि यह onRequestFocusInDescendants में पहले संभव बच्चे को फ़ोकस करने की कोशिश करता है और यदि केवल RecyclerView ध्यान देने योग्य है तो यह जीत जाता है।

संपादित करें (वारन के लिए धन्यवाद): और चिकनी स्क्रॉल के लिए सेट करना न भूलें yourRecyclerView.setNestedScrollingEnabled(false);


12
वैसे, आपको अपनाRecyclerView.setNestedScrollingEnabled (झूठा) जोड़ना होगा; स्क्रॉल को सुचारू बनाने के लिए
वारन-

1
@Kenji केवल पहले LinearLayout के अंदर देखने के लिए जहां RecyclerView रखा गया है। या कि LinearLayout (RecyclerView कंटेनर) ही अगर पहला परिवर्तन मदद नहीं करता है।
दिमित्री गवरिल्को 18

1
@DmitryGavrilko मेरे पास एक मुद्दा है, जहां RecycleView एक NestedScrollview के अंदर है। मैं recycleview.scrollToPosition (X) का उपयोग नहीं कर सकता; , यह सिर्फ काम नहीं करता है। मैंने पिछले 6 दिनों में सब कुछ करने की कोशिश की, लेकिन मैं इससे पार पा सकता हूं। कोई उपाय? मैं बहुत आभारी रहूंगा!
कारोली

3
आप सिर्फ android:focusableInTouchMode="false"recyclerView पर भी सेट कर सकते हैं ताकि आपको अन्य सभी दृश्यों के लिए सही सेट करने की आवश्यकता न हो।
अक्सिओम

1
दिमित्री गवरिल्को से सहमत, एंड्रॉइड को सेट करने के बाद काम किया: फ़ोकस करने योग्यइन्चौमोड = "सच" में रैखिक रेखा के साथ जिसमें पुनर्नवीनीकरण होता है। @Aksiom सेटिंग android: focusableInTouchMode = "गलत" से recyclerView ने मेरे लिए काम नहीं किया है।
शेंद्रे किरण

103

अपने LinearLayoutतत्काल बाद में, निम्नलिखित तरीके से NestedScrollViewउपयोग करेंandroid:descendantFocusability

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        android:descendantFocusability="blocksDescendants">

संपादित करें

चूंकि उनमें से कई को यह उत्तर उपयोगी लगता है, इसलिए यह स्पष्टीकरण भी प्रदान करेगा।

का उपयोग यहाँdescendantFocusability दिया गया है । और यहाँfocusableInTouchMode पर के रूप में । तो में उपयोग कर रहा हूँblocksDescendantsdescendantFocusability नहीं है यह लाभ ध्यान केंद्रित करने की अनुमति देता है, जबकि बच्चे को छू और इसलिए अनियोजित व्यवहार बंद कर दिया जा सकता है।

के लिए के रूप में focusInTouchMode, दोनों AbsListViewऔर RecyclerViewप्रणाली को बुलाती है setFocusableInTouchMode(true);तो यह आपके XML लेआउट में है कि विशेषता का उपयोग करने की आवश्यकता नहीं है, डिफ़ॉल्ट रूप से उनके निर्माता में।

और NestedScrollViewनिम्नलिखित विधि के लिए प्रयोग किया जाता है:

private void initScrollView() {
        mScroller = ScrollerCompat.create(getContext(), null);
        setFocusable(true);
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
        setWillNotDraw(false);
        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
    }

यहां, setFocusable()इसके बजाय विधि का उपयोग किया जाता है setFocusableInTouchMode()। लेकिन इस पोस्ट के अनुसार , focusableInTouchModeकुछ शर्तों के लिए इसे टाला नहीं जाना चाहिए क्योंकि यह एंड्रॉइड सामान्य व्यवहार के साथ स्थिरता को तोड़ता है। एक गेम एक एप्लिकेशन का एक अच्छा उदाहरण है जो टच मोड प्रॉपर्टी में फ़ोकस करने योग्य का अच्छा उपयोग कर सकता है। MapView, यदि Google मैप्स की तरह फ़ुलस्क्रीन में उपयोग किया जाता है, तो इसका एक और अच्छा उदाहरण है जहाँ आप टच मोड में फ़ोकस करने योग्य का सही उपयोग कर सकते हैं।


धन्यवाद! यह मेरे मामले में काम करता है। दुर्भाग्य से Android: focusableInTouchMode = "सच" मेरे लिए काम नहीं किया।
मिखाइल

1
इसने मेरे लिए काम किया। मैंने अपने recyclerview (मेरे मामले में, यह एक रेखीयआउट था) के मूल दृश्य में कोड की इस पंक्ति को जोड़ा और इसने एक आकर्षण की तरह काम किया।
amzer

मैं NearScrollView के बाद LinearLayout का उपयोग कर रहा था जिसमें RecyclerView और EditText शामिल हैं। स्क्रॉलिंग व्यवहार एंड्रॉइड का उपयोग करके तय किया गया था: वंशावलीफोकसबिलिटी = "ब्लॉकसेंडेंडेंट्स" लिनियरलैटआउट के अंदर लेकिन एडिट टेक्स्ट ध्यान केंद्रित नहीं कर सकता है। किसी भी विचार क्या हो रहा है?
सागर चापागैन

@ सागरचैपगैन यह blocksDescendantsसभी वंशजों का ध्यान केंद्रित करने की संपत्ति है । मुझे यकीन नहीं है, लेकिन कोशिश करूँगाbeforeDescendants
जिमित पटेल

2
@JimitPatel मेरी समस्या का उपयोग करके हल किया गया थाrecyclerView.setFocusable(false); nestedScrollView.requestFocus();
सागर चापागैन

16
android:descendantFocusability="blocksDescendants"

अंदर LinearLayout मेरे लिए काम किया।


6
लेकिन क्या इसका मतलब यह है कि अंदर के विचारों पर ध्यान केंद्रित किया जाएगा, और उपयोगकर्ता उन तक नहीं पहुंच पाएंगे?
Android डेवलपर

11

मेरे पास एक ही मुद्दा था और NestedScrollView का विस्तार करके और ध्यान केंद्रित करने वाले बच्चों को अक्षम करके गाया गया था। किसी कारण से, RecyclerView ने हमेशा ध्यान केंद्रित करने का अनुरोध किया, जब मैंने सिर्फ दराज खोला और बंद किया।

public class DummyNestedScrollView extends NestedScrollView {
public DummyNestedScrollView(Context context) {
    super(context);
}

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

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

/**
 * Fixind problem with recyclerView in nested scrollview requesting focus
 * http://stackoverflow.com/questions/36314836/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle
 * @param child
 * @param focused
 */
@Override
public void requestChildFocus(View child, View focused) {
    Log.d(getClass().getSimpleName(), "Request focus");
    //super.requestChildFocus(child, focused);

}


/**
 * http://stackoverflow.com/questions/36314836/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle
 * @param direction
 * @param previouslyFocusedRect
 * @return
 */
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
    Log.d(getClass().getSimpleName(), "Request focus descendants");
    //return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
    return false;
}
}

यह काम करता है लेकिन यह फोकस अवधारणा को भी तोड़ता है और आप स्क्रीन पर दो केंद्रित क्षेत्रों के साथ आसानी से स्थिति प्राप्त कर सकते हैं। तो इसका उपयोग केवल तभी करें जब आपके पास RecyclerViewधारकों के अंदर कोई EditText न हो ।
एंड्री चेर्नोप्रुडोव

4

मेरे मामले में यह कोड मेरा मुद्दा हल करता है

RecyclerView recyclerView = findViewById(R.id.recyclerView);
NestedScrollView nestedScrollView= findViewById(R.id.nestedScrollView);

recyclerView.setFocusable(false);
nestedScrollView.requestFocus();

//populate recyclerview here

मेरे लेआउट में NestedScrollView के रूप में एक पेरेंट लेआउट है जिसमें एक बच्चा LinearLayout है। LinearLayout में ओरिएंटेशन "वर्टिकल" है और चिल्ड रिसाइक्लर व्यू और एडिट टेक्स्ट। संदर्भ


2

मेरे दो अनुमान हैं।

पहला: इस लाइन को अपने NestedScrollView पर डालने का प्रयास करें

app:layout_behavior="@string/appbar_scrolling_view_behavior"

दूसरा: उपयोग करें

<android.support.design.widget.CoordinatorLayout

इस तरह आपके माता-पिता के विचार

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="fill_vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp">

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:orientation="vertical"
                      android:padding="16dp">

            <TextView
                style="@style/TextAppearance.AppCompat.Caption"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Title:"/>

            <TextView
                style="@style/TextAppearance.AppCompat.Body1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="@dimen/bodyPadding"
                android:text="Neque porro quisquam est qui dolorem ipsum"/>

            <TextView
                style="@style/TextAppearance.AppCompat.Caption"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Subtitle:"/>

            <TextView
                style="@style/TextAppearance.AppCompat.Body1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="@dimen/bodyPadding"
                android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."/>

        </LinearLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:focusable="false"/>

    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

मेरा अंतिम संभव समाधान। में कसम खाता हूँ :)


और कोऑर्डिनेटर
लयआउट

2

यह समस्या रीसायकल व्यू फ़ोकस के कारण आती है।

स्वचालित रूप से सभी फोकस दृश्य को रीसायकल करने के लिए चले गए यदि इसका आकार स्क्रीन के आकार को बढ़ाता है।

जोड़ने android:focusableInTouchMode="true"की तरह पहले ChildView को TextView, Buttonइतने पर और (पर नहीं ViewGroupकी तरह Linear, Relativeसमाधान ऊपर और इसी तरह) बना भावना समस्या लेकिन एपीआई स्तर 25 को हल करने के लिए और काम नहीं करता है।

अपने चाइल्डव्यू में इन 2 लाइन को जोड़ें जैसे TextView, Buttonऔर इसी तरह (न की ViewGroupतरह Linear, Relativeऔर इसी तरह )

 android:focusableInTouchMode="true"
 android:focusable="true"

मुझे सिर्फ एपीआई स्तर 25 पर इस समस्या का सामना करना पड़ा। मुझे उम्मीद है कि अन्य लोग इसमें समय बर्बाद नहीं करेंगे।

रीसायकल व्यू पर स्मूथ स्क्रॉलिंग के लिए इस लाइन को जोड़ें

 android:nestedScrollingEnabled="false"

लेकिन इस अटायरिब्यूट्स को जोड़ने पर केवल एपीआई स्तर 21 या इसके बाद के संस्करण के साथ काम करता है। यदि आप चाहते हैं कि एपीआई स्तर 25 से नीचे की तरफ स्मूथिंग स्क्रॉलिंग का काम हो तो इस लाइन को अपनी कक्षा में जोड़ें

 mList = findViewById(R.id.recycle_list);
 ViewCompat.setNestedScrollingEnabled(mList, false);

0

जावा कोड में, अपने रिसाइकलर व्यू को इनिशियलाइज़ करने और एडेप्टर सेट करने के बाद, इस लाइन को जोड़ें:

recyclerView.setNestedScrollingEnabled(false)

आप किसी रिश्तेदारलेआउट के साथ लेआउट को लपेटने का भी प्रयास कर सकते हैं ताकि दृश्य उसी स्थिति में रहें लेकिन recyclerView (जो स्क्रॉल करें) पहले xml पदानुक्रम में है। आखिरी सुझाव एक हताश प्रयास: पी


0

जैसा कि मुझे जवाब देने में देर हो रही है, लेकिन किसी और की मदद कर सकते हैं। बस अपने ऐप स्तर build.gradle में नीचे या उच्चतर संस्करण का उपयोग करें और समस्या को हटा दिया गया है।

compile com.android.support:recyclerview-v7:23.2.1

0

शीर्ष पर स्क्रॉल करने के लिए, बस इसमें कॉल करें setcontentview:

scrollView.SmoothScrollTo(0, 0);

यह प्रश्न का उत्तर प्रदान नहीं करता है
उमर अता

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.