Android RecyclerView示例–多个ViewType
到目前为止,我们在RecyclerView中显示了相同类型的视图。
在本教程中,我们将在RecyclerView中实现异构布局。
回收站视图
具有异构布局的RecyclerView通常用于显示节标题和详细信息(两者都需要不同的布局,因此需要不同的视图类型)。
此外,它还用于Newsfeed应用程序(例如Facebook,Instagram)中,该应用程序针对不同类型显示本质上不同的视图。
例如:文本,图像,gif,视频等。
每一个在RecyclerView中都需要不同的布局类型。
它在NavigationDrawer中也用于将页眉与本节的其余部分分开。
不浪费时间,让我们在应用程序中实现它。
Android RecyclerView多个ViewType项目结构
我们将实现三种视图类型(文本,图像,音频),这些视图类型将通过三种不同的布局进行放大。
每个适配器都有在适配器类中指定的自己的实现。
代码
我们的activity_main.xml包含CoordinatorLayout作为根目录,而RecyclerView充当其子视图。
<?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.theitroad.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>
请注意RecyclerView中的ʻapp:layout_behavior =" @ string/appbar_scrolling_view_behavior"行。
删除它会在整个屏幕上滚动RecyclerView,从而使其与AppBarLayout重叠。
下面给出了在Adapter中填充数据的Model.java类
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; } }
它由三种数据类型组成。
int type保持视图类型不变。
"字符串文本"包含将在TextView中显示的字符串。
" int data"变量用于存储我们要填充的各个数据。
理想情况下,它将包含可绘制或者原始类型的资源。
MainActivity.java类如下所示
package com.theitroad.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); } }
" R.raw.sound"是一个sound.mp3文件,将以"音频视图类型"播放。
RecyclerView的Adapter类包含三个需要重写的主要方法。
getItemViewType()
onCreateViewHolder()
- onBindViewHolder()
我们将在getItemViewType()方法中使用switch语句返回各自的viewType
。
这个viewType
变量是Adapter类的内部变量。
在onCreateViewHolder()和onBindViewHolder中使用它来填充和填充映射的布局。
在介绍Adapter类的实现之前,让我们看一下为每种视图类型定义的布局类型。
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>
注意:在build.gradle文件中为CardView添加以下依赖项
compile 'com.android.support:cardview-v7:24.2.0'
确保appcompat依赖项的版本号与cardview之一匹配。
(目前对我来说是24.2.0。
可能与您有所不同。
)
我们将为上述每个布局创建三个单独的ViewHolder类,如下面的MultiViewTypeAdapter.java类所示。
package com.theitroad.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(); } }
在上面的代码中,我们保留了一个全局布尔变量,用于存储每次单击时切换的音量按钮状态(以及更改FloatingActionButton的图像资源)。