במדריך זה, נשתמש ב־CustomAdapter שממלא את השורות המותאמות אישית של ה־Android ListView עם ArrayList
. גם כדי לשדרג את חוויית המשתמש, נוסיף אנימציה ל־ListView בעת גלילה.
סקירת Custom Adapter ל־Android ListView
האדפטר הפשוט ביותר למילוי תצוגה מ־ArrayList הוא ArrayAdapter
. זה מה שנממש במדריך זה. ישנם אדפטרים נוספים כמו CursorAdapter
שמקשר ישירות ל־result set מ־מסד נתונים SQLite מקומי והוא משתמש ב־Cursor כמקור נתונים.
מחזור יצירת שורות
כש־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>
במדריך זה נבנה אפליקציה שמורכבת מרשימת שורות המציגות תיאורי טקסט וסמל מידע. לחיצה על השורה תציג את ה-SnackBar עם אלמנטי הטקסט של אותה שורה. לחיצה על המידע תציג SnackBar עם מידע ספציפי לשורה הזו.
מבנה הפרויקט
קוד
אנו יוצרים 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;
}
}
ה-Adapter המותאם אישית שמאכלס את ה-DataModel לתוך ה-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 כאשר לוחצים עליו עם תיאור לשורה המתאימה. גם שורות הרשימה מנומסות בעת גלילה. קבצי משאבי האנימציה השניים נתונים למטה. 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
שבו ה-Adapter המותאם אישית מוגדר ל-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);
}
}
הפלט של היישום בפעולה מוצג למטה. מתקרבת סוף המדריך. תוכל להוריד את פרויקט המתאם המותאם אישית של ListView של Android הסופי מהקישור למטה.
הורדת פרויקט המתאם המותאם אישית של ListView של Android
התייחסות: מדריך API של ListView