O RecyclerView dentro do ScrollView não está funcionando


278

Estou tentando implementar um layout que contém RecyclerView e ScrollView no mesmo layout.

Modelo de layout:

<RelativeLayout>
    <ScrollView android:id="@+id/myScrollView">
       <unrelated data>...</unrealated data>

           <android.support.v7.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/my_recycler_view"
            />
    </ScrollView>


</RelativeLayout>

Problemas: eu posso rolar até o último elemento de ScrollView

Coisas que tentei:

  1. visualização do cartão dentro de ScrollView(agora ScrollViewcontém RecyclerView) - pode ver o cartão até oRecyclerView
  2. O pensamento inicial era implementar isso viewGroupusando em RecyclerViewvez de ScrollViewonde um de seus modos de exibição é o, CardViewmas eu tenho exatamente os mesmos resultados que com oScrollView


15
uma solução simples em muitos desses casos é usar NestedScrollViewem vez disso, como ele lida com uma série de questões de rolagem
Richard Le Mesurier

Richard deu a resposta em fevereiro. Use a em NestedScrollViewvez de a ScrollView. É exatamente para isso que serve.
Mike M.

2
Não muda nada para mim.
Luke Allison

2
Para referência futura, se alguém estiver enfrentando problemas semelhantes apenas com dispositivos marshmallow / nougat (API 23, 24), verifique minha solução alternativa em stackoverflow.com/a/38995399/132121
Hossain Khan 17/16

Respostas:


654

use em NestedScrollViewvez deScrollView

Consulte o documento de referência NestedScrollView para obter mais informações.

e adicione recyclerView.setNestedScrollingEnabled(false);ao seuRecyclerView


24
Trabalha com: android.support.v4.widget.NestedScrollView
Cocorico

7
manter android:layout_height="wrap_content"para o layout inflado para ViewHolder
Sarath Babu

4
Em um layout complexo NestedScrollViewfica para mim, ao contrário do ScrollView. Procura de uma solução sem usarNestedScrollView
Roman Samoylenko

7
Além disso, você pode adicionar android: nestedScrollingEnabled = "false" ao XML em vez de recyclerView.setNestedScrollingEnabled (false);
kostyabakay

2
Funcionou para mim, mas lembre-se de que os itens do recyclerView não estão sendo reciclados.
Mostafa Arian Nejad

102

Sei que estou atrasado no jogo, mas o problema ainda existe mesmo depois que o Google corrigiu o problema. android.support.v7.widget.RecyclerView

A questão eu recebo agora é RecyclerViewcom layout_height=wrap_contentnão tomar altura de toda a questão itens dentro ScrollViewque única acontece em versões Marshmallow e Nougat + (API 23, 24, 25).
(ATUALIZAÇÃO: Substituindo ScrollViewpor android.support.v4.widget.NestedScrollViewobras em todas as versões. De alguma forma, perdi o teste da solução aceita . Adicionei isso no meu projeto do github como demonstração.)

Depois de tentar coisas diferentes, encontrei uma solução alternativa que corrige esse problema.

Aqui está minha estrutura de layout em poucas palavras:

<ScrollView>
  <LinearLayout> (vertical - this is the only child of scrollview)
     <SomeViews>
     <RecyclerView> (layout_height=wrap_content)
     <SomeOtherViews>

A solução alternativa é o wrap the RecyclerViewwith RelativeLayout. Não me pergunte como encontrei essa solução alternativa !!!¯\_(ツ)_/¯

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:descendantFocusability="blocksDescendants">

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

Exemplo completo está disponível no projeto GitHub - https://github.com/amardeshbd/android-recycler-view-wrap-content

Aqui está um screencast de demonstração mostrando a correção em ação:

Screencast


6
Obrigado cara .. é muita ajuda .. mas a rolagem fica difícil após a sua solução, então defino recyclerview.setNestedScrollingEnabled (false); e agora funciona como um encanto.
Sagar Chavada

4
Este método é visualizações de reciclagem? se tivermos centenas de objetos a serem reciclados. Isso não é solução para hackear.
androidXP 25/05

1
Sim, @androidXP está certo, este truque não é uma solução para uma longa lista. Meu caso de uso foi corrigido no item em uma exibição de lista inferior a 10. E quanto à maneira como encontrei a outra solução alternativa, estava tentando coisas aleatórias, essa era uma delas :-)
Hossain Khan

perfect.solved meu problema no adnroid marshmallow e superior. ;)
Amir Ziarati

2
se eu usar essa solução, o onBindView será chamado para todos os itens da lista, que não é o caso da reciclerview.
thedarkpassenger

54

Embora a recomendação de que

você nunca deve colocar uma exibição rolável dentro de outra exibição rolável

É um bom conselho, no entanto, se você definir uma altura fixa na exibição da recicladora, ela deverá funcionar bem.

Se você conhece a altura do layout do item do adaptador, basta calcular a altura do RecyclerView.

int viewHeight = adapterItemSize * adapterData.size();
recyclerView.getLayoutParams().height = viewHeight;

10
Como obter adapterItemSize na reciclagem, tem alguma ideia?
Krutik

1
Ele funciona como um encanto! Pouca correção deve ser: int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = viewHeight;
Vaibhav Jani

3
Como descobrir o adapterItemSize?
Droidster.me 21/08/2015

2
@JoakimEngstrom Qual é a adapterItemSizevariável?
IgorGanapolsky

1
Evite a exibição do reciclador na exibição de rolagem, porque a exibição de rolagem oferece um espaço infinito ao filho. Isso faz com que a exibição do reciclador com wrap_content como altura seja medida infinitamente na direção vertical (até o último item da exibição do reciclador). Em vez de usar a exibição de reciclagem dentro da exibição de rolagem, use apenas a exibição de reciclagem com diferentes tipos de itens. Com essa implementação, os filhos da exibição de rolagem se comportariam como tipo de exibição. Manipule esses tipos de exibição na exibição do reciclador.
Abhishesh 25/05

28

Caso a configuração de altura fixa para o RecyclerView não funcione para alguém (como eu), eis o que eu adicionei à solução de altura fixa:

mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        int action = e.getAction();
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                rv.getParent().requestDisallowInterceptTouchEvent(true);
                break;
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
});

1
Na verdade, isso funcionou bem para mim. Com isso, consegui mover a exibição de rolagem para cima e para baixo e, quando seleciono a revisão de rolagem para rolagem, ela tem prioridade sobre a exibição de rolagem. Obrigado pela dica
PGMacDesign 10/06/2015

1
funcionou para mim também, apenas certifique-se de usar getParent em um filho direto do scrollview
BigBen3216

Este método também funciona em distribuições de cyanogenmod. A solução de altura fixa no cyanogenmod funciona, mas somente se a altura fixa for a altura absoluta de todos os itens da lista, o que desafia o ponto de usar a reciclagem em primeiro lugar. Votado.
28415 HappyKatz

Eu também precisava de recyclerView.setNestedScrollingEnabled (false);
Analizer


15

RecyclerViews é bom para colocar em ScrollViews, desde que não estejam rolando eles mesmos. Nesse caso, faz sentido torná-lo uma altura fixa.

A solução adequada é usar wrap_content na altura do RecyclerView e implementar um LinearLayoutManager personalizado que possa lidar adequadamente com o empacotamento.

Copie este LinearLayoutManager para o seu projeto: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Em seguida, envolva o RecyclerView:

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

E configure-o assim:

    RecyclerView list = (RecyclerView)findViewById(R.id.list);
    list.setHasFixedSize(true);
    list.setLayoutManager(new com.example.myapp.LinearLayoutManager(list.getContext()));
    list.setAdapter(new MyViewAdapter(data));

Editar: Isso pode causar complicações com a rolagem, porque o RecyclerView pode roubar os eventos de toque do ScrollView. Minha solução foi apenas abandonar o RecyclerView em todos e usar um LinearLayout, inflar programaticamente sub-visualizações e adicioná-las ao layout.


4
Não foi possível chamar setNestedScrollingEnabled (false) na visualização da reciclagem?
Eshaan

14

Para ScrollView, você pode usar fillViewport=truee fazer layout_height="match_parent"como abaixo e colocar a visão do reciclador dentro:

<ScrollView
    android:fillViewport="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/llOptions">
          <android.support.v7.widget.RecyclerView
            android:id="@+id/rvList"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
</ScrollView>

Nenhum outro ajuste de altura é necessário através do código.


Testado funcionando bem usando a v23.2.1. Estava usando-o para adicionar layout acima da reciclagem.
winsontan520

apenas o RecyclerView rolagem eo ScrollView não rolagem
Samad

13

Calcular RecyclerViewa altura manualmente não é bom, melhor é usar um costume LayoutManager.

A razão para emissão acima é qualquer ponto de vista que tem de rolagem ( ListView, GridView, RecyclerView) falhou para o seu cálculo de altura quando add como uma criança em uma outra visão tem de rolagem. Portanto, substituir seu onMeasuremétodo resolverá o problema.

Substitua o gerenciador de layout padrão pelo seguinte:

public class MyLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

private static boolean canMakeInsetsDirty = true;
private static Field insetsDirtyField = null;

private static final int CHILD_WIDTH = 0;
private static final int CHILD_HEIGHT = 1;
private static final int DEFAULT_CHILD_SIZE = 100;

private final int[] childDimensions = new int[2];
private final RecyclerView view;

private int childSize = DEFAULT_CHILD_SIZE;
private boolean hasChildSize;
private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
private final Rect tmpRect = new Rect();

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(Context context) {
    super(context);
    this.view = null;
}

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
    this.view = null;
}

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(RecyclerView view) {
    super(view.getContext());
    this.view = view;
    this.overScrollMode = ViewCompat.getOverScrollMode(view);
}

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
    super(view.getContext(), orientation, reverseLayout);
    this.view = view;
    this.overScrollMode = ViewCompat.getOverScrollMode(view);
}

public void setOverScrollMode(int overScrollMode) {
    if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
        throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
    if (this.view == null) throw new IllegalStateException("view == null");
    this.overScrollMode = overScrollMode;
    ViewCompat.setOverScrollMode(view, overScrollMode);
}

public static int makeUnspecifiedSpec() {
    return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);

    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);

    final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
    final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;

    final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
    final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;

    final int unspecified = makeUnspecifiedSpec();

    if (exactWidth && exactHeight) {
        // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
        super.onMeasure(recycler, state, widthSpec, heightSpec);
        return;
    }

    final boolean vertical = getOrientation() == VERTICAL;

    initChildDimensions(widthSize, heightSize, vertical);

    int width = 0;
    int height = 0;

    // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
    // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
    // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
    // called whiles scrolling)
    recycler.clear();

    final int stateItemCount = state.getItemCount();
    final int adapterItemCount = getItemCount();
    // adapter always contains actual data while state might contain old data (f.e. data before the animation is
    // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
    // state
    for (int i = 0; i < adapterItemCount; i++) {
        if (vertical) {
            if (!hasChildSize) {
                if (i < stateItemCount) {
                    // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                    // we will use previously calculated dimensions
                    measureChild(recycler, i, widthSize, unspecified, childDimensions);
                } else {
                    logMeasureWarning(i);
                }
            }
            height += childDimensions[CHILD_HEIGHT];
            if (i == 0) {
                width = childDimensions[CHILD_WIDTH];
            }
            if (hasHeightSize && height >= heightSize) {
                break;
            }
        } else {
            if (!hasChildSize) {
                if (i < stateItemCount) {
                    // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                    // we will use previously calculated dimensions
                    measureChild(recycler, i, unspecified, heightSize, childDimensions);
                } else {
                    logMeasureWarning(i);
                }
            }
            width += childDimensions[CHILD_WIDTH];
            if (i == 0) {
                height = childDimensions[CHILD_HEIGHT];
            }
            if (hasWidthSize && width >= widthSize) {
                break;
            }
        }
    }

    if (exactWidth) {
        width = widthSize;
    } else {
        width += getPaddingLeft() + getPaddingRight();
        if (hasWidthSize) {
            width = Math.min(width, widthSize);
        }
    }

    if (exactHeight) {
        height = heightSize;
    } else {
        height += getPaddingTop() + getPaddingBottom();
        if (hasHeightSize) {
            height = Math.min(height, heightSize);
        }
    }

    setMeasuredDimension(width, height);

    if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
        final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
                || (!vertical && (!hasWidthSize || width < widthSize));

        ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
    }
}

private void logMeasureWarning(int child) {
    if (BuildConfig.DEBUG) {
        Log.w("MyLinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
    }
}

private void initChildDimensions(int width, int height, boolean vertical) {
    if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
        // already initialized, skipping
        return;
    }
    if (vertical) {
        childDimensions[CHILD_WIDTH] = width;
        childDimensions[CHILD_HEIGHT] = childSize;
    } else {
        childDimensions[CHILD_WIDTH] = childSize;
        childDimensions[CHILD_HEIGHT] = height;
    }
}

@Override
public void setOrientation(int orientation) {
    // might be called before the constructor of this class is called
    //noinspection ConstantConditions
    if (childDimensions != null) {
        if (getOrientation() != orientation) {
            childDimensions[CHILD_WIDTH] = 0;
            childDimensions[CHILD_HEIGHT] = 0;
        }
    }
    super.setOrientation(orientation);
}

public void clearChildSize() {
    hasChildSize = false;
    setChildSize(DEFAULT_CHILD_SIZE);
}

public void setChildSize(int childSize) {
    hasChildSize = true;
    if (this.childSize != childSize) {
        this.childSize = childSize;
        requestLayout();
    }
}

private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
    final View child;
    try {
        child = recycler.getViewForPosition(position);
    } catch (IndexOutOfBoundsException e) {
        if (BuildConfig.DEBUG) {
            Log.w("MyLinearLayoutManager", "MyLinearLayoutManager doesn't work well with animations. Consider switching them off", e);
        }
        return;
    }

    final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();

    final int hPadding = getPaddingLeft() + getPaddingRight();
    final int vPadding = getPaddingTop() + getPaddingBottom();

    final int hMargin = p.leftMargin + p.rightMargin;
    final int vMargin = p.topMargin + p.bottomMargin;

    // we must make insets dirty in order calculateItemDecorationsForChild to work
    makeInsetsDirty(p);
    // this method should be called before any getXxxDecorationXxx() methods
    calculateItemDecorationsForChild(child, tmpRect);

    final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
    final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

    final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
    final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());

    child.measure(childWidthSpec, childHeightSpec);

    dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
    dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;

    // as view is recycled let's not keep old measured values
    makeInsetsDirty(p);
    recycler.recycleView(child);
}

private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
    if (!canMakeInsetsDirty) {
        return;
    }
    try {
        if (insetsDirtyField == null) {
            insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
            insetsDirtyField.setAccessible(true);
        }
        insetsDirtyField.set(p, true);
    } catch (NoSuchFieldException e) {
        onMakeInsertDirtyFailed();
    } catch (IllegalAccessException e) {
        onMakeInsertDirtyFailed();
    }
}

private static void onMakeInsertDirtyFailed() {
    canMakeInsetsDirty = false;
    if (BuildConfig.DEBUG) {
        Log.w("MyLinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
    }
}
}

Como posso chamar essa classe quando configuramos os dados para o adaptador?
Milan Gajera 13/04/19

11

Tente isso. Resposta muito tarde. Mas certamente ajude alguém no futuro.

Defina seu Scrollview como NestedScrollView

<android.support.v4.widget.NestedScrollView>
 <android.support.v7.widget.RecyclerView>
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.NestedScrollView>

Na sua visão de reciclagem

recyclerView.setNestedScrollingEnabled(false); 
recyclerView.setHasFixedSize(false);

8
usar RecyclerView dentro de NestedScrollView está chamando onBindView para cada item da lista, mesmo que o item não esteja visível. Alguma solução para esse problema?
thedarkpassenger

8

UPDATE: esta resposta está desatualizada agora, pois existem widgets como NestedScrollView e RecyclerView que oferecem suporte à rolagem aninhada.

você nunca deve colocar uma exibição rolável dentro de outra exibição rolável!

Eu sugiro que você faça sua exibição principal do reciclador de layout e coloque suas visualizações como itens da exibição do reciclador.

dê uma olhada neste exemplo, que mostra como usar várias visualizações no adaptador de exibição do reciclador. link para exemplo


Eu tenho uma página com mais de um Reciclador . Existe alguma outra maneira de convencer isso? alguma coisa como Instagram ou Google Play comentário parte que mais carga registro quando você clica em mais comentários
Ashkan

torná-lo uma única recyclerView e colocar seus pontos de vista como itens para o reciclador
EC84B4

6
Isso é um absurdo. Você pode aninhar RecyclerViews muito bem.
IgorGanapolsky

Esta resposta está desatualizada. Temos coisas como NestedScrollView que permitem visualizações roláveis ​​aninhadas. Agora, o Nested RecyclerViews também funciona.
Corey Horn

7

Se você colocar RecyclerViewdentro NestedScrollViewe ativar recyclerView.setNestedScrollingEnabled(false);, a rolagem funcionará bem .
No entanto, há um problema

RecyclerView não recicle

Por exemplo, seu RecyclerView(interno NestedScrollViewou ScrollView) possui 100 itens.
Ao Activityiniciar, serão criados 100 itens ( onCreateViewHoldere onBindViewHolder100 itens serão chamados ao mesmo tempo).
Exemplo, para cada item, você carregará uma imagem grande da API => atividade criada -> 100 imagens serão carregadas.
Torna a atividade inicial lenta e atrasada.
Solução possível :
- Pensando em usar RecyclerViewcom vários tipos.

No entanto, se no seu caso, houver apenas alguns itens dentro RecyclerViewe reciclar ou não reciclar não afetam muito o desempenho, você pode usá-lo RecyclerViewdentro ScrollViewpara


Mostrar como posso fazer o RecyclerView reciclar mesmo dentro do ScollView? Obrigado!
Mentiroso

2
@ Mentiroso, atualmente não há como RecyclerViewreciclar depois de colocá-lo ScrollView. Se você quiser reciclar, pensando em outra abordagem (como usar RecyclerView com o tipo múltipla)
Phan Van Linh

4

Você pode usar desta maneira:

Adicione esta linha à sua visualização xml do recyclerView:

        android:nestedScrollingEnabled="false"

experimente, a reciclagem será rolada suavemente com altura flexível

espero que isso tenha ajudado.


2

Parece que NestedScrollViewresolve o problema. Eu testei usando este layout:

<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/dummy_text"
        />

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        >
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </android.support.v7.widget.CardView>

</LinearLayout>

E funciona sem problemas


Bro, ainda tem o mesmo problema depois de alterado para NestedScrollview no Scrollview.
Mohanraj S

mmm ... Você pode compartilhar algum código ... Eu estou tendo zero problemas, mas u nunca pode saber com este tipo de questões
royB

Como posso compartilhar meu código por e-mail ou através de estouro de pilha
Mohanraj S

2
o uso desse código chama onBindView para todos os itens da lista, mesmo que esses itens não estejam visíveis na lista. Isso anula o objetivo da reciclagem.
thedarkpassenger

2

Usei o CustomLayoutManager para desativar a rolagem do RecyclerView. Também não use o Recycler View como WrapContent, use-o como 0dp, Weight = 1

public class CustomLayoutManager extends LinearLayoutManager {
    private boolean isScrollEnabled;

    // orientation should be LinearLayoutManager.VERTICAL or HORIZONTAL
    public CustomLayoutManager(Context context, int orientation, boolean isScrollEnabled) {
        super(context, orientation, false);
        this.isScrollEnabled = isScrollEnabled;
    }

    @Override
    public boolean canScrollVertically() {
        //Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
        return isScrollEnabled && super.canScrollVertically();
    }
}

Use CustomLayoutManager no RecyclerView:

CustomLayoutManager mLayoutManager = new CustomLayoutManager(getBaseActivity(), CustomLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(mLayoutManager);
        ((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 
        recyclerView.setAdapter(statsAdapter);

XML da interface do usuário:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/background_main"
    android:fillViewport="false">


    <LinearLayout
        android:id="@+id/contParentLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <edu.aku.family_hifazat.libraries.mpchart.charts.PieChart
                android:id="@+id/chart1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/x20dp"
                android:minHeight="@dimen/x300dp">

            </edu.aku.family_hifazat.libraries.mpchart.charts.PieChart>


        </FrameLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">


        </android.support.v7.widget.RecyclerView>


    </LinearLayout>


</ScrollView>

2

Eu estava tendo o mesmo problema. Isso é o que eu tentei e funciona. Estou compartilhando meu código xml e java. Espero que isso ajude alguém.

Aqui está o xml

<?xml version="1.0" encoding="utf-8"?>

< NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/iv_thumbnail"
            android:layout_width="match_parent"
            android:layout_height="200dp" />

        <TextView
            android:id="@+id/tv_description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Description" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Buy" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Reviews" />
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rc_reviews"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        </android.support.v7.widget.RecyclerView>

    </LinearLayout>
</NestedScrollView >

Aqui está o código java relacionado. Ele funciona como um encanto.

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setNestedScrollingEnabled(false);

E os layouts abaixo do RecyclerView?
Tapa Salvar

isso funciona também. Você só precisa usar. NestedScrollViewem vez de vista de rolagem
Zeeshan Shabbir

Sim, apenas NestedScrollView. Mas solução com chamadas NestedScrollView + RecycleView populações muito lentas de RecyclerView (acho que para calcutaion de Y-posição da primeira vista após RecyclerView)
Tapa Salvar


0

Na verdade, o principal objetivo do RecyclerViewé compensar ListViewe ScrollView. Em vez de fazer o que você está realmente fazendo: ter um RecyclerViewem um ScrollView, sugiro ter apenas um RecyclerViewque possa lidar com muitos tipos de crianças.


1
Isso funcionaria apenas desde que seus filhos pudessem ser coletados no lixo assim que você os rolasse para fora da vista. Se você tem filhos que são mapFragments ou streetviews, não faz sentido, pois eles são forçados a recarregar cada vez que rolam para fora da visualização de reciclagem. Incorporá-los em uma visualização de rolagem e, em seguida, gerar uma visão de reciclagem na parte inferior faz mais sentido.
Simon Simon

1
@Simon there is handy setIsRecyclable () in ViewHolder
ernazm

O RecyclerView substitui o ListView, não pretende substituir o ScrollView.
26716 cyroxis

Este comentário merece melhor. É uma solução e ainda melhor que a aceita.
Kai Wang

0

Primeiro você deve usar em NestedScrollViewvez de ScrollViewcolocar o RecyclerViewinterior NestedScrollView.

Use a classe de layout personalizado para medir a altura e a largura da tela:

public class CustomLinearLayoutManager extends LinearLayoutManager {

public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
}

private int[] mMeasuredDimension = new int[2];

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                      int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int width = 0;
    int height = 0;
    for (int i = 0; i < getItemCount(); i++) {
        if (getOrientation() == HORIZONTAL) {
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    heightSpec,
                    mMeasuredDimension);

            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            measureScrapChild(recycler, i,
                    widthSpec,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);
            height = height + mMeasuredDimension[1];
            if (i == 0) {
                width = mMeasuredDimension[0];
            }
        }
    }
    switch (widthMode) {
        case View.MeasureSpec.EXACTLY:
            width = widthSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    switch (heightMode) {
        case View.MeasureSpec.EXACTLY:
            height = heightSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    setMeasuredDimension(width, height);
}

private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                               int heightSpec, int[] measuredDimension) {
    View view = recycler.getViewForPosition(position);
    recycler.bindViewToPosition(view, position);
    if (view != null) {
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                getPaddingLeft() + getPaddingRight(), p.width);
        int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                getPaddingTop() + getPaddingBottom(), p.height);
        view.measure(childWidthSpec, childHeightSpec);
        measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
        measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
        recycler.recycleView(view);
    }
}
}

E implemente o código abaixo na atividade / fragmento de RecyclerView:

 final CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);

    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(mAdapter);

    recyclerView.setNestedScrollingEnabled(false); // Disables scrolling for RecyclerView, CustomLinearLayoutManager used instead of MyLinearLayoutManager
    recyclerView.setHasFixedSize(false);

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            int visibleItemCount = layoutManager.getChildCount();
            int totalItemCount = layoutManager.getItemCount();
            int lastVisibleItemPos = layoutManager.findLastVisibleItemPosition();
            Log.i("getChildCount", String.valueOf(visibleItemCount));
            Log.i("getItemCount", String.valueOf(totalItemCount));
            Log.i("lastVisibleItemPos", String.valueOf(lastVisibleItemPos));
            if ((visibleItemCount + lastVisibleItemPos) >= totalItemCount) {
                Log.i("LOG", "Last Item Reached!");
            }
        }
    });

0

Se o RecyclerView mostrar apenas uma linha dentro do ScrollView. Você só precisa definir a altura da sua linha para android:layout_height="wrap_content".


0

você também pode substituir o LinearLayoutManager para fazer a visualização da reciclagem rolar suavemente

@Override
    public boolean canScrollVertically(){
        return false;
    }

0

Desculpe chegar atrasado à festa, mas parece que existe outra solução que funciona perfeitamente para o caso que você mencionou.

Se você usar uma exibição de reciclador dentro de uma exibição de reciclador, ela parece funcionar perfeitamente bem. Eu pessoalmente tentei e o usei, e parece não dar lentidão nem agitação. Agora não tenho certeza se essa é uma boa prática ou não, mas, ao aninhar várias visualizações do reciclador, até a visualização de rolagem aninhada diminui. Mas isso parece funcionar bem. Por favor, tente. Tenho certeza de que o aninhamento vai ficar perfeitamente bem com isso.


0

Outra abordagem para resolver o problema é usar o ConstraintLayoutinterior ScrollView:

<ScrollView>
  <ConstraintLayout> (this is the only child of ScrollView)
    <...Some Views...>
    <RecyclerView> (layout_height=wrap_content)
    <...Some Other Views...>

Mas eu ainda continuaria com a androidx.core.widget.NestedScrollView abordagem proposta por Yang Peiyong .


-2

** Solução que funcionou para mim
Use NestedScrollView com altura como wrap_content

<br> RecyclerView 
                android:layout_width="match_parent"<br>
                android:layout_height="wrap_content"<br>
                android:nestedScrollingEnabled="false"<br>
                  app:layoutManager="android.support.v7.widget.LinearLayoutManager"
                tools:targetApi="lollipop"<br><br> and view holder layout 
 <br> android:layout_width="match_parent"<br>
        android:layout_height="wrap_content"

// O conteúdo da sua linha entra aqui


Alguns comentários sobre uma resposta semelhante dizem que desativar a rolagem aninhada anula o propósito de usar um RecyclerView, ou seja, que ele não reciclará visualizações. Não tenho certeza de como confirmar isso.
Jk7 # 13/17

1
Definir nestedScrollingEnabled = "false" faz com que o RecyclerView NÃO recicle suas visualizações, pelo menos na minha configuração (um RecyclerView dentro de um NestedScrollView). Confirmado adicionando um RecyclerListener ao RecyclerView e definindo um ponto de interrupção dentro do método onViewRecycled ().
Jk7 # 13/17
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.