Android RecyclerView示例–多个ViewType

时间:2020-02-23 14:29:12  来源:igfitidea点击:

到目前为止,我们在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的图像资源)。