Finora abbiamo mostrato lo stesso tipo di visualizzazioni all’interno di un RecyclerView. In questo tutorial, implementeremo layout eterogenei all’interno di un RecyclerView.
RecyclerView
Un RecyclerView con layout eterogenei viene comunemente utilizzato per mostrare intestazioni di sezione e dettagli (entrambi richiedono layout diversi, quindi diversi tipi di visualizzazione). È anche utilizzato in un’applicazione di Newsfeed (come Facebook, Instagram) che mostra visualizzazioni essenzialmente diverse per tipi diversi. Esempio: testo, immagine, gif, video, ecc. Ognuno di questi richiede un tipo di layout diverso all’interno del RecyclerView. È anche utilizzato in un NavigationDrawer per separare l’intestazione dal resto della sezione. Senza perdere ulteriore tempo, implementiamolo nella nostra applicazione.
Struttura del progetto Android RecyclerView con più tipi di visualizzazione
Implementeremo tre tipi di visualizzazione (testo, immagine, audio) che vengono gonfiati da tre layout diversi. Ognuno ha la propria implementazione specificata nella classe dell’adattatore.
Codice
Il nostro activity_main.xml contiene CoordinatorLayout come radice e RecyclerView agisce come vista figlio.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.journaldev.recyclerviewmultipleviewtype.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>
Fai attenzione alla riga app:layout_behavior="@string/appbar_scrolling_view_behavior"
all’interno di RecyclerView. Rimuovendo questo, la RecyclerView scorrerà su tutto lo schermo sovrapponendosi con AppBarLayout. La classe Model.java che popola i dati nell’adattatore è riportata di seguito
public class Model {
public static final int TEXT_TYPE=0;
public static final int IMAGE_TYPE=1;
public static final int AUDIO_TYPE=2;
public int type;
public int data;
public String text;
public Model(int type, String text, int data)
{
this.type=type;
this.data=data;
this.text=text;
}
}
È composta da tre tipi di dati.
- Il
int type
contiene la costante del tipo di visualizzazione. - La variabile
String text
contiene la stringa che verrà visualizzata nel TextView. - La variabile
int data
viene utilizzata per memorizzare i dati relativi che popoleremo. Idealmente conterrà una risorsa di tipo drawable o raw.
La classe MainActivity.java è riportata di seguito
package com.journaldev.recyclerviewmultipleviewtype;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ArrayList<Model> list= new ArrayList();
list.add(new Model(Model.TEXT_TYPE,"Hello. This is the Text-only View Type. Nice to meet you",0));
list.add(new Model(Model.IMAGE_TYPE,"Hi. I display a cool image too besides the omnipresent TextView.",R.drawable.wtc));
list.add(new Model(Model.AUDIO_TYPE,"Hey. Pressing the FAB button will playback an audio file on loop.",R.raw.sound));
list.add(new Model(Model.IMAGE_TYPE,"Hi again. Another cool image here. Which one is better?",R.drawable.snow));
MultiViewTypeAdapter adapter = new MultiViewTypeAdapter(list,this);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, OrientationHelper.VERTICAL, false);
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(adapter);
}
}
Il R.raw.sound
è un file sound.mp3 che verrà riprodotto nel tipo di visualizzazione audio. La classe Adapter per RecyclerView contiene tre metodi principali che devono essere sovrascritti.
getItemViewType()
onCreateViewHolder()
onBindViewHolder()
Utilizzeremo le istruzioni switch nel metodo getItemViewType() per restituire il rispettivo viewType
. Questa variabile viewType
è interna alla classe Adapter. Viene utilizzata in onCreateViewHolder() e onBindViewHolder() per gonfiare e popolare i layout mappati. Prima di passare all’implementazione della classe Adapter, diamo un’occhiata ai tipi di layout definiti per ogni tipo di vista. text_type.xml
<android.support.v7.widget.CardView xmlns:card_view="https://schemas.android.com/apk/res-auto"
xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_horizontal_margin"
card_view:cardElevation="10dp">
<TextView
android:id="@+id/type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
/>
</android.support.v7.widget.CardView>
image_type.xml
<android.support.v7.widget.CardView xmlns:card_view="https://schemas.android.com/apk/res-auto"
xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_horizontal_margin"
card_view:cardElevation="10dp">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView
android:id="@+id/type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
/>
<ImageView
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop"
android:src="@drawable/snow"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
audio_type.xml
<android.support.v7.widget.CardView xmlns:card_view="https://schemas.android.com/apk/res-auto"
xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_horizontal_margin"
card_view:cardElevation="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
/>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:tint="#FFFFFF"
android:id="@+id/fab"
android:layout_below="@+id/type"
android:layout_margin="@dimen/activity_horizontal_margin"
android:src="@drawable/volume"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
Nota: Aggiungi la seguente dipendenza per CardView nel file build.gradle
compile 'com.android.support:cardview-v7:24.2.0'
Assicurati che il numero di versione della dipendenza appcompat corrisponda a quello di cardview. (Per me è attualmente 24.2.0, potrebbe essere diverso per te.) Creeremo tre classi ViewHolder separate per ciascuno dei layout sopra citati, come mostrato nella classe MultiViewTypeAdapter.java di seguito.
package com.journaldev.recyclerviewmultipleviewtype;
import android.content.Context;
import android.media.MediaPlayer;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
/**
* Created by anupamchugh on 09/02/16.
*/
public class MultiViewTypeAdapter extends RecyclerView.Adapter {
private ArrayList<Model>dataSet;
Context mContext;
int total_types;
MediaPlayer mPlayer;
private boolean fabStateVolume = false;
public static class TextTypeViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
CardView cardView;
public TextTypeViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.cardView = (CardView) itemView.findViewById(R.id.card_view);
}
}
public static class ImageTypeViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
ImageView image;
public ImageTypeViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.image = (ImageView) itemView.findViewById(R.id.background);
}
}
public static class AudioTypeViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
FloatingActionButton fab;
public AudioTypeViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.fab = (FloatingActionButton) itemView.findViewById(R.id.fab);
}
}
public MultiViewTypeAdapter(ArrayList<Model>data, Context context) {
this.dataSet = data;
this.mContext = context;
total_types = dataSet.size();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case Model.TEXT_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_type, parent, false);
return new TextTypeViewHolder(view);
case Model.IMAGE_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_type, parent, false);
return new ImageTypeViewHolder(view);
case Model.AUDIO_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.audio_type, parent, false);
return new AudioTypeViewHolder(view);
}
return null;
}
@Override
public int getItemViewType(int position) {
switch (dataSet.get(position).type) {
case 0:
return Model.TEXT_TYPE;
case 1:
return Model.IMAGE_TYPE;
case 2:
return Model.AUDIO_TYPE;
default:
return -1;
}
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int listPosition) {
Model object = dataSet.get(listPosition);
if (object != null) {
switch (object.type) {
case Model.TEXT_TYPE:
((TextTypeViewHolder) holder).txtType.setText(object.text);
break;
case Model.IMAGE_TYPE:
((ImageTypeViewHolder) holder).txtType.setText(object.text);
((ImageTypeViewHolder) holder).image.setImageResource(object.data);
break;
case Model.AUDIO_TYPE:
((AudioTypeViewHolder) holder).txtType.setText(object.text);
((AudioTypeViewHolder) holder).fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (fabStateVolume) {
if (mPlayer.isPlaying()) {
mPlayer.stop();
}
((AudioTypeViewHolder) holder).fab.setImageResource(R.drawable.volume);
fabStateVolume = false;
} else {
mPlayer = MediaPlayer.create(mContext, R.raw.sound);
mPlayer.setLooping(true);
mPlayer.start();
((AudioTypeViewHolder) holder).fab.setImageResource(R.drawable.mute);
fabStateVolume = true;
}
}
});
break;
}
}
}
@Override
public int getItemCount() {
return dataSet.size();
}
}
Nel codice sopra, stiamo mantenendo una variabile booleana globale per memorizzare lo stato del pulsante del volume che viene invertito ad ogni clic (insieme alla modifica della risorsa dell’immagine per il FloatingActionButton). Di seguito è riportato l’output dell’applicazione sopra. Questo pone fine a questo tutorial. È possibile scaricare il progetto finale Android RecyclerViewMultipleViewType dal link sottostante.
Source:
https://www.digitalocean.com/community/tutorials/android-recyclerview-example