في هذا البرنامج التعليمي، سنستخدم CustomAdapter الذي يملأ الصفوف المخصصة لـ Android ListView باستخدام ArrayList
. أيضًا لتحسين تجربة المستخدم، سنقوم بتحريك القائمة أثناء التمرير.
نظرة عامة على محول القائمة المخصصة لـ Android
أبسط محول لملء عرض من ArrayList
هو ArrayAdapter
. هذا ما سنقوم بتنفيذه في هذا البرنامج التعليمي. هناك محولات أخرى أيضًا، مثل CursorAdapter
الذي يرتبط مباشرة بنتيجة من قاعدة بيانات SQLite المحلية ويستخدم مؤشرًا كمصدر للبيانات.
إعادة استخدام الصفوف
عندما يتم إنشاء ListView ويتم ملء الصفوف بحيث يتم ملؤها بالكامل، لا تتم إنشاء عناصر صف جديدة في الذاكرة. عندما يتم التمرير عبر القائمة، يُحتفظ بالعناصر التي تترك الشاشة في الذاكرة للاستخدام في وقت لاحق، ومن ثم يُعيد كل صف جديد يدخل الشاشة استخدام صف أقدم محفوظ في الذاكرة.
إنشاء قالب عرض
لنقم بإنشاء تخطيط XML يقدم العناصر في صف بطريقة مخصصة. row_item.xml
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="Marshmallow"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/name"
android:layout_marginTop="5dp"
android:text="Android 6.0"
android:textColor="@android:color/black" />
<ImageView
android:id="@+id/item_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@android:drawable/ic_dialog_info" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<TextView
android:id="@+id/version_heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="API: "
android:textColor="@android:color/black"
android:textStyle="bold" />
<TextView
android:id="@+id/version_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="23"
android:textAppearance="?android:attr/textAppearanceButton"
android:textColor="@android:color/black"
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout>
في هذا البرنامج التعليمي ، سنقوم ببناء تطبيق يتكون من قائمة من الصفوف التي تعرض وصفوف نصية وأيقونة معلومات. بالنقر فوق الصف ، ستعرض شريط الوميض بعناصر النص لهذا الصف. بالنقر فوق المعلومات ، سيتم عرض شريط الوميض مع معلومات محددة لهذا الصف.
هيكل المشروع
الكود
نقوم بإنشاء ListView مخصص باستخدام تركيبة ArrayAdapter مع DataModel ككائن. getView() هو الطريقة التي تعيد العرض الفعلي المستخدم كصف داخل ListView في موضع معين. يحتوي content_main.xml
على ListView كما هو موضح أدناه. content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
xmlns:app="https://schemas.android.com/apk/res-auto"
tools:context="com.journaldev.customlistview.MainActivity"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main">
<ListView
android:id="@+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
يتم عرض نموذج البيانات الذي يتم تضمينه في ArrayList أدناه. DataModel.java
public class DataModel {
String name;
String type;
String version_number;
String feature;
public DataModel(String name, String type, String version_number, String feature ) {
this.name=name;
this.type=type;
this.version_number=version_number;
this.feature=feature;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public String getVersion_number() {
return version_number;
}
public String getFeature() {
return feature;
}
}
المحول المخصص الذي يملأ نموذج البيانات في ListView موضح أدناه. CustomAdapter.java
public class CustomAdapter extends ArrayAdapter implements View.OnClickListener{
private ArrayList dataSet;
Context mContext;
// ذاكرة التخزين المؤقتة للعرض
private static class ViewHolder {
TextView txtName;
TextView txtType;
TextView txtVersion;
ImageView info;
}
public CustomAdapter(ArrayList data, Context context) {
super(context, R.layout.row_item, data);
this.dataSet = data;
this.mContext=context;
}
@Override
public void onClick(View v) {
int position=(Integer) v.getTag();
Object object= getItem(position);
DataModel dataModel=(DataModel)object;
switch (v.getId())
{
case R.id.item_info:
Snackbar.make(v, "Release date " +dataModel.getFeature(), Snackbar.LENGTH_LONG)
.setAction("No action", null).show();
break;
}
}
private int lastPosition = -1;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// الحصول على عنصر البيانات لهذا الموضع
DataModel dataModel = getItem(position);
// التحقق مما إذا كان هناك عرض موجود يُعاد استخدامه، وإلا فنفخ العرض
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.row_item, parent, false);
viewHolder.txtName = (TextView) convertView.findViewById(R.id.name);
viewHolder.txtType = (TextView) convertView.findViewById(R.id.type);
viewHolder.txtVersion = (TextView) convertView.findViewById(R.id.version_number);
viewHolder.info = (ImageView) convertView.findViewById(R.id.item_info);
result=convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
Animation animation = AnimationUtils.loadAnimation(mContext, (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
result.startAnimation(animation);
lastPosition = position;
viewHolder.txtName.setText(dataModel.getName());
viewHolder.txtType.setText(dataModel.getType());
viewHolder.txtVersion.setText(dataModel.getVersion_number());
viewHolder.info.setOnClickListener(this);
viewHolder.info.setTag(position);
// إرجاع العرض المكتمل للتقديم على الشاشة
return convertView;
}
}
في الكود أعلاه، قمنا بإضافة onClickListener
إلى ImageView الذي يعرض SnackBar عند النقر عليه مع وصف للصف الذي يتم النقر عليه. كما يتم تحريك صفوف القائمة عند التمرير. يتم تقديم ملفين مورد xml للتحريك أدناه. down_from_top.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="-100%" android:toYDelta="0%"
android:duration="400" />
</set>
up_from_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="100%" android:toYDelta="0%"
android:duration="400" />
</set>
يتم تعريف MainActivity.java
حيث يُعين المحول المخصص على ListView أدناه. بالإضافة إلى ذلك، يتم ملء ArrayList عشوائي من كائنات DataModel. MainActivity.java
public class MainActivity extends AppCompatActivity {
ArrayList dataModels;
ListView listView;
private static CustomAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
listView=(ListView)findViewById(R.id.list);
dataModels= new ArrayList<>();
dataModels.add(new DataModel("Apple Pie", "Android 1.0", "1","September 23, 2008"));
dataModels.add(new DataModel("Banana Bread", "Android 1.1", "2","February 9, 2009"));
dataModels.add(new DataModel("Cupcake", "Android 1.5", "3","April 27, 2009"));
dataModels.add(new DataModel("Donut","Android 1.6","4","September 15, 2009"));
dataModels.add(new DataModel("Eclair", "Android 2.0", "5","October 26, 2009"));
dataModels.add(new DataModel("Froyo", "Android 2.2", "8","May 20, 2010"));
dataModels.add(new DataModel("Gingerbread", "Android 2.3", "9","December 6, 2010"));
dataModels.add(new DataModel("Honeycomb","Android 3.0","11","February 22, 2011"));
dataModels.add(new DataModel("Ice Cream Sandwich", "Android 4.0", "14","October 18, 2011"));
dataModels.add(new DataModel("Jelly Bean", "Android 4.2", "16","July 9, 2012"));
dataModels.add(new DataModel("Kitkat", "Android 4.4", "19","October 31, 2013"));
dataModels.add(new DataModel("Lollipop","Android 5.0","21","November 12, 2014"));
dataModels.add(new DataModel("Marshmallow", "Android 6.0", "23","October 5, 2015"));
adapter= new CustomAdapter(dataModels,getApplicationContext());
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView > parent, View view, int position, long id) {
DataModel dataModel= dataModels.get(position);
Snackbar.make(view, dataModel.getName()+"\n"+dataModel.getType()+" API: "+dataModel.getVersion_number(), Snackbar.LENGTH_LONG)
.setAction("No action", null).show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// نفخ القائمة؛ هذا يضيف عناصر إلى شريط العمل إذا كان موجودًا
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// التعامل مع عناصر شريط العمل هنا. سيتم
// التعامل تلقائيًا مع نقرات على زر الصفحة الرئيسية/الرجوع، طالما
// أنك تحدد نشاطًا أصليًا في AndroidManifest.xml
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
الناتج المعروض لتطبيق في عمله يظهر أدناه. يأتي هذا مع نهاية هذا البرنامج التعليمي. يمكنك تحميل المشروع النهائي Android ListView Custom Adapter من الرابط أدناه.
تحميل مشروع Android ListView Custom Adapter
المرجع: API Guide List View