مشخصات مقاله
-
2325
-
0.0
-
7040
-
0
-
0
استفاده از لیست در اندروید
مقاله ی حاضر به شما آموزش می دهد چگونه ListView را با activity و fragment در اندروید بکار ببرید.
آموزش استفاده از کلاس ListView در اندروید
نمایش مجموعه ای سازماندهی شده از المان ها در اندروید یک الگوی بسیار معمول در اپلیکیشن های تحت موبایل است. کاربر رو به روی خود لیستی از آیتم ها را می بیند و می تواند به راحتی داخل آن پیمایش کند. چنین activity در تصویر زیر به نمایش گذاشته شده است.
به طور معمول، کاربر از طریق نوار ابزار با لیست تعامل بر قرار می کند. به طور مثال، یک دکمه در نوار ابزار که لیست را بروز رسانی نموده و آیتم هایی را از آن حذف یا به آن اضافه می کند. آیتم های فردی لیست را می توان انتخاب نمود که به دنبال این انتخاب نوار ابزار بروز آوری شده یا ممکن است یک صفحه ی جدید با جزئیات فراوان به نمایش در آید. در زیر می بینید که با انتخاب یک آیتم از لیست، activity دیگری راه اندازی شده و برای کاربر به نمایش در می آید.
آموزش اندروید و widget/المان رابط کاربری ListView
آموزش View هایی برای مدیریت لیست ها
چارچوب نرم افزاری (framework) اندروید برای پیاده سازی لیست های ساده، دو کلاس ListView و ExpandableListView را ارائه می دهد. با بهره گیری از این دو کلاس، برنامه نویس قادر خواهد بود به راحتی لیست های قابل پیمایش با نوار اسکرول را پیاده سازی کند.
کلاس ExpandableListView علاوه بر سازمان دهی المان ها، آن ها را در دو سطح یا گروه متمایز قرار می دهد، به طوری که کاربر می تواند با کلیک بر روی هر یک، المان های فرزند آن دو را به صورت جداگانه مشاهده کند.
آموزش نوع داده ای آیتم های لیست
آیتم هایی که در لیست به نمایش در می آیند، می توانند یک آبجکت جاوا از هر نوعی باشد. کلاس adapter داده های مربوطه را از data object (کلاسی که داده ها در آن کپسوله شده اند) استخراج کرده و داده های واکشی شده را به view های متناظر در ListView تخصیص می دهد.
این آیتم ها به طور معمول همان data model لیست خوانده می شوند. کلاس adapter این داده ها را به عنوان ورودی دریافت می کند. در حقیقت adapter یک پل ارتباطی است که داده ها را از منبع داده (مثلا آرایه) خوانده و آن ها را به Adapter view تحویل می دهد. این داده ها نهایتا در اختیار کامپوننت های UI قرار داده شده و در نمایشگر ارائه می شوند.
آموزش Adapter ها
adapter در واقع data model اپلیکیشن را مدیریت می کند و آن را با توجه به المان های فردی در widget بروز رسانی کرده و تطبیق می دهد. کلاس adapter خود از کلاس BaseAdapter مشتق می شود.
هر خط که در widget یا المان رابط کاربری اطلاعاتی را نشان می دهد، خود از یک layout تشکیل شده و می تواند بر اساس نیاز پیچیده یا ساده باشد. ظاهر کلی سطرها در لیست اغلب به این صورت است که در سمت چپ یک عکس قابل مشاهده است و در وسط دو خط متن مانند تصویر زیر را شامل می شود.
فایل layout متناظر این سطر از لیست می تواند به صورت زیر باشد.
کلاس adapter به ازای هر سطر در لیست، layout مربوطه را inflate می کند (آن را در حافظه بارگذاری و آماده ی نمایش در UI می نماید). در واقع با استفاده از متد getView() هر layout را واکشی و سپس داده های آن را به view های مرتبط در سطر میزبان متصل می کند.
Adapter از طریق متد setAdapter که به آبجکت ListView الحاق شده، به ListView متصل می شود.
این تنها ListView نیست که از Adapter استفاده می کند، بلکه سایر view هایی که از Adapterview ارث بری دارند، نظیر Spinner، Gallery و StackView نیز برای واکشی داده های خود با این کلاس همکاری دارند.
آموزش فیلتر و مرتب سازی داده ها
فیلتر کردن و مرتب سازی داده ها بر عهده ی adapter می باشد. منطق مربوطه را می بایست در بدنه ی adapter اختصاصی خود (که از کلاس پدر ارث بری کردید) پیاده سازی نمایید.
آموزش بروز رسانی داده ها در adapter
متد notifyDataSetChanged() در کلاس adapter زمانی فراخوانی می شود که داده ها تغییر کرده باشند یا داده های جدید در دسترس قرار گرفته باشد.
متد notifyDataSetInvalidated() زمانی صدا خورده می شود که داده ها دیگر در دسترس نباشند.
آموزش تعریف listener برای آگاهی از تغییرات
برای واکنش نشان دادن به تغییراتی که در لیست رخ می دهد (برای مثال انتخابی که کاربر می کند)، لازم است یک OnItemClickListener به ListView خود الحاق کنید.
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(),
"Click ListItem Number " + position, Toast.LENGTH_LONG)
.show();
}
});
آموزش Adapter پیش فرض
آموزش کلاس های adapter پیش فرض محیط (platform) اندروید
اندروید تعدادی adapter با پیاده سازی پیش فرض ارائه می دهد که مهم ترین و پرکاربردترین آن ها به شرح زیر می باشد: ArrayAdapter و CursorAdapter.ArrayAdapter که زمانی استفاده می شود که منبع داده ای آرایه باشد و دیگری java.util.List. SimpleCursorAdapter که می تواند به هنگام کار با داده های مستقر در دیتابیس مورد استفاده قرار گیرد (منبع داده ای یک cursor برای ردیف های دیتابیس باشد).
آموزش استفاده از ArrayAdapter
کلاس ArrayAdapter می تواند یک لیست یا آرایه ای از آبجکت های جاوا را به عنوان ورودی دریافت کند. هر آبجکت جاوایی به یک سطر نگاشت می شود. در واقع متد toString() را به ازای هر المان در آرایه صدا می زند و یک شی view می سازد و آن را در TextView قرار می دهد.
شما می توانید ID یا شناسه ی منحصربفرد view را در تابع سازنده (Constructor) کلاس ArrayAdapter تعریف کنید. در غیر این صورت شناسه ی android.R.id.text1 به صورت پیش فرض مورد استفاده قرار می گیرد.
کلاس ArrayAdapter به شما این امکان را می دهد تمامی المان های موجود در منبع داده (data structure) را با فراخوانی متدclear() حذف نمایید. سپس شما می توانید به وسیله ی متد add() المان های جدید و به وسیله ی addAll() یک Collection جدید اضافه نمایید.
این امکان نیز برای توسعه دهنده وجود دارد که محتوای منبع داده (برای مثال آرایه) را ویرایش نموده و سپس با فراخوانی متد notifyDataSetChanged() در adapter، این آبجکت را از تغییرات در داده ها آگاه نمایید.
اگر می خواهید داده ها را در adapter خود تغییر دهید، در آن صورت منبع داده ی اصلی (underlying data structure) نیز باید این عملیات را پشتیبانی کند. برای مثال، کلاس ArrayList این امکان را به شما می دهد ولی این امر درمورد آرایه صادق نیست.
آموزش نمونه ی استفاده از ListView با ArrayAdapter
کد XML زیر، یک قطعه از فایل layout به نام activity_listviewexampleactivity.xml را به نمایش می گذارد که کامپوننت رابط کاربری ListView را شامل می شود.
مثال زیر کاربرد آبجکت ListView در یک activity را به نمایش می گذارد. این مثال برای چیدمان و تنظیم ظاهر سطرها (row layout) از یک layout پیش فرض و آماده ی محیط اندروید (platform) بهره می گیرد. علاوه بر آن مثال حاضر حذف آیتم ها از لیست را همراه با انیمیشن به نمایش می گذارد.
package com.vogella.android.listview.withanimation;
public class ListViewExampleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listviewexampleactivity);
final ListView listview = (ListView) findViewById(R.id.listview);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux",
"OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
"Android", "iPhone", "WindowsMobile" };
final ArrayList list = new ArrayList();
for (int i = 0; i < values.length; ++i) {
list.add(values[i]);
}
final StableArrayAdapter adapter = new StableArrayAdapter(this,
android.R.layout.simple_list_item_1, list);
listview.setAdapter(adapter);
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, final View view,
int position, long id) {
final String item = (String) parent.getItemAtPosition(position);
view.animate().setDuration(2000).alpha(0)
.withEndAction(new Runnable() {
@Override
public void run() {
list.remove(item);
adapter.notifyDataSetChanged();
view.setAlpha(1);
}
});
}
});
}
private class StableArrayAdapter extends ArrayAdapter {
HashMap mIdMap = new HashMap();
public StableArrayAdapter(Context context, int textViewResourceId,
List objects) {
super(context, textViewResourceId, objects);
for (int i = 0; i < objects.size(); ++i) {
mIdMap.put(objects.get(i), i);
}
}
@Override
public long getItemId(int position) {
String item = getItem(position);
return mIdMap.get(item);
}
@Override
public boolean hasStableIds() {
return true;
}
}
}
آموزش پیاده سازی adapter های اختصاصی
آموزش ساخت یک adapter اختصاصی
برای مدیریت تخصیص داده و پشتیبانی از چندین view، می بایست خود یک کلاس adapter با پیاده سازی اختصاصی تعریف نمایید.
برای این منظور ابتدا لازم است یک کلاس پیاده سازی شده ی adapter را به ارث ببرید و یا به طور مستقیم از کلاس BaseAdapter یک کلاس فرزند ایجاد کرده و بدنه ی آن را خود پیاده سازی کنید.
اغلب توسعه دهنده از کلاس ArrayAdapter ارث بری کرده و یک adapter اختصاصی پیاده سازی می کند. این کار به مراتب ساده تر از ایجاد کلاس فرزند و ارث بری مستقیم از BaseAdapter است.
آموزش آماده سازی سطر از لیست
Adapter می بایست به ازای هر سطر از لیست، یک layout ایجاد کند. آبجکت (instance) ListView متد getView() را برای هر المان در لیست، در سطح کلاس Adater صدا می زند. به واسطه ی این متد adapter layout مربوط به سطر را ایجاد کرده و داده ها را به view های مربوطه در layout نگاشت می کند.
این root معمولا یک ViewGroup (یک layout manager) است که خود میزبان چندین view دیگر می باشد. به عنوان مثال می توان از ImageView و TextView نام برد. تصویر زیر یک لیست را با layout های متفاوت برای سطرهای زوج و فرد نشان می دهد.
متد getView() فایل XML را خوانده و آن را به listview تبدیل می کند.
حال با استفاده از متد getView() فایل layout مبتنی بر XML خود را بارگذاری یا inflate کرده و سپس محتوای تک تک view های سطر مورد نظر را بر اساس آبجکت جاوا تنظیم می کنید (آن ها را با داده هایی از آبجکت جاوا پر می کنید). به منظور inflate کردن فایل layout مبتنی بر XML خود، می توانید از سرویس LayoutInflator استفاده نمایید.
این سرویس که layout ها را خوانده و در حافظه بارگذاری می کند (inflate)، به راحتی به واسطه ی فراخوانی متد getLayoutInflator() در سطح کلاس activity و یا فراخوانی متد context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) به راحتی قابل دسترسی می باشد.
پس از اینکه adapter فایل layout مربوطه را در حافظه بارگذاری و به اصطلاح inflate کرد، view های متناظر در layout را پیدا می کند و متعاقبا آن ها را با داده پر می نماید. همان طور که قبلا نیز به آن اشاره شده، جهت پیدا کردن المان های فردی در layout، کافی است متد findViewById() را بر روی top level view (view میزبان که view مورد نظر در آن تعریف شده است) فراخوانی نمایید.
آموزش نمونه ای از پیاده سازی یک adapter اختصاصی و custom
کد زیر بدنه ی یک کلاس adapter با پیاده سازی اختصاصی را نشان می دهد. این آبجکت adapter فرض می گیرد شما دو فایل تصویری png (no.png و ok.png) در یکی از پوشه های directory]]res/drawable خود دارید. کد نوشته شده در این adapter، فایل XML layout را در حافظه بارگذاری (inflate) کرده، view های مربوطه را در layout پیدا می کند و سپس محتوای آن ها را بر اساس داده های ورودی مقداردهی و تنظیم می نماید.
package de.vogella.android.listactivity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class MySimpleArrayAdapter extends ArrayAdapter{ private final Context context; private final String[] values; public MySimpleArrayAdapter(Context context, String[] values) { super(context, -1, values); this.context = context; this.values = values; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rowView = inflater.inflate(R.layout.rowlayout, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.label); ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); textView.setText(values[position]); // change the icon for Windows and iPhone String s = values[position]; if (s.startsWith("iPhone")) { imageView.setImageResource(R.drawable.no); } else { imageView.setImageResource(R.drawable.ok); } return rowView; } }
آموزش بروز رسانی data model از طریق کلاس adapter
سطر مورد نظر می تواند view هایی داشته باشد که با data model (آبجکتی که داده ها را کپسوله می کند) از طریق کلاس adapter تعامل دارند. به طور مثال، شما می توانید یک Checkbox در layout مربوط به سطر داشته باشید و هر زمان که این Checkbox تیک دار و به اصطلاح انتخاب شد، داده های موجود در آبجکت model نیز همگام با آن تغییر کرده و بروز رسانی شوند.
آموزش ListActivity و ListFragment
آموزش ظرف آماده و پیش فرض برای استفاده از ListView
اندروید با ارائه ی fragment ها و کلاس های activity ویژه، پیاده سازی و مدیریت لیست را برای توسعه دهنده بسیار آسان می کند.
برای ایجاد لیست در activity و fragment، اندروید به ترتیب کلاس های ListActivity و ListFragment را ارائه می دهد.
لازم به ذکر است که برای این المان ها نیازی به تخصیص layout نیست چرا که activity یا fragment مورد نظر خود به صورت پیش فرض خود یک ListView آماده دارند.
ListActivity و ListFragment به برنامه نویس این اجازه را می دهند تا با بازنویسی (override) پیاده سازی متد onListItemClick() انتخاب آیتم ها در لیست را مدیریت کند.
هر دو کلاس همچنین این اختیار به برنامه نویس می دهند تا adapter را به وسیله ی متد SetListAdapter() به ListView پیش فرض متصل کند.
نمونه کد زیر یک پیاده سازی ساده از ListFragment را نشان می دهد.
package de.vogella.android.fragments;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.app.ListFragment;
public class MyListFragment extends ListFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2" };
ArrayAdapter adapter = new ArrayAdapter(getActivity(),
android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO implement some logic
}
}
نمونه کد بعدی که مشاهده می کنید، استفاده از ListActivity را به نمایش می گذارد.
package de.vogella.android.listactivity;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
public class MyListActivity extends ListActivity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2" };
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
}
}
آموزش ListActivity و layout اختصاصی
شما می توانید برای هر دو کلاس ListActivity و ListFragment فایل layout اختصاصی داشته باشید. در این مثال، fragment یا activity در layout ارائه شده به دنبال ListView ای که مقدار android:id آن به صورت پیش فرض بر روی @android:id/list تنظیم شده است، می گردد. تکه کد زیر این کاربرد را به نمایش می گذارد.
در صورتی که از این ID استفاده نکرده یا ListView را در layout خود اضافه نکنید، اپلیکیشن به هنگام نمایش activity یا fragment مربوطه از کار می افتد.
آموزش انتخاب یک view مکان نگهدار/placeholdder برای لیست خالی
می توانید از یک view با شاسنه ی @android:id/empty در layout خود استفاده کنید. چنانچه ListView تهی باشد، در آن صورت activity و fragment مربوطه این view را به صورت خودکار نمایش می دهند و در غیر این صورت آن را به طور کلی مخفی می کند. در چنین view ای شما می توانید یک پیغام خطا را به نمایش بگذارید.
تمرین: استفاده از ListView و ListActivity
پروژه ی زیر نحوه ی استفاده از یک ListView در ListActivity را نمایش می دهد. در این پروژه شما از کلاس از پیش تعریف شده ی ArrayAdapter و یک layout آماده ی اندروید برای سطرها استفاده می کنید.
یک پروژه ی جدید اندروید به نام de.vogella.android.listactivity و یک کلاس به نام MyListActivity ایجاد کنید.
کلاس MyListActivity را بر اساس نمونه کد زیر ویرایش نمایید. همان طور که می بینید متد setContentView() استفاده نشده است.
package de.vogella.android.listactivity;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class MyListActivity extends ListActivity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2" };
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
String item = (String) getListAdapter().getItem(position);
Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show();
}
}
تمرین: ListActivity با layout اختصاصی
در مثال حاضر، فایل layout مربوط به سطرها را خود به صورت اختصاصی تعریف کرده و آن را در adapter بکار می برید.
ابتدا فایل rowlayout.xml را در پوشه ی directory]]res/layout پروژه ی de.vogella.android.listactivity ایجاد نمایید.
Activity خود را جهت استفاده از layout جدید ویرایش نمایید.
package de.vogella.android.listactivity;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class MyListActivity extends ListActivity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2" };
// use your custom layout
ArrayAdapter adapter = new ArrayAdapter(this,
R.layout.rowlayout, R.id.label, values);
setListAdapter(adapter);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
String item = (String) getListAdapter().getItem(position);
Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show();
}
}
آموزش: پیاده سازی adapter اختصاصی
برنامه ی زیر از دو فایل تصویری "no.png" و "ok.png" استفاده می کند. این دو فایل در پوشه ی "res/drawable-mdpi" قرار دارند. شما بایستی آیکون های خود را ایجاد کنید. در صورتی که آیکون دلخواه را پیدا نکردید، می توانید "icon.png" را کپی کرده و با استفاده از یک برنامه ی گرافیکی آن را کمی تغییر دهید و دومین آیکون را هم به همین ترتیب ایجاد نمایید.
کلاس MySimpleArrayAdapter را ایجاد کنید. این کلاس adapter و پل ارتباطی بین model و UI خواهد بود.
package de.vogella.android.listactivity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class MySimpleArrayAdapter extends ArrayAdapter{ private final Context context; private final String[] values; public MySimpleArrayAdapter(Context context, String[] values) { super(context, R.layout.rowlayout, values); this.context = context; this.values = values; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rowView = inflater.inflate(R.layout.rowlayout, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.label); ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); textView.setText(values[position]); // Change the icon for Windows and iPhone String s = values[position]; if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { imageView.setImageResource(R.drawable.no); } else { imageView.setImageResource(R.drawable.ok); } return rowView; } }
برای استفاده از این adapter، لازم است activity را به صورت زیر ویرایش نمایید.
package de.vogella.android.listactivity;
import android.app.ListActivity;
import android.os.Bundle;
public class MyListActivity extends ListActivity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2" };
MySimpleArrayAdapter adapter = new MySimpleArrayAdapter(this, values);
setListAdapter(adapter);
}
}
با اجرای این برنامه لیستی از آیتم ها را مشاهده می کنید که کنار برخی آیکون ضرب در و کنار برخی دیگر تیک دیده می شود.
آموزش ListView و مبحث کارایی و سرعت اپلیکیشن
کارایی و سرعت در اندروید از اهمیت بسیار بالایی برخوردار است و کاربران انتظار واکنش سریع و سرعت بالای پاسخگویی از اپلیکیشن را دارند. از لحاظ قدرت سخت افزاری و منابع موجود، یک دستگاه اندروید به هیچ وجه با کامپیوترهای دسکتاپ قابل مقایسه نیست.
در این بخش شرح خواهیم داد چگونه adapter لیست خود را به صورت بهینه پیاده سازی نمایید و عملیات غیر ضروری را جهت افزایش سرعت اپلیکیشن و صرفه جویی در مصرف منابع سیستم کاهش دهید. گفتنی است که adapter های پیش فرض اندروید همچون ArrayAdapter طوری طراحی شده اند که از لحاظ کارایی کاملا بهینه هستند.
آموزش عملیات سنگین و زمان بر
تمامی view هایی که از فایل layout XML خوانده و inflate می شوند، در نهایت به آبجکت های Java تبدیل می شوند. این دو فرایند هر دو علاوه بر زمان بر بودن، حافظه ی قابل توجهی را به خود اختصاص می دهند. بعلاوه، استفاده از متد findViewById() بسیار زمان بر است، هرچند به اندازه ی inflate کردن فایل های XML سنگین نیست.
آموزش جلوگیری از inflation فایل های layout و ایجاد آبجکت های جاوا جهت بهینه سازی برنامه
یک آبجکت ListView معمولا اطلاعات بیشتری نسبت به سطرهای قابل مشاهده در صفحه دربردارد. چنانچه کاربر با اسکرول داخل لیست پیمایش کند، آن هنگام سطرها و view های مربوطه ی هریک از ناحیه ی قابل مشاهده برای کاربر خارج می شوند. آبجکت های جاوا که این سطرها را به نمایش می گذارند و در واقع کد پشت view ها هستند، قابل بازیافت و استفاده ی مجدد برای سطرهایی می باشند که با پیمایش کاربر، در صفحه به نمایش در می آیند.
اگر اندروید تشخیص دهد که سطری دیگر در صفحه جاری قابل مشاهده نیست، به متد getView() از کلاس adapter اجازه می دهد تا view مرتبط را از طریق پارامتر convertView بازیافت کند (reuse).
کلاس adapter می تواند داده های جدید را به view های موجود در سلسله مراتب view ها که با پارامتر convertView در دسترس قرار گرفته، متصل کند. این امر مانع از inflate شدن فایل XML و ایجاد آبجکت های جدید Java می شود.
چنانچه امکان بازیافت یک سطر وجود نداشت (reuse)، سیستم اندروید باید مقدار null را به پارامتر convertView ارسال کند. در واقع، این کد موجود در بدنه ی adapter است که باید مکانیزمی برای مدیریت این سناریو پیاده سازی کند.
آموزش استفاده از الگوی توسعه ی View holder در اپلیکیشن جهت بهینه سازی
پیاده سازی الگو توسعه ی ViewHolder این امکان را فراهم می کند تا از فراخوانی متد findViewById() در کلاس adapter جلوگیری شود.
ViewHolder معمولا یک کلاس درون ساخته static در adapter هست که اشاره گرهایی در قالب متغیر به view های مربوطه (که متناظر آن ها در فایل layout تعریف شده) در خود نگهداری می کند. این اشاره گرها به وسیله ی متد setTag() (و به صورت یک تگ) به view متناظر در سطر مورد نظر متصل می شوند.
چنانچه آبجکت convertView را دریافت کردیم، آنگاه می توانیم نمونه ای از ViewHolder را با متد getTag() بازیابی کرده و attribute های جدید را به وسیله ی اشاره گر (متغیر متناظر در) ViewHolder به view های مربوطه تخصیص دهیم.
اگرچه این روش کمی پیچیده به نظر می رسد، اما حدودا 15 % سریع تر از فراخوانی متد findViewById() و پیدا کردن تک تک viewها در فایل XML می باشد.
کد زیر نمونه ای بهینه سازی شده از یک adapter را نشان می دهد که از الگوی توسعه یview holder استفاده نموده و با بازیافت view های جاری در استفاده از منابع و حافظه صرفه جویی می کند.
package de.vogella.android.listactivity; import android.app.Activity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class MyPerformanceArrayAdapter extends ArrayAdapter{ private final Activity context; private final String[] names; static class ViewHolder { public TextView text; public ImageView image; } public MyPerformanceArrayAdapter(Activity context, String[] names) { super(context, R.layout.rowlayout, names); this.context = context; this.names = names; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; // reuse views if (rowView == null) { LayoutInflater inflater = context.getLayoutInflater(); rowView = inflater.inflate(R.layout.rowlayout, null); // configure view holder ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01); viewHolder.image = (ImageView) rowView .findViewById(R.id.ImageView01); rowView.setTag(viewHolder); } // fill data ViewHolder holder = (ViewHolder) rowView.getTag(); String s = names[position]; holder.text.setText(s); if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { holder.image.setImageResource(R.drawable.no); } else { holder.image.setImageResource(R.drawable.ok); } return rowView; } }
آموزش ذخیره سازی انتخاب یک view (تعیین وضعیت انتخاب با متد setChoiceMode())
به صورت پیش فرض، ListView هیچ حالت انتخاب پیش فرض و فعالی ندارد. شما می توانید این حالت را با فراخوانی متد setChoiceMode() فعال نمایید. برای اینکه کاربر بتواند چندین آیتم را انتخاب کند، می بایست پارامتر ListView.CHOICE_MODE_MULTIPLE را به متد مذکور ارسال کنید و اگر می خواهید امکان انتخاب یک آیتم را بیشتر نداشته باشد، مقدار ListView.CHOICE_MODE_SINGLE را به عنوان آرگومان به متد مورد نظر پاس می دهید.
جهت بازیابی آیتم های انتخاب شده ی ListView، اگر یک آیتم انتخاب شده باشد، متد getCheckedItemPosition() را فراخوانی می کنید و اگر چندین آیتم انتخاب شده باشد، listView.getCheckedItemPositions() را صدا می زنید. اگر آیتم ها دارای ID مشخصی هستند، می توانید با استفاده از متد getCheckedItemIds() شناسه یا ID آیتم های انتخاب شده را بازگردانی نمایید.
اندروید از قبل برای تنظیم ظاهر لیست هایی که چندین آیتم در آن انتخاب شده باشد، یک layout آماده ارائه می دهد: android.R.layout.simple_list_item_multiple_choice. این فایل دربردارنده ی view کاملا تنظیم شده CheckedTextView می باشد.
در activity های زیر نحوه ی استفاده از این حالت های انتخاب به نمایش گذاشته شده است. لازم به ذکر است که در صورت استفاده از این حالت ها، ListView مقادیر انتخابی را ذخیره می کند. اما اطلاعات مذکور به صورت دائمی در data model ذخیره و ماندگار نمی شوند.
package com.vogella.android.listview.selection.multi;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import com.vogella.android.listview.selection.R;
public class MainActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] values = new String[] { "a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "w", "x", "y", "z" };
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_multiple_choice, values);
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(this,
String.valueOf(getListView().getCheckedItemCount()),
Toast.LENGTH_LONG).show();
return true;
}
}
package com.vogella.android.listview.selection.single;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] values = new String[] { "a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "w", "x", "y", "z" };
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_single_choice, values);
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(this,
String.valueOf(getListView().getCheckedItemCount()),
Toast.LENGTH_LONG).show();
return true;
}
}
آموزش استفاده از Contextual action mode برای ListView ها (actionbar موقتی که بر روی actionbar اصلی قرار می گیرد)
جهت مطالعه و درک مطالب این بخش، بایستی از قبل با مفهوم ActionBar و contextual action mode آشنا باشید. در زیر به شرح نحوه ی استفاده از contextual action mode برای مدیریت حالت انتخاب در ListView خواهیم پرداخت.
به منظور تخصیص یک contextual action mode به یک آیتم از لیست که با کلیک طولانی مدت روی آیتم مورد نظر بر روی action bar اصلی برنامه (جهت انجام کارهای جزئی تر) نمایان می شود، کافی است متد setOnItemLongClickListener() را بر روی ListView بکار ببرید. این متد اطلاعات آیتم انتخابی را دربر دارد.
مثال زیر استفاده از contextual action mode را به صورت کاربردی به نمایش می گذارد. برای این مثال لازم است یک فایل منو XML به نام rowselection.xml تعریف کرده باشید که منوی آن تنها یک آیتم با شناسه ی @+id/menuitem1_show داشته باشد.
package de.vogella.android.listactivity;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Toast;
public class MyListActivityActionbar extends ListActivity
implements ActionMode.Callback {
protected Object mActionMode;
public int selectedItem = -1;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] values = new String[] { "Android",
"iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu",
"Windows7", "Max OS X", "Linux", "OS/2",
"Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2", "Ubuntu",
"Windows7", "Max OS X",
"Linux", "OS/2" };
MySimpleArrayAdapter adapter = new MySimpleArrayAdapter(this, values);
setListAdapter(adapter);
getListView().setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
if (mActionMode != null) {
return false;
}
selectedItem = position;
// Start the CAB using the ActionMode.Callback defined above
mActionMode = MyListActivityActionbar.this.startActionMode(MyListActivityActionbar.this);
view.setSelected(true);
return true;
}
});
}
private void show() {
Toast.makeText(MyListActivityActionbar.this, String.valueOf(selectedItem), Toast.LENGTH_LONG).show();
}
// Called when the action mode is created; startActionMode() was called
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
// Assumes that you have "contexual.xml" menu resources
inflater.inflate(R.menu.rowselection, menu);
return true;
}
// Called each time the action mode is shown. Always called after
// onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menuitem1_show:
show();
// Action picked, so close the CAB
mode.finish();
return true;
default:
return false;
}
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
selectedItem = -1;
}
}
حال اگر اپلیکیشن خود را اجرا کرده و بر روی یک آیتم در آن طولانی مدت کلیک کنید، contextual action bar شما به نمایش در می آید.
آموزش پیاده سازی قابلیت لغو/undo action
آموزش چه زمانی باید قابلیت لغو عملیات را به برنامه اضافه کنید
لازم است قابلیت لغو عملیات حساس را برای کاربر اپلیکیشن خود فراهم کنید. برای مثال ممکن است کاربر از حذف آیتم های انتخابی لیست منصرف شده و بخواهد این عملیات را لغو کند.
روش معمول و پر طرفدار این است که یک گزینه در انتهای صفحه برای کاربر به نمایش بگذارید که امکان انتخاب این گزینه با گذشت مدت زمان خاص یا هنگامی که کاربر به ادامه ی تعامل با اپلیکیشن می پردازد، از میان برداشته می شود.
به عنوان نمونه می توان به اپلیکیشن Gmail اشاره کرد که دقیقا همین رفتار را برای لغو عملیات (حذف) پیاده سازی می کند.
مثال
نمونه ی زیر قابلیت undo جهت لغو عملیات حذف دائمی یک آیتم را پیاده سازی می کند. برای این منظور یک نوار موقتی نمایان می شود که کاربر برای کلیک بر روی آن مدت زمان محدودی دارد و پس از گذشت آن بازه ی زمانی، نوار حاوی دکمه ی undo با انیمیشن از صفحه محو می شود.
یک پروژه ی جدید به نام com.vogella.android.userinterface.undo بر اساس قالب آماده (template) BlankTemplate ایجاد کنید.
فایل layout زیر را برای activity خود ایجاد کنید. این فایل با استفاده از FrameLayout دو بخش مجزای رابط کاربری را نمایش می دهد. نواری که دکمه را به نمایش می گذارد، در ابتدا مخفی می باشد. دکمه برای عکس خود از یک drawable استفاده می کند.
کلاس activity را به صورت زیر ویرایش کنید. ویزارد پروژه خود یک المان ActionBar تولید می کند. این المان در کد زیر بکار گرفته شده است. در صورت تمایل می توانید ActionBar دلخواه خود را ایجاد کنید.
package com.vogella.android.userinterface.undo;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import com.vogella.android.actionbar.undo.R;
public class MainActivity extends Activity {
private View viewContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView l = (ListView) findViewById(R.id.listview);
String[] values = new String[] { "Ubuntu", "Android", "iPhone","Windows", "Ubuntu", "Android", "iPhone", "Windows"};
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, values);
viewContainer = findViewById(R.id.undobar);
l.setAdapter(adapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
showUndo(viewContainer);
return true;
}
public void onClick(View view) {
Toast.makeText(this, "Deletion undone", Toast.LENGTH_LONG).show();
viewContainer.setVisibility(View.GONE);
}
public static void showUndo(final View viewContainer) {
viewContainer.setVisibility(View.VISIBLE);
viewContainer.setAlpha(1);
viewContainer.animate().alpha(0.4f).setDuration(5000)
.withEndAction(new Runnable() {
@Override
public void run() {viewContainer.setVisibility(View.GONE);
}
});
}
}
با انتخاب المان موجود در action bar، نواری حامل دکمه ی Undo به مدت 5 ثانیه نمایش داده می شود.
آموزش بهینه سازی کارایی و سرعت اپلیکیشن
نمونه ی زیر نسخه ی بهینه از adapter مثال قبلی که سرعت و کارایی نسبتا بهتری دارد را پیاده سازی می کند.
ابتدا کلاس زیر به نام MyPerformanceArrayAdapter را ایجاد کنید.
package de.vogella.android.listactivity; import android.app.Activity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class MyPerformanceArrayAdapter extends ArrayAdapter{ private final Activity context; private final String[] names; static class ViewHolder { public TextView text; public ImageView image; } public MyPerformanceArrayAdapter(Activity context, String[] names) { super(context, R.layout.rowlayout, names); this.context = context; this.names = names; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; // reuse views if (rowView == null) { LayoutInflater inflater = context.getLayoutInflater(); rowView = inflater.inflate(R.layout.rowlayout, null); // configure view holder ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01); viewHolder.image = (ImageView) rowView .findViewById(R.id.ImageView01); rowView.setTag(viewHolder); } // fill data ViewHolder holder = (ViewHolder) rowView.getTag(); String s = names[position]; holder.text.setText(s); if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) {holder.image.setImageResource(R.drawable.no); } else {holder.image.setImageResource(R.drawable.ok); } return rowView; } }
Adapter جدید خود را به activity اضافه کنید. با اجرای برنامه متوجه می شوید که ظاهر آن تغییری نکرده، با این وجود اپلیکیشن به ویژه در خواندن و کار با dataset های بزرگ بسیار سریع تر عمل می کند.
package de.vogella.android.listactivity;
import android.app.ListActivity;
import android.os.Bundle;
public class MyListActivity extends ListActivity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2" };
setListAdapter(new MyPerformanceArrayAdapter(this, values));
}
}
آموزش نحوه ی نمایش دو آیتم در یک ListView
می توانید کلاس SimpleAdapter را جهت نمایش داده های دو المان بکار ببرید. این کلاس به آرایه ای از رشته ها (داده های متغیر آرایه ای from) احتیاج دارد که در آن فیلدهایی که قرار است داده ها از بیرون وارد آن شوند (input data) تعریف شده است. کلاس مزبور همچنین به آرایه ای از اعداد صحیح (int array) نیاز دارد که این اعداد ID یا شناسه ی منحصربفرد widget هایی در فایل layout می باشند که فیلدها در سطر به آن نگاشت (map) می شوند.
داده ها در این برنامه لیستی از نوع Map ها (خانواده ای برای نگهداری جفت های کلید و مقدار) است. Map به ازای هر فیلد در From یک مقدار تعریف می کند.
در زیر مثالی را می بینید که برای تنظیم ظاهر سطر مربوطه از یک layout آماده و از پیش ساخته شده ی اندروید استفاده می کند.
package de.vogella.android.listactivity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.SimpleAdapter;
public class MyTwoListItemsActivity extends ListActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<>> list = buildData();
String[] from = { "name", "purpose" };
int[] to = { android.R.id.text1, android.R.id.text2 };
SimpleAdapter adapter = new SimpleAdapter(this, list,
android.R.layout.simple_list_item_2, from, to);
setListAdapter(adapter);
}
private ArrayList<>> buildData() {
ArrayList<>> list = new ArrayList<>>();
list.add(putData("Android", "Mobile"));
list.add(putData("Windows7", "Windows7"));
list.add(putData("iPhone", "iPhone"));
return list;
}
private HashMap putData(String name, String purpose) {
HashMap item = new HashMap();
item.put("name", name);
item.put("purpose", purpose);
return item;
}
}
آموزش انتخاب چندین آیتم در ListView
آموزش تعامل بین model و ListView
پس از ایجاد لیست، قاعدتا می خواهید آیتم هایی را در آن انتخاب نمایید. از آنجایی که (به هنگام پیمایش به پایین و بالای لیست) سطرهای ListView بازیافت شده (recycle) و مجددا مورد استفاده قرار می گیرند، نمی توان انتخاب (اطلاعات مربوط به وضعیت و انتخاب) را در سطح View ذخیره کرد.
انتخاب آیتم ها در لیست (و تبادل داده بین UI و Model)، تنها یک نمونه از تعامل بین سطر (UI) و داده های متناظر در model است و می توان سناریوهای دیگری را تصور کرد.
به منظور ماندگارسازی و ذخیره ی دائمی انتخاب، شما بایستی data model خود را با اطلاعات مربوط به وضعیت انتخاب شده بروز رسانی نمایید.
?
به منظور بروز رسانی data model در ListView، ابتدا می بایست کلاس Adapter اختصاصی خود را تعریف نمایید. در سطح این کلاس، شما یک listener به View متصل می کنید که وظیفه ی انتخاب المان متناظر در model را بر عهده دارد. پس از انتخاب المان مورد نظر در لیست، اطلاعات المان مربوطه را در model بروز رسانی می کنید. حال برای بروز رسانی و دسترسی به اطلاعات المان متناظر در model، آن را به صورت یک تگ به view اضافه می کنید.
مثال زیر نشان می دهد چگونه از آبجکت های استاندارد جاوا استفاده کرده و چگونه بین Views و model تعاملو ارتباط دو طرفه برقرار نمایید.
در این تمرین نیز پروژه ی قبلی، de.vogella.android.listactivity، را ادامه می دهید.
ابتدا Model زیر را ایجاد نمایید. این کلاس اسم و اطلاعات را، در صورتی که المان در حال حاضر انتخاب شده باشد، در خود ذخیره می نماید.
package de.vogella.android.listactivity;
public class Model {
private String name;
private boolean selected;
public Model(String name) {
this.name = name;
selected = false;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
یک فایل layout به نام rowbuttonlayout.xml مانند زیر ایجاد نمایید.
یک کلاس Adapter با پیاده سازی زیر ایجاد نمایید. کلاس نام برده یک listener (گوش فراخوان به رخدادها) بر روی آبجکت Checkbox اضافه می کند. به دنبال انتخاب checkbox، داده های مربوطه در model نیز بروز رسانی می شوند. Checkbox به وسیله ی متد getTag()، داده های المان متناظر خود در data model را بازیابی می کند.
package de.vogella.android.listactivity; import java.util.List; import android.app.Activity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; public class InteractiveArrayAdapter extends ArrayAdapter{ private final List list; private final Activity context; public InteractiveArrayAdapter(Activity context, List list) { super(context, R.layout.rowbuttonlayout, list); this.context = context; this.list = list; } static class ViewHolder { protected TextView text; protected CheckBox checkbox; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = null; if (convertView == null) { LayoutInflater inflator = context.getLayoutInflater(); view = inflator.inflate(R.layout.rowbuttonlayout, null); final ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) view.findViewById(R.id.label); viewHolder.checkbox = (CheckBox) view.findViewById(R.id.check); viewHolder.checkbox .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Model element = (Model) viewHolder.checkbox .getTag(); element.setSelected(buttonView.isChecked()); } }); view.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); } else { view = convertView; ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position)); } ViewHolder holder = (ViewHolder) view.getTag(); holder.text.setText(list.get(position).getName()); holder.checkbox.setChecked(list.get(position).isSelected()); return view; } }
حال کد activity را به صورت زیر ویرایش نمایید.
package de.vogella.android.listactivity;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
public class MyList extends ListActivity {
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// create an array of Strings, that will be put to our ListActivity
ArrayAdapter adapter = new InteractiveArrayAdapter(this,
getModel());
setListAdapter(adapter);
}
private List getModel() {
List list = new ArrayList();
list.add(get("Linux"));
list.add(get("Windows7"));
list.add(get("Suse"));
list.add(get("Eclipse"));
list.add(get("Ubuntu"));
list.add(get("Solaris"));
list.add(get("Android"));
list.add(get("iPhone"));
// Initially select one of the items
list.get(1).setSelected(true);
return list;
}
private Model get(String s) {
return new Model(s);
}
}
برنامه را اجرا کرده و آیتم ها را علامت گذاری کنید. با توجه به کد اپلیکیشن، تمامی تغییرات در UI اپلیکیشن، بلافاصله در model منعکس شده و در واقع آبجکت model به صورت خودکار با داده های جدید بروز رسانی می شود.
آموزش پیاده سازی یک ListView کشویی و قابل بسط
ExpandableListView
ExpandableListView مشابه ListView است با این تفاوت که به شما این امکان را می دهد تا برای المان ها گروه و جزئیات بیشتر ایجاد کنید. برای این منظور group.ExpandableListView انتظار یک آبجکت adapter از جنس BaseExpandableListAdapter را دارد.
در این سناریو بایستی دو layout تعریف کنید: یکی برای گروه و دیگری برای سطری که جزئیات را نمایش می دهد.
در این مثال، یک لیست کشویی یا ExpandableListView مشابه تصویر زیر ایجاد خواهید نمود.
آموزش پیاده سازی یک ExpandableListView
یک پروژه جدید به نام com.vogella.android.listview.expandable همراه با activity ای به نام MainActivity ایجاد کنید.
فایل های layout زیر را ایجاد نمایید. ابتدا فایل layout/activity_main.xml را تعریف کنید.
سپس فایل layout/listrow_group.xml را ایجاد نمایید.
آخرین فایل layout ای که باید تعریف کنید، layout/listrow_details.xml می باشد.
کلاسی با پیاده سازی زیر را تعریف نمایید که به عنوان domain model آبجکت ExpandableListView شما ایفای نقش می کند.
package com.vogella.android.listview.expandable;
import java.util.ArrayList;
import java.util.List;
public class Group {
public String string;
public final List children = new ArrayList();
public Group(String string) {
this.string = string;
}
}
حال کلاس adapter با پیاده سازی زیر را ایجاد نموده و کد کلاس activity را به صورت زیر ویرایش نمایید.
package com.vogella.android.listview.expandable;
import android.app.Activity;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckedTextView;
import android.widget.TextView;
import android.widget.Toast;
public class MyExpandableListAdapter extends BaseExpandableListAdapter {
private final SparseArray groups;
public LayoutInflater inflater;
public Activity activity;
public MyExpandableListAdapter(Activity act, SparseArray groups) {
activity = act;
this.groups = groups;
inflater = act.getLayoutInflater();
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return groups.get(groupPosition).children.get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}
@Override
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final String children = (String) getChild(groupPosition, childPosition);
TextView text = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.listrow_details, null);
}
text = (TextView) convertView.findViewById(R.id.textView1);
text.setText(children);
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity, children,
Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
@Override
public int getChildrenCount(int groupPosition) {
return groups.get(groupPosition).children.size();
}
@Override
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}
@Override
public int getGroupCount() {
return groups.size();
}
@Override
public void onGroupCollapsed(int groupPosition) {
super.onGroupCollapsed(groupPosition);
}
@Override
public void onGroupExpanded(int groupPosition) {
super.onGroupExpanded(groupPosition);
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.listrow_group, null);
}
Group group = (Group) getGroup(groupPosition);
((CheckedTextView) convertView).setText(group.string);
((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
}
package com.vogella.android.listview.expandable;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.Menu;
import android.widget.ExpandableListView;
public class MainActivity extends Activity {
// more efficient than HashMap for mapping integers to objects
SparseArray groups = new SparseArray();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createData();
ExpandableListView listView = (ExpandableListView) findViewById(R.id.listView);
MyExpandableListAdapter adapter = new MyExpandableListAdapter(this,
groups);
listView.setAdapter(adapter);
}
public void createData() {
for (int j = 0; j < 5; j++) {
Group group = new Group("Test " + j);
for (int i = 0; i < 5; i++) {
group.children.add("Sub Item" + i);
}
groups.append(j, group);
}
}
}
آموزش متفرقه
آموزش تعریف کردن یک گوش فراخوان به کلیک طولانی مدت (LongItemClickListener) به آیتم های لیست
بد نیست یک LongItemClickListener به وسیله ی setOnItemLongClickListener() برای آیتم های لیست خود تعریف کنید که با کلیک طولانی کاربر بر روی آیتم های لیست صدا خورده می شود.
package de.vogella.android.listactivity;
import android.widget.AdapterView.OnItemLongClickListener;
public class MyList extends Activity {
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// configure your list view as before
// ListView is assigned to local variable list...
list.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view,
int position, long id) {
Toast.makeText(MyList.this,
"Item in position " + position + " clicked",
Toast.LENGTH_LONG).show();
// Return true to consume the click event. In this case the
// onListItemClick listener is not called anymore.
return true;
}
});
}
}
آموزش اضافه کردن Header و Footer به لیست
می توانید المان های دلخواه خود را به لیست اضافه نمایید. به عنوان مثال می توانید یک layout یا قالب با این چیدمان تعریف نمایید: دو text view تعریف کرده و یک لیست بین این دو قرار دهید. در این نوع چیدمان یک text view در بالای لیست و در بخش header صفحه و دیگری در پایین text view و در بخش footer قابل مشاهده می باشد. برای نمایش header یا footer در ابتدا و انتهای لیست، کافی است دو متد addHeaderView() یا addFooterView() را در سطح کلاس ListView (بر روی آبجکت listView در قطعه کد زیر) تعریف نمایید.
// configuration as before ListView listView = (ListView) findViewById(R.id.list); View header = getLayoutInflater().inflate(R.layout.header, null); View footer = getLayoutInflater().inflate(R.layout.footer, null); listView.addHeaderView(header); listView.addFooterView(footer); listView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_single_choice, android.R.id.text1, names)); } }
آموزش SimpleCursorAdapter
چنانچه لازم است داده های برنامه ی خود را از Content provider دریافت کنید یا آن را مستقیم از دیتابیس بخوانید، در آن صورت می توانید با استفاده از کلاس SimpleCursorAdapter اطلاعات مورد نیاز لیست (ListView) خود را مشخص نمایید (داده ها را از cursor به view ها متصل نمایید). توضیحات زیر برای شما شرح می دهد چگونه می توان بهcontent provider Contacts (یک دیتابیس مقایس پذیر با اطلاعات مربوط به مخاطبین یا contacts تعریف می کند) دسترسی داشت و اطلاعات مورد نیاز لیست را در اختیار آن قرار داد ( یادآوری: content provider به شما اجازه می دهد تا داده های خود را در نقطه ی مرکزی برای دسترسی اپلیکیشن هایی که به آن نیاز دارند به اشتراک بگذارید).
یک پروژه ی جدید اندروید به نام "de.vogella.android.listactivity.cursor" به همراه activity ای به نام MyListActivity تعریف نمایید. کلاس MyListActivity را به صورت زیر ویرایش نمایید.
package de.vogella.android.listactivity.cursor;
import android.app.ListActivity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ListAdapter;
import android.widget.SimpleCursorAdapter;
public class MyListActivity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Cursor mCursor = getContacts();
startManagingCursor(mCursor);
// now create a new list adapter bound to the cursor.
// SimpleListAdapter is designed for binding to a Cursor.
ListAdapter adapter = new SimpleCursorAdapter(this, // Context.
android.R.layout.two_line_list_item, // Specify the row template
// to use (here, two
// columns bound to the
// two retrieved cursor
// rows).
mCursor, // Pass in the cursor to bind to.
// Array of cursor columns to bind to.
new String[] { ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME },
// Parallel array of which template objects to bind to those
// columns.
new int[] { android.R.id.text1, android.R.id.text2 });
// Bind to our new adapter.
setListAdapter(adapter);
}
private Cursor getContacts() {
// Run query
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] { ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME };
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '"
+ ("1") + "'";
String[] selectionArgs = null;
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC";
return managedQuery(uri, projection, selection, selectionArgs,
sortOrder);
}
}
لازم است به اپلیکیشن خود مجوز خواندن اطلاعات contacts را بدهید (مقدار خصیصه ی android:name: را در فایل تنظیمات یا manifest بر روی "android.permission.READ_CONTACTS" قرار دهید).
آموزش سایر کتابخانه های کد باز (Open Source libraries)
کاربر مجبور است برای بروز رسانی ActionBar بر روی دکمه ی refresh کلیک کند که ممکن است گاهی تجربه ی کاربری ضعیفی را به دنبال داشته باشد. Chris Banes یک کتابخانه ی کد باز پیاده سازی کرده که الگوی pull to refresh را برای ListView در اپلیکیشن شما implement می کند. برای دسترسی به آن می توانید به این آدرس مراجعه نمایید: https://github.com/chrisbanes/Android-PullToRefresh.
علاوه بر آن شاید لازم باشد با پیاده سازی قابلیت swipe در اپلیکیشن، به کاربر این امکان را بدهید که آیتم ها را با کشیدن انگشت بر روی نمایشگر از لیست حذف کند. Roman Nurik برای این منظور یک کتابخانه ی آماده نوشته و Jake Wharton این کتابخانه را برای پشتیبانی از ورژن های قبلی کتابخانه های اندروید (و افزودن قابلیت های جدید به API های قدیمی تر) backport نموده است. برای دسترسی به هریک می توانید به ترتیب به آدرس های https://github.com/romannurik/android-swipetodismiss و https://github.com/JakeWharton/SwipeToDismissNOA مراجعه نمایید.