Android RecyclerView و Android CardView تمت إضافتهما في Android Lollipop مع Material Design. بالنسبة لأولئك الذين لا يعرفون ما هو Material Design، فإنه دليل شامل لعناصر واجهة المستخدم التي تم تقديمها منذ Android 5.0، ويعمل على تحسين جاذبية التطبيقات بصرياً.
Android RecyclerView
Android RecyclerView هو إصدار متقدم وقوي ومرن لل ListView. يشبه Android RecyclerView ListView باستثناء أنه يجبرنا على استخدام فئة RecyclerView.ViewHolder لحمل العناصر وهو ليس إجباريًا في ListView. كما يوحي الاسم، يتم استخدام Android RecyclerView لإعادة استخدام الخلايا عند التمرير لأعلى ولأسفل من خلال إعادة استخدام العناصر في القائمة. تحسين آخر في RecyclerView هو أنه يسمح لنا بتعيين LayoutManagers بشكل ديناميكي أثناء التشغيل، على عكس ListView الذي كان متاحًا فقط في قائمة التمرير العمودية. يسمح RecyclerView لنا بتعيين أنواع الشرائح التالية أثناء التشغيل.
- LinearLayoutManager: يدعم كل من القوائم العمودية والأفقية
- StaggeredLayoutManager : يدعم قوائم متقطعة
- GridLayoutManager : يدعم عرض الشبكات كما هو موضح في GalleryView سابقًا
فئات Android RecyclerView
- فئة RecyclerView.ItemAnimator توفر دعمًا أفضل لتحريك العروض بشكلٍ أفضل من ListViews
- فئة RecyclerView.ItemDecorator توفر دعمًا أفضل عندما يتعلق الأمر بإضافة حدود وفواصل مما يعطي لنا تحكمًا هائلاً
لذلك ، فإن RecyclerView أكثر قابلية للتخصيص بالمقارنة مع ListView وتمنح المستخدمين تحكمًا أكبر. يتوفر RecyclerView في مكتبة الدعم. لذا علينا تعديل نص الجرادل لإضافة التبعيات التالية.
dependencies {
compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
}
Android CardView
تظهر مكون واجهة المستخدم لـ Android CardView المعلومات داخل بطاقات. يتم استخدام هذا المكون عادةً لعرض معلومات الاتصال. هذا المكون متوفر في مكتبة دعم أخرى لذا علينا إضافة تبعيتها أيضًا.
dependencies {
compile 'com.android.support:cardview-v7:21.0.0-rc1'
compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
}
تُسَمِح واجهة البرمجة لـ Android CardView بالتحكم في لون الخلفية، الظل، نصف قطر الزاوية، والارتفاع، وما إلى ذلك. لاستخدام السمات المخصصة في XML، نحتاج إلى إضافة الإعلان التالي لمساحة الأسماء إلى تخطيط الوالدين. الإعلان التالي يحتوي على بعض السمات من مشروعنا.
<android.support.v7.widget.CardView
android:id="@+id/card_view"
xmlns:card_view="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="@color/grey_300"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="true">
السمات المهمة المستخدمة أعلاه هي:
- card_view:cardCornerRadius: يُستخدم لتعيين نصف قطر الزاوية في تخطيطاتنا
- card_view:cardBackgroundColor: يُستخدم لتعيين لون خلفية العرض
في مشروعنا التوضيحي، سنُضيف RecyclerView لعرض قائمة من CardViews تحتوي على أسماء وأرقام إصدارات Android مع شعار عينة. تم برمجة تفعيل CardView onclick
لإزالة تلك البطاقة من القائمة. لقد أضفنا خيارًا في ActionBar لإضافة البطاقات المزالة مرة أخرى بالترتيب. ملاحظة: تم اختيار صور الشعار بشكل عشوائي من Google، لذا الأحجام قد تتنوع.
مثال على Android RecyclerView وCardView
تتكون المشروع من فئة
MainActivity
التي تعرض RecyclerView
. يتم إضافة CardView إلى RecyclerView من فئة CustomAdapter. يتم استخدام DataModel لاسترداد البيانات لكل CardView من خلال getters. تحتفظ فئة MyData بمصفوفات النصوص والرسومات بالإضافة إلى هويتها.
مثال على رمز Android RecyclerView و CardView
تحتوي activity_main.xml
على RecyclerView داخل RelativeLayout كما هو موضح أدناه. كود activity_main.xml:
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:background="@color/grey_300"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
/>
</RelativeLayout>
تم تعريف تخطيط Android CardView أدناه: كود cards_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:tag="cards main container">
<android.support.v7.widget.CardView
android:id="@+id/card_view"
xmlns:card_view="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="@color/color_white"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<ImageView
android:id="@+id/imageView"
android:tag="image_tag"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:src="@drawable/ic_launcher"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_weight="2"
android:orientation="vertical"
>
<TextView
android:id="@+id/textViewName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="Android Name"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<TextView
android:id="@+id/textViewVersion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="Android Version"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
يحتوي Android CardView على ImageView مع TextViews اثنين في تخطيط خطي متداخل. تحتوي menu_main.xml
على عنصر واحد لإضافة البطاقات المزالة. كود menu_main.xml:
<menu 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"
tools:context=".MainActivity">
<item android:id="@+id/add_item"
android:title="Add"
android:orderInCategory="100"
app:showAsAction="always"/>
</menu>
تم تعريف فئة MainActivity.java
كما هو موضح أدناه:
package com.journaldev.recyclerviewcardview;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
private static RecyclerView recyclerView;
private static ArrayList data;
static View.OnClickListener myOnClickListener;
private static ArrayList removedItems;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myOnClickListener = new MyOnClickListener(this);
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
data = new ArrayList();
for (int i = 0; i < MyData.nameArray.length; i++) {
data.add(new DataModel(
MyData.nameArray[i],
MyData.versionArray[i],
MyData.id_[i],
MyData.drawableArray[i]
));
}
removedItems = new ArrayList();
adapter = new CustomAdapter(data);
recyclerView.setAdapter(adapter);
}
private static class MyOnClickListener implements View.OnClickListener {
private final Context context;
private MyOnClickListener(Context context) {
this.context = context;
}
@Override
public void onClick(View v) {
removeItem(v);
}
private void removeItem(View v) {
int selectedItemPosition = recyclerView.getChildPosition(v);
RecyclerView.ViewHolder viewHolder
= recyclerView.findViewHolderForPosition(selectedItemPosition);
TextView textViewName
= (TextView) viewHolder.itemView.findViewById(R.id.textViewName);
String selectedName = (String) textViewName.getText();
int selectedItemId = -1;
for (int i = 0; i < MyData.nameArray.length; i++) {
if (selectedName.equals(MyData.nameArray[i])) {
selectedItemId = MyData.id_[i];
}
}
removedItems.add(selectedItemId);
data.remove(selectedItemPosition);
adapter.notifyItemRemoved(selectedItemPosition);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
if (item.getItemId() == R.id.add_item) {
// التحقق مما إذا كان هناك عناصر لإضافتها
if (removedItems.size() != 0) {
addRemovedItemToList();
} else {
Toast.makeText(this, "Nothing to add", Toast.LENGTH_SHORT).show();
}
}
return true;
}
private void addRemovedItemToList() {
int addItemAtListPosition = 3;
data.add(addItemAtListPosition, new DataModel(
MyData.nameArray[removedItems.get(0)],
MyData.versionArray[removedItems.get(0)],
MyData.id_[removedItems.get(0)],
MyData.drawableArray[removedItems.get(0)]
));
adapter.notifyItemInserted(addItemAtListPosition);
removedItems.remove(0);
}
}
تُستدعى طريقة `removeItems()` من طريقة المستمع لإزالة CardView التي تم النقر عليها. يتم تخزين معرفها المقابل في مصفوفة للاسترجاع لاحقًا. لإضافة العرض لاحقًا، قمنا بتنفيذ طريقة أخرى تُسمى `addRemovedItemToList()`، حيث نُضيف هذا العرض في موضع محدد مسبقًا في القائمة ونقوم بإزالة معرفه من مصفوفة `removedItems`. يتم إعلام CustomAdapter في كلتا الحالتين. يتم تعريف فئة CustomeAdapter.java أدناه :
package com.journaldev.recyclerviewcardview;
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;
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {
private ArrayList<DataModel> dataSet;
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textViewName;
TextView textViewVersion;
ImageView imageViewIcon;
public MyViewHolder(View itemView) {
super(itemView);
this.textViewName = (TextView) itemView.findViewById(R.id.textViewName);
this.textViewVersion = (TextView) itemView.findViewById(R.id.textViewVersion);
this.imageViewIcon = (ImageView) itemView.findViewById(R.id.imageView);
}
}
public CustomAdapter(ArrayList<DataModel> data) {
this.dataSet = data;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cards_layout, parent, false);
view.setOnClickListener(MainActivity.myOnClickListener);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int listPosition) {
TextView textViewName = holder.textViewName;
TextView textViewVersion = holder.textViewVersion;
ImageView imageView = holder.imageViewIcon;
textViewName.setText(dataSet.get(listPosition).getName());
textViewVersion.setText(dataSet.get(listPosition).getVersion());
imageView.setImageResource(dataSet.get(listPosition).getImage());
}
@Override
public int getItemCount() {
return dataSet.size();
}
}
في الشفرة أعلاه، قمنا بتنفيذ ViewHolder الخاص بنا عن طريق تمديد `RecyclerView.ViewHolder`. يتم نفخ العرض من `cards_layout.xml` الذي قمنا بتعريفه في دليل الشرائط. يتم إرفاق OnClickListener في MainActivity بهذا العرض في المقطع أدناه.
view.setOnClickListener(MainActivity.myOnClickListener);
تخزن ArrayList جميع البيانات على شكل كائن فئة DataModel في ArrayList وتضيفها إلى البطاقات المعنية في القائمة. تعريف فئة DataModel.java وفئة MyData.java التي تحتوي على البيانات المحددة لهذا التطبيق موضحة أدناه :
package com.journaldev.recyclerviewcardview;
public class DataModel {
String name;
String version;
int id_;
int image;
public DataModel(String name, String version, int id_, int image) {
this.name = name;
this.version = version;
this.id_ = id_;
this.image=image;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
public int getImage() {
return image;
}
public int getId() {
return id_;
}
}
package com.journaldev.recyclerviewcardview;
public class MyData {
static String[] nameArray = {"Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich","JellyBean", "Kitkat", "Lollipop", "Marshmallow"};
static String[] versionArray = {"1.5", "1.6", "2.0-2.1", "2.2-2.2.3", "2.3-2.3.7", "3.0-3.2.6", "4.0-4.0.4", "4.1-4.3.1", "4.4-4.4.4", "5.0-5.1.1","6.0-6.0.1"};
static Integer[] drawableArray = {R.drawable.cupcake, R.drawable.donut, R.drawable.eclair,
R.drawable.froyo, R.drawable.gingerbread, R.drawable.honeycomb, R.drawable.ics,
R.drawable.jellybean, R.drawable.kitkat, R.drawable.lollipop,R.drawable.marsh};
static Integer[] id_ = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
}
أدناه هو الإخراج الذي أنتجه تطبيقنا مثال RecyclerView و CardView على نظام Android. كما يمكنك رؤية أن العنصر المزال دائمًا يتم إضافته في المؤشر الثالث (المركز الرابع في القائمة). هذا يضع نهاية لهذا البرنامج التعليمي حول RecyclerView و CardView على نظام Android. يمكنك تنزيل مشروع مثال Android RecyclerView CardView من الرابط أدناه.