¿Cómo construir un ListView horizontal con RecyclerView?

338

Necesito implementar una vista de lista horizontal en mi aplicación de Android. Investigué un poco y encontré ¿Cómo puedo hacer un ListView horizontal en Android? y Horizontal ListView en Android? sin embargo, estas preguntas se hicieron antes de que se publicara Recyclerview. ¿Hay una mejor manera de implementar esto ahora con Recyclerview?

Andre Perkins
fuente
12
Simplemente use un LinearLayoutManagercon orientación establecida en HORIZONTAL.
Egor Neliuba
@EgorN Lo intenté, lo hace horizontal pero parece que incluso cambia los elementos secundarios de la fila del adaptador a horizontal también. Tengo un RelativeLayout. No estoy seguro de cómo solucionar esto?
Muhammad Umar

Respuestas:

740

¿Hay una mejor manera de implementar esto ahora con Recyclerview ahora?

Si.

Cuando utiliza un RecyclerView, debe especificar uno LayoutManagerque sea responsable de diseñar cada elemento en la vista. El le LinearLayoutManagerpermite especificar una orientación, como lo LinearLayoutharía normalmente .

Para crear una lista horizontal con RecyclerView, puede hacer algo como esto:

LinearLayoutManager layoutManager
    = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);

RecyclerView myList = (RecyclerView) findViewById(R.id.my_recycler_view);
myList.setLayoutManager(layoutManager);
Bryan Herbst
fuente
Lo intenté, lo hace horizontal, pero parece que incluso cambia los elementos secundarios de la fila del adaptador a horizontal también. Tengo un RelativeLayout. No estoy seguro de cómo solucionar esto?
Muhammad Umar
2
RelativeLayoutno tiene el concepto de horizontal versus vertical, por lo que realmente no entiendo la pregunta.
Bryan Herbst
2
Aparentemente, hay algunos problemas con RecyclerView y el LayoutManager de desplazamiento horizontal ... code.google.com/p/android/issues/detail?id=74772 - lo encontré porque también estaba teniendo problemas con el uso de RecyclerView de desplazamiento horizontal
AgentKnopf
Zainodis, ¿has descubierto qué usar entonces? ¿LinearLayoutManager ni siquiera aparece como una importación para mí? ¿Me estoy perdiendo algo
Lion789
@ Tanis.7x Esto funciona muy bien para mí, pero llena la lista de izquierda a derecha. ¿Alguien sabe si hay una manera de poblar de derecha a izquierda? (El primer artículo es más a la derecha en la lista, elemento en la posición 1 es luego hacia la izquierda, y así sucesivamente ...)
raisedandglazed
169
 <android.support.v7.widget.RecyclerView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
agua hervida
fuente
¿Cómo configurar el LayoutManager entonces?
Kai Wang
@kaiwang pls ver arriba "Tanis.7x" respuesta.
agua hervida
app:layoutManager="android.support.v7.widget.LinearLayoutManager"no funcionará para la compilación de lanzamiento. Me he enfrentado a este problema Release build.
Abu Yousuf
Estaba buscando cómo mostrar esto en el generador de interfaces. herramientas: orientación = herramientas "horizontales": layoutManager = "android.support.v7.widget.LinearLayoutManager" me salvó gracias.
Mohammad Tabbara
'<androidx.recyclerview.widget.RecyclerView android: layout_width = "match_parent" android: layout_height = "70dp" android: layout_gravity = "bottom" android: orientación = "horizontal" aplicación: layoutManager = "androidx.recyclerview.widget.LinearLayoutManager" /> '
Yanny
74

Ejemplo completo

ingrese la descripción de la imagen aquí

La única diferencia real entre una vertical RecyclerViewy una horizontal es cómo configura el LinearLayoutManager. Aquí está el fragmento de código. El ejemplo completo está abajo.

LinearLayoutManager horizontalLayoutManagaer = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(horizontalLayoutManagaer);

Este ejemplo más completo sigue el modelo de mi RecyclerViewrespuesta vertical .

Actualizar dependencias de Gradle

Asegúrese de que las siguientes dependencias estén en su gradle.buildarchivo de aplicación :

implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'

Puede actualizar los números de versión a lo que sea más actual .

Crear diseño de actividad

Agregue el RecyclerViewa su diseño xml.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

</RelativeLayout>

Crear diseño de elemento

Cada artículo en nuestro RecyclerViewtendrá un solo color Viewsobre un TextView. Cree un nuevo archivo de recursos de diseño.

recyclerview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp">

    <View
        android:id="@+id/colorView"
        android:layout_width="100dp"
        android:layout_height="100dp"/>

    <TextView
        android:id="@+id/tvAnimalName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"/>

</LinearLayout>

Crea el adaptador

El RecyclerViewnecesita un adaptador para poblar los puntos de vista de cada fila (punto horizontal) con sus datos. Crea un nuevo archivo java.

MyRecyclerViewAdapter.java

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {

    private List<Integer> mViewColors;
    private List<String> mAnimals;
    private LayoutInflater mInflater;
    private ItemClickListener mClickListener;

    // data is passed into the constructor
    MyRecyclerViewAdapter(Context context, List<Integer> colors, List<String> animals) {
        this.mInflater = LayoutInflater.from(context);
        this.mViewColors = colors;
        this.mAnimals = animals;
    }

    // inflates the row layout from xml when needed
    @Override
    @NonNull
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.recyclerview_item, parent, false);
        return new ViewHolder(view);
    }

    // binds the data to the view and textview in each row
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        int color = mViewColors.get(position);
        String animal = mAnimals.get(position);
        holder.myView.setBackgroundColor(color);
        holder.myTextView.setText(animal);
    }

    // total number of rows
    @Override
    public int getItemCount() {
        return mAnimals.size();
    }

    // stores and recycles views as they are scrolled off screen
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        View myView;
        TextView myTextView;

        ViewHolder(View itemView) {
            super(itemView);
            myView = itemView.findViewById(R.id.colorView);
            myTextView = itemView.findViewById(R.id.tvAnimalName);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
        }
    }

    // convenience method for getting data at click position
    public String getItem(int id) {
        return mAnimals.get(id);
    }

    // allows clicks events to be caught
    public void setClickListener(ItemClickListener itemClickListener) {
        this.mClickListener = itemClickListener;
    }

    // parent activity will implement this method to respond to click events
    public interface ItemClickListener {
        void onItemClick(View view, int position);
    }
}

Notas

  • Aunque no es estrictamente necesario, incluí la funcionalidad para escuchar eventos de clic en los elementos. Esto estaba disponible en el antiguo ListViewsy es una necesidad común. Puede eliminar este código si no lo necesita.

Inicializar RecyclerView en actividad

Agregue el siguiente código a su actividad principal.

MainActivity.java

public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener {

    private MyRecyclerViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // data to populate the RecyclerView with
        ArrayList<Integer> viewColors = new ArrayList<>();
        viewColors.add(Color.BLUE);
        viewColors.add(Color.YELLOW);
        viewColors.add(Color.MAGENTA);
        viewColors.add(Color.RED);
        viewColors.add(Color.BLACK);

        ArrayList<String> animalNames = new ArrayList<>();
        animalNames.add("Horse");
        animalNames.add("Cow");
        animalNames.add("Camel");
        animalNames.add("Sheep");
        animalNames.add("Goat");

        // set up the RecyclerView
        RecyclerView recyclerView = findViewById(R.id.rvAnimals);
        LinearLayoutManager horizontalLayoutManager
                = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.HORIZONTAL, false);
        recyclerView.setLayoutManager(horizontalLayoutManager);
        adapter = new MyRecyclerViewAdapter(this, viewColors, animalNames);
        adapter.setClickListener(this);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on item position " + position, Toast.LENGTH_SHORT).show();
    }
}

Notas

  • Observe que la actividad implementa lo ItemClickListenerque definimos en nuestro adaptador. Esto nos permite manejar los eventos de clic de elemento en onItemClick.

Terminado

Eso es. Debería poder ejecutar su proyecto ahora y obtener algo similar a la imagen en la parte superior.

Notas

Suragch
fuente
12

Si desea utilizar un RecyclerViewcon el GridLayoutManager, esta es la forma de lograr el desplazamiento horizontal.

recyclerView.setLayoutManager(
new GridLayoutManager(recyclerView.getContext(), rows, GridLayoutManager.HORIZONTAL, false));
José Cabrera
fuente
Esto funciona bien para mí ... principalmente porque puede establecer el número de filas ... ¿es posible hacer esto también en LinearLayoutManager?
superUser
9

Intentar construir un ListView horizontal lleva demasiado tiempo. Lo he resuelto de dos maneras.

1. Usando un ViewPager cuyo adaptador se extiende desde PagerAdapter.

2. Usando RecyclerView tal como se indica arriba. Necesita aplicar LayoutManager como en el siguiente código:

LinearLayoutManager layoutManager
    = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);

RecyclerView myList = (RecyclerView) findViewById(R.id.my_recycler_view);
myList.setLayoutManager(layoutManager);
Jayman Jani
fuente
6

Si desea utilizar la Vista horizontal del reciclador para actuar como ViewPager, ahora es posible con la ayuda de LinearSnapHelperla versión 24.2.0 de la Biblioteca de soporte.

En primer lugar, agregue RecyclerView a su actividad / fragmento

<android.support.v7.widget.RecyclerView
        android:layout_below="@+id/sign_in_button"
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:id="@+id/blog_list"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>

En mi caso he utilizado una CardViewdentro de laRecyclerView

blog_row.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 

    xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_margin="15dp"
        android:orientation="vertical">

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

            <com.android.volley.toolbox.NetworkImageView
                android:id="@+id/imageBlogPost"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:paddingBottom="15dp"
                android:src="@drawable/common_google_signin_btn_text_light_normal" />

            <TextView
                android:id="@+id/TitleTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
               android:layout_marginBottom="20dp"

                android:text="Post Title Here"
                android:textSize="16sp" />

            <TextView
                android:id="@+id/descriptionTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Post Description Here"
                android:paddingBottom="15dp"
                android:textSize="14sp" />
        </LinearLayout>

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

En tu actividad / fragmento

    private RecyclerView mBlogList;




 LinearLayoutManager layoutManager
                    = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
            mBlogList = (RecyclerView) findViewById(R.id.blog_list);

            mBlogList.setHasFixedSize(true);
            mBlogList.setLayoutManager(layoutManager);

LinearSnapHelper snapHelper = new LinearSnapHelper() {
            @Override
            public int findTargetSnapPosition(RecyclerView.LayoutManager lm, int velocityX, int velocityY) {
                View centerView = findSnapView(lm);
                if (centerView == null)
                    return RecyclerView.NO_POSITION;

                int position = lm.getPosition(centerView);
                int targetPosition = -1;
                if (lm.canScrollHorizontally()) {
                    if (velocityX < 0) {
                        targetPosition = position - 1;
                    } else {
                        targetPosition = position + 1;
                    }
                }

                if (lm.canScrollVertically()) {
                    if (velocityY < 0) {
                        targetPosition = position - 1;
                    } else {
                        targetPosition = position + 1;
                    }
                }

                final int firstItem = 0;
                final int lastItem = lm.getItemCount() - 1;
                targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem));
                return targetPosition;
            }
        };
        snapHelper.attachToRecyclerView(mBlogList);

El último paso es configurar el adaptador a RecyclerView

mBlogList.setAdapter(firebaseRecyclerAdapter);
AndroidBeginner
fuente
4

Con el lanzamiento de la biblioteca RecyclerView, ahora puede alinear una lista de imágenes enlazadas con texto fácilmente. Puede usar LinearLayoutManager para especificar la dirección en la que le gustaría orientar su lista, ya sea vertical u horizontal como se muestra a continuación.

ingrese la descripción de la imagen aquí

Puede descargar una demostración completa de esta publicación

Daniel Nyamasyo
fuente
2
 <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            >
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:scrollbars="vertical|horizontal" />
        </HorizontalScrollView>

    import androidx.appcompat.app.AppCompatActivity;
    import android.content.Context;
    import android.content.ContextWrapper;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.os.Environment;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.Toast;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    public class MainActivity extends AppCompatActivity
     {
        ImageView mImageView1;
        Bitmap bitmap;
        String mSavedInfo;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mImageView1 = (ImageView) findViewById(R.id.image);
        }
        public Bitmap getBitmapFromURL(String src) {
            try {
                java.net.URL url = new java.net.URL(src);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoInput(true);
                connection.connect();
                InputStream input = connection.getInputStream();
                Bitmap myBitmap = BitmapFactory.decodeStream(input);
                return myBitmap;
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
        public void button2(View view) {
            new DownloadImageFromTherad().execute();
        }
        private class DownloadImageFromTherad extends AsyncTask<String, Integer, String> {
            @Override
            protected String doInBackground(String... params) {
                bitmap = getBitmapFromURL("https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png");
                return null;
            }

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                File sdCardDirectory = Environment.getExternalStorageDirectory();
                File image = new File(sdCardDirectory, "test.png");
                boolean success = false;
                FileOutputStream outStream;
                mSavedInfo = saveToInternalStorage(bitmap);
                if (success) {
                    Toast.makeText(getApplicationContext(), "Image saved with success", Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(getApplicationContext(), "Error during image saving" + mSavedInfo, Toast.LENGTH_LONG).show();
                }
            }
        }
        private String saveToInternalStorage(Bitmap bitmapImage) {
            ContextWrapper cw = new ContextWrapper(getApplicationContext());
            // path to /data/data/yourapp/app_data/imageDir
            File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
            File mypath = new File(directory, "profile.jpg");
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(mypath);
                bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return directory.getAbsolutePath();
        }
        private void loadImageFromStorage(String path) {
            try {
                File f = new File(path, "profile.jpg");
                Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));
                mImageView1.setImageBitmap(b);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        public void showImage(View view) {
            loadImageFromStorage(mSavedInfo);
        }
    }
Syed Danish Haider
fuente
1

Hay una subclase de RecyclerView llamada HorizontalGridView que puede usar para tener una dirección horizontal. VerticalGridView para dirección vertical

Andrew Luca
fuente
55
¿HorizontalGridView está destinado a ser utilizado para dispositivos que no son de TV? Afaik the leanback library está destinado a televisores
AgentKnopf
2
el uso de Leanback aumentará la minSdkVersion de su aplicación a 17
Someone Somewhere
1

Es tanto para horizontal como para vertical.

RecyclerView recyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_recycler);
    recyclerView = (RecyclerView)findViewById(R.id.recyclerViewId);

    RecyclAdapter adapter = new RecyclAdapter();

    //Vertical RecyclerView
    RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
    recyclerView.setLayoutManager(mLayoutManager);

    //Horizontal RecyclerView
    //recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext(),LinearLayoutManager.HORIZONTAL,false));

    recyclerView.setAdapter(adapter);

}
Akbar Khan
fuente
1

Vista del reciclador en horizontal dinámico.

Implementación de vista de reciclador

RecyclerView musicList = findViewById(R.id.MusicList);

// RecyclerView musiclist = findViewById(R.id.MusicList1);
// RecyclerView musicLIST = findViewById(R.id.MusicList2);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
musicList.setLayoutManager(layoutManager);

String[] names = {"RAP", "CH SHB", "Faheem", "Anum", "Shoaib", "Laiba", "Zoki", "Komal", "Sultan","Mansoob Gull"};
musicList.setAdapter(new ProgrammingAdapter(names));'

Clase de adaptador para la vista de reciclador, en la que hay un soporte de vista para mantener la vista de ese reciclador

public class ProgrammingAdapter 
     extendsRecyclerView.Adapter<ProgrammingAdapter.programmingViewHolder> {

private String[] data;

public ProgrammingAdapter(String[] data)
{
    this.data = data;
}

@Override
public programmingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View view = inflater.inflate(R.layout.list_item_layout, parent, false);

    return new programmingViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull programmingViewHolder holder, int position) {
    String title = data[position];
    holder.textV.setText(title);
}

@Override
public int getItemCount() {
    return data.length;
}

public class programmingViewHolder extends RecyclerView.ViewHolder{
    ImageView img;
    TextView textV;
    public programmingViewHolder(View itemView) {
        super(itemView);
        img =  itemView.findViewById(R.id.img);
        textV =  itemView.findViewById(R.id.textt);
    }
}
Faheem Khan
fuente
1
recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));

recyclerView.setAdapter(adapter);
Hamza Ali
fuente
0

Prueba esto:

myrecyclerview.setLayoutManager(
        new LinearLayoutManager(getActivity(),
                                LinearLayoutManager.HORIZONTAL,false));
myrecyclerview.setAdapter(recyclerAdapter);

solo en caso de que tenga una vista de reciclador con algunos fragmentos.

John Vergara
fuente