حتى الآن، قمنا بعرض نفس نوع العروض داخل RecyclerView. في هذا الدرس، سنقوم بتنفيذ تخطيطات متنوعة داخل RecyclerView.
RecyclerView
يُستخدم RecyclerView بتخطيطات متنوعة عادةً لعرض رؤوس الأقسام والتفاصيل (كل منهما يتطلب تخطيطًا مختلفًا، وبالتالي نوع عرض مختلف). كما يُستخدم في تطبيق Newsfeed (مثل Facebook و Instagram) الذي يعرض آراءً مختلفة تمامًا لأنواع مختلفة. مثال: نص، صورة، صورة متحركة، فيديو، إلخ. كل من هذه يتطلب نوعًا مختلفًا من التخطيط داخل RecyclerView. كما يُستخدم في NavigationDrawer لفصل الرأس عن باقي القسم. دون إضاعة الوقت، دعونا نقوم بتنفيذه في تطبيقنا.
بنية مشروع Android RecyclerView بعدة أنواع لعرض العناصر
سنقوم بتنفيذ ثلاثة أنواع من العروض (نص، صورة، صوت) يتم نفخها بواسطة ثلاثة تخطيطات مختلفة. لكل منها تنفيذ خاص في فئة المحول.
الكود
يحتوي 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.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>
ألاحظوا على السطر app:layout_behavior="@string/appbar_scrolling_view_behavior"
داخل RecyclerView. سيؤدي إزالته إلى تمرير RecyclerView على الشاشة بأكملها مما يتسبب في تداخله مع AppBarLayout. فئة 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
على ثابت نوع العرض. - تحتوي
String text
على السلسلة التي سيتم عرضها في TextView. - المتغير
int data
يُستخدم لتخزين البيانات الخاصة التي سنقوم بملؤها Ideal يحتوي على موارد نوع رسم أو نوع خام.
تحتوي فئة MainActivity.java على
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);
}
}
الملف R.raw.sound هو ملف sound.mp3 الذي سيتم تشغيله في نوع العرض الصوتي. تحتوي فئة المحول لـ RecyclerView على ثلاثة طرق رئيسية يجب تجاوزها.
getItemViewType()
onCreateViewHolder()
onBindViewHolder()
سنستخدم تعليمات التبديل في طريقة getItemViewType() لإرجاع نوع العرض المناسب. المتغير viewType
هذا داخلي لفئة المحول. يتم استخدامه في onCreateViewHolder() وonBindViewHolder لتضخيم وملء التخطيطات المعينة. قبل أن ننتقل إلى تنفيذ فئة المحول، دعونا نلقي نظرة على أنواع التخطيطات التي تم تعريفها لكل نوع من أنواع العرض. 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>
ملاحظة: أضف الاعتمادية التالية لـ CardView في ملف build.gradle
compile 'com.android.support:cardview-v7:24.2.0'
تأكد من أن رقم الإصدار للاعتمادية appcompat يتطابق مع تلك للcardview. (24.2.0 حالياً بالنسبة لي. قد يكون مختلفًا بالنسبة لك.) سنقوم بإنشاء ثلاث فئات مستقلة لعناصر ViewHolder لكل من التخطيطات المذكورة أعلاه كما هو موضح في فئة MultiViewTypeAdapter.java أدناه.
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();
}
}
في الشفرة أعلاه، نحتفظ بمتغير بولياني عام لتخزين حالة زر الصوت المفعلة عند كل نقرة (بالإضافة إلى تغيير مصدر الصورة لـ FloatingActionButton). يتم توفير نتيجة التطبيق أعلاه. هذا يضع نهاية لهذا البرنامج التعليمي. يمكنك تنزيل مشروع Android النهائي لـ RecyclerViewMultipleViewType من الرابط أدناه.
Source:
https://www.digitalocean.com/community/tutorials/android-recyclerview-example