NestedScrollView içindeki Recycler görünümü, kaydırmanın ortada başlamasına neden oluyor


130

Bir NestedScrollView içine RecyclerView eklediğimde garip bir kaydırma davranışı alıyorum.

Olan şey şudur: Kaydırma görünümünde ekranda gösterilenden daha fazla satır olduğunda, etkinlik başlatılır başlatılmaz, NestedScrollView üstten bir kayma ile başlar (resim 1). Kaydırma görünümünde hepsinin aynı anda gösterilebilmesi için birkaç öğe varsa, bu olmaz (resim 2).

Destek kitaplığının 23.2.0 sürümünü kullanıyorum.

Resim 1 : YANLIŞ - üstten kayma ile başlar

Resim 1

Resim 2 : DOĞRU - geri dönüşümcü görünümünde birkaç öğe

Resim 2

Düzen kodumun altına yapıştırıyorum:

<?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>

Bir şey mi kaçırıyorum? Bunu nasıl düzelteceğine dair bir fikri olan var mı?

Güncelleme 1

Etkinliğimi başlatırken aşağıdaki kodu yerleştirirsem doğru çalışır:

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

Sv, NestedScrollView için bir referanstır, ancak oldukça hack gibi görünür.

Güncelleme 2

İstendiği gibi, işte adaptör kodum:

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());
    }
}

Ve işte ViewHolder'ım:

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'daki her bir öğenin düzeni burada verilmiştir (bu sadece android.R.layout.simple_spinner_item- bu ekran yalnızca bu hatanın bir örneğini göstermek içindir):

<?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"/>

ile çalıştı android:focusableInTouchMode="false"için RecyclerView? smth açıkça "düzeninizi aşağıya gitmeye zorlamaktır ... (1295 öğeye sahip olduğunuzda nerede başlar? alt veya ilk ekran gibi sadece küçük üst ofset)
snachmsm

Android: clipToPadding = "true" ayarını NestedScrollView'a ayarlayın.
natario

ayrıca: kaydırılabilir görünümü aynı şekilde kaydırılabilir başka bir görünümde tutmak pek iyi bir model değildir ... umarım doğru kullanıyorsunuzdurLayoutManager
snachmsm

Her iki önerinizi de denedim ve ne yazık ki işe yaramadı. @snachmsm geri dönüşümcü görünümündeki öğe sayısı ne olursa olsun, ofset her zaman aynıdır. Bir NestedScrollView içine RecyclerView yerleştirmenin iyi bir model olup olmadığına gelince, bu aslında bir Google mühendisi tarafından plus.google.com/u/0/+AndroidDevelopers/posts/9kZ3SsXdT2T
Luccas Correa

1
NestedScrollView içinde RecyclerView kullanırken bile aynı sorunu yaşıyorum. Bu kötü bir model çünkü geri dönüştürücünün kendisi işe yaramayacak. Tüm görünümler bir seferde çizilecektir (çünkü WRAP_CONTENT, geri dönüşümcü görünümünün yüksekliğine ihtiyaç duyar). Arka planda herhangi bir geri dönüşüm görünümü olmayacağından, geri dönüşümcü görünümünün asıl amacı çalışmıyor. Ancak geri dönüşümlü görünümü kullanarak verileri yönetmek ve düzeni çizmek kolaydır, bu deseni kullanabilmenizin tek nedeni budur. Kesin olarak ihtiyacınız olmadıkça kullanmamak daha iyi.
Ashok Varma

Yanıtlar:


254

Böyle bir sorunu ayarlayarak çözdüm:

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

RecyclerView üzerindeki görüşüme (istenmeyen kaydırmadan sonra gizlendi). Bu özelliği, RecyclerView üzerindeki LinearLayout'a veya RecyclerView konteyneri olan LinearLayout'a ayarlamayı deneyin (başka bir durumda bana yardımcı oldu).

NestedScrollView kaynağında gördüğüm gibi, olası ilk çocuğu onRequestFocusInDescendants'a odaklamaya çalışır ve eğer sadece RecyclerView odaklanabilirse kazanır.

Düzenleme (Waran sayesinde): ve sorunsuz kaydırma için ayarlamayı unutmayın yourRecyclerView.setNestedScrollingEnabled(false);


12
Bu arada, yourRecyclerView.setNestedScrollingEnabled (false); kaydırmayı pürüzsüz hale getirmek için
Waran

1
@Kenji, yalnızca RecyclerView'ın yerleştirildiği LinearLayout'un içinde ilk görüntülemeyi sağlar. Veya ilk değişiklik yardımcı olmazsa bu LinearLayout (RecyclerView konteyneri) kendisine.
Dmitry Gavrilko

1
@DmitryGavrilko RecycleView'ın NestedScrollview içinde olduğu bir sorunum var. Recycleview.scrollToPosition (X) kullanamıyorum; , sadece çalışmıyor. Son 6 günde her şeyi denedim ama üstesinden gelebilirim. herhangi bir öneri? Çok minnettar olurum!
Karoly

3
android:focusableInTouchMode="false"Diğer tüm görünümler için doğru ayarlamanıza gerek kalmaması için sadece recyclerView'e de ayarlayabilirsiniz.
Aksiom

1
Dmitry Gavrilko ile anlaşın, android: focusableInTouchMode = recyclerview içeren lineer düzene "true" ayarlandıktan sonra çalıştı. @Aksiom android: focusableInTouchMode = recyclerView'a "false" ayarı benim için çalışmıyor.
Shendre Kiran

103

Senin de LinearLayouthemen sonrasÖ NestedScrollView, kullanım android:descendantFocusabilityşu şekilde

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

DÜZENLE

Birçoğu bu cevabı faydalı bulduğundan, açıklama da sağlayacaktır.

Kullanımı buradadescendantFocusability verilmiştir . Ve itibariyle üzerinde burada . Yani kullanarak içinde dokunaklı ve dolayısıyla plansız davranış durdurulabilir süre yok kazanç odaklanma 's çocuk verir.focusableInTouchModeblocksDescendantsdescendantFocusability

Gelince focusInTouchMode, hem AbsListViewve RecyclerViewyöntemini çağıran setFocusableInTouchMode(true);o XML düzenleri bu özellik kullanmak için gerekli değildir, bu yüzden varsayılan olarak onların yapıcı içinde.

Ve NestedScrollViewaşağıdaki yöntem için kullanılır:

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();
    }

Burada setFocusable()yerine yöntem kullanılır setFocusableInTouchMode(). Ama buna göre yazı , focusableInTouchModebu Android normal davranış tutarlılığı kırar gibi belirli koşullar için sürece kaçınılmalıdır. Bir oyun, dokunma modunda odaklanabilir özelliği iyi bir şekilde kullanabilen bir uygulamaya iyi bir örnektir. MapView, Google Haritalar'da olduğu gibi tam ekranda kullanılırsa, odaklanabilirliği dokunmatik modda doğru şekilde kullanabileceğiniz başka bir güzel örnektir.


Teşekkür ederim! Benim durumumda çalışıyor. Ne yazık ki android: focusableInTouchMode = "true" benim için çalışmadı.
Mikhail

1
Bu benim için çalıştı. Bu kod satırını geri dönüşüm görünümümün ana görünümüne ekledim (benim durumumda, bu bir LinearLayout idi) ve bir cazibe gibi çalıştı.
amzer

NestedScrollView ve ardından RecyclerView ve EditText içeren LinearLayout kullanıyordum. Kaydırma davranışı, LinearLayout içinde android: descendantFocusability = "BlocksDescendants" kullanılarak düzeltildi, ancak EditText odaklanamıyor. Ne olduğu hakkında bir fikrin var mı?
Sagar Chap yine

@SagarChapagain Tüm neslin blocksDescendantsodaklanmasını engelleme özelliğidir . Emin değilim, ama denebeforeDescendants
Jimit Patel

2
@JimitPatel sorunum kullanılarak çözüldürecyclerView.setFocusable(false); nestedScrollView.requestFocus();
Sagar Chapagain

17
android:descendantFocusability="blocksDescendants"

LinearLayout içinde Benim için çalıştı.


6
Ancak bu, içerideki görüşlere odaklanmanın engelleneceği ve kullanıcıların bunlara ulaşamayacağı anlamına mı geliyor?
android geliştiricisi

11

Aynı sorunu yaşadım ve NestedScrollView'ı genişleterek ve çocuklara odaklanmayı devre dışı bırakarak çözdüm. Bazı nedenlerden dolayı RecyclerView, çekmeceyi yeni açıp kapattığımda bile her zaman odak istedi.

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;
}
}

Çalışıyor ama aynı zamanda odak kavramını da bozuyor ve ekranda iki odaklanmış alanla durumu kolayca yakalayabilirsiniz. Bu nedenle, yalnızca RecyclerViewtutucuların içinde EditText yoksa kullanın .
Andrey Chernoprudov

4

Benim durumumda bu kod benim sorunumu çözüyor

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

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

//populate recyclerview here

Düzenim, LinearLayout alt öğesi olan NestedScrollView olarak bir ana düzen içeriyor. LinearLayout "dikey" oryantasyonuna ve RecyclerView ve EditText alt öğelerine sahiptir. Referans


2

İki tahminim var.

İlk olarak: Bu satırı NestedScrollView’a koymayı deneyin

app:layout_behavior="@string/appbar_scrolling_view_behavior"

İkinci: Kullanım

<android.support.design.widget.CoordinatorLayout

Ebeveyninizin görüşü Bunun gibi

<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>

Olası son çözümüm. Yemin ederim :)


Üst görünüm olarak Koordinatör
Düzenini

2

Bu sorun, geri dönüşüm görünümü Focus nedeniyle ortaya çıkıyor.

Boyutu ekranın boyutunu genişlettiğinde tüm odak otomatik olarak geri dönüşüm görünümüne geçti.

ekleme android:focusableInTouchMode="true"gibi ilk ChildView için TextView, Buttonvb (Not üzerinde ViewGroupgibi Linear, Relativeve So üzerine) mantıklı sorunu ancak API Seviye 25 çözmeye ve çözüm yukarıdaki iş yapmaz.

Tıpkı senin ChildView bu 2 satırı ekleyin TextView, Buttonve So üzerinde (Not üzerinde ViewGroupgibi Linear, Relativeve So üzerine)

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

Bu problemle az önce API seviyesi 25'te karşılaştım. Umarım diğer insanlar bununla vakit kaybetmezler.

RecycleView'da Düzgün Kaydırma için bu satırı ekleyin

 android:nestedScrollingEnabled="false"

ancak bu özelliklerin eklenmesi yalnızca API seviyesi 21 veya üzeri ile çalışır. Düzgün kaydırmanın API seviyesi 25'in altında çalışmasını istiyorsanız, bu satırı sınıfınıza ekleyin

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

0

Java kodunda, recyclerView'unuzu başlattıktan ve adaptörü ayarladıktan sonra şu satırı ekleyin:

recyclerView.setNestedScrollingEnabled(false)

Ayrıca, görünümlerin aynı konumda kalması, ancak recyclerView (kaydırılan) xml hiyerarşisinde ilk sırada olması için düzeni bir göreliLayout ile sarmayı da deneyebilirsiniz. Son öneri umutsuz bir girişimdir: p


0

Cevap vermekte geciktiğim için, ancak başka birine yardımcı olabilir. Uygulama düzeyinde build.gradle'da aşağıdaki veya daha yüksek sürümü kullanmanız yeterlidir; sorun ortadan kalkar.

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

0

yukarı kaydırmak için şunu arayın setcontentview:

scrollView.SmoothScrollTo(0, 0);

bu soruya cevap vermiyor
Umar Ata

0

Sadece eklemek android:descendantFocusability="blocksDescendants"NestedScrollView içeride ViewGroup üzerinde.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.