در دو مطلب قبلی (اینجا و اینجا) از دیتابیس در اندروید حرف زدیم و با کلیات آن آشنا شدیم ولی هنوز از پایگاه داده در برنامهای به صورت واقعی استفاده نکردیم. حالا در این مطلب میخواهیم از دیتابیس به صورت واقعی استفاده کنیم. در این مطلب میخواهیم اسامی افراد را وارد دیتابیس کنیم و بعد آنها را در لیستویو (ListView) نشان دهیم! به همین سادگی!
۱- ذخیره اطلاعات افراد در پایگاه داده:
اگر از دو مطلب قبلی به خاطر داشته باشید تابع onCreate در اکتیویتی MainActivity را به خاطر داشته باشید میبینید که آن را نیمه کاره رها کردیم. حالا میخواهیم برگردیم به تابع onCreate و آن را کامل کنیم. تابع onClick دکمه saveButton را به شکل زیر کامل میکنیم:
saveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { name = nameEditText.getText().toString(); family = familyEditText.getText().toString(); PersonDatabaseAdapter databaseAdapter = new PersonDatabaseAdapter(MainActivity.this); Person person = new Person(); person.setName(name); person.setFamily(family); databaseAdapter.savePerson(person); } });
فرایند کار بسیار ساده است. اول از کلاس PersonDatabaseAdapter که در دو مطلب قبلی با آن سر و کله میزدیم یک شی میسازیم. بعد از اطلاعاتی که کاربر در فرم وارد کرده است یک شی از کلاس Person میسازیم و بعد از شیا ساخته شده از PersonDatabaseAdapter میخواهیم که آن را در دیتابیس ذخیره کند! به همین سادگی!
۲- خواندن همه اطلاعات از دیتابیس:
در کلاس PersonDatabaseAdapter توابعی برای ذخیره کردن و اصلاح کردن و حذف کردن و خواندن Person نوشتیم ولی حالا میخواهیم یک تابع دیگر به این کلاس اضافه کنیم که همه Person ها را از دیتابیس بخوانیم. این تابع خیلی شبیه همان تابع readPerson است با یک تفاوت: این بار یک حلقه تکرار به تابع اضافه میکنیم تا بتوانیم همه اطلاعات را از Cursor بخوانیم:
public ArrayList<Person> readAllPerson() { ArrayList<Person> persons = null; String[] columns = new String[]{"id", "name", "family"}; String selection = null; String[] selectionArgs = null; String groupBy = null; String having = null; String orderBy = null; String limit = null; SQLiteDatabase database = null; try { database = sqLiteOpenHelper.getWritableDatabase(); Cursor cursor = database.query("tbl_persons", columns, selection, selectionArgs, groupBy, having, orderBy, limit); if (cursor != null && cursor.moveToFirst()) { persons = new ArrayList<>(); int idIndex = 0; int nameIndex = 1; int familyIndex = 2; do { long personId = cursor.getLong(idIndex); String personName = cursor.getString(nameIndex); String personFamily = cursor.getString(familyIndex); Person person = new Person(); person.setId(personId); person.setName(personName); person.setFamily(personFamily); persons.add(person); } while(cursor.moveToNext()); } } catch (Exception ex) { Log.d("Database", "Exception:" + ex.getMessage()); } finally { if (database != null && database.isOpen()) { database.close(); } } return persons; }
اگر با یک نگاه فهمیدید که چه اتفاقی افتاده است، بروید به بخش بعدی. اما اگر ابهام دارید توضیحات زیر را بخوانید.
اولین تغییر در امضای تابع است. به جای این که تابع یک Person برگرداند حالا یک لیست از Person برمیگرداند:
public ArrayList<Person> readAllPerson() { ... }
تغییر بعدی این است که دیگر لازم نیست id را به تابع بفرستیم و بعد هم این که این بار به جای این که کوئری (query) بر اساس id باشد و فقط به دنبال یک فرد خاص بگردد قرار است همه رکوردهای اطلاعاتی را برگرداند:
String selection = null; String[] selectionArgs = null;
تغییر بعدی بعد از چک کردن cursor است. اگر cursor خالی نباشد پس یک یا چند رکورد اطلاعاتی در آن است. بنابراین اول یک ArrayList از Person میسازیم:
persons = new ArrayList<>();
حالا رسیدیم به تغییر اصلی: ایجاد یک حلقه تکرار و خواندن همه اطلاعاتی که داخل cursor وجود دارد. در هر بار چرخش این حلقه تکرار یک شی Person ایجاد میشود و به ArrayList اضافه میشود:
do { long personId = cursor.getLong(idIndex); String personName = cursor.getString(nameIndex); String personFamily = cursor.getString(familyIndex); Person person = new Person(); person.setId(personId); person.setName(personName); person.setFamily(personFamily); persons.add(person); } while(cursor.moveToNext());
تنها نکته جدیدی که در این حلقه تکرار است تابع moveToNext است. این تابع به صورت خیلی ساده چک میکند که آیا cursor اطلاعات بیشتری دارد یا نه. اگر اطلاعات بیشتری در cursor باشد این تابع cursor را به جلو میبرد و مقدار true را برمیگرداند. در غیر این صورت مقدار false برمیگرداند و حلقه تکرار متوقف میشود.
۳- ساختن اکتیویتی نمایش دهنده لیست افراد
بالاخره کلاس PersonDatabaseAdapter تمام شد! حالا تمام اطلاعاتی که میخواهیم را دارد! برای ادامه ابتدا یک Activity به پروژه اضافه میکنیم به نام PersonListActivity.
اولین کاری که میکنیم این است که فایل layout آن را به شکل زیر تغییر میدهیم:
<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/personlistView" android:layout_width="match_parent" android:layout_height="match_parent" />
حالا یک دکمه به اکتیویتی Main اضافه میکنم که ما را به این اکتیویتی بیاورد:
<Button android:id="@+id/goToList" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/goToList" android:layout_gravity="center_horizontal"/>
و در کد هم این تابع را اضافه میکنیم:
goToListButton = (Button) findViewById(R.id.goToList); goToListButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, PersonListActivity.class); startActivity(intent); } });
۴- نمایش همه اطلاعات با لیستویو:
حالا وارد اکتیویتی PersonListActivity میشویم. در ابتدا باید لیستویو را تعریف کنیم:
public class PersonListActivity extends AppCompatActivity { private ListView personListView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_person_list); personListView = (ListView) findViewById(R.id.personListView); } }
بعد باید اطلاعات همه افراد را از دیتابیس بگیریم:
public class PersonListActivity extends AppCompatActivity { private ListView personListView; private PersonDatabaseAdapter databaseAdapter; private ArrayList<Person> persons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_person_list); personListView = (ListView) findViewById(R.id.personListView); databaseAdapter = new PersonDatabaseAdapter(this); persons = databaseAdapter.readAllPerson(); } }
به نظر میرسد که همه چیز آماده است اما این طور نیست. هنوز به لیستویو نگفتهایم که هر Person را چطور نمایش بدهد. برای این کار ابتدا باید قالب یا الگوی هر Person را برای لیستویو تعریف کنیم. برای این کار یک فایل layout جدید میسازیم:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/personNameTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/personFamilyTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textStyle="bold" android:textSize="18sp"/> </LinearLayout>
حالا لیستویو را داریم، دادهها را داریم و قالبی که هر رکورد اطلاعاتی را قرار است نمایش دهد. فقط یک چیز کم داریم: چطور اینها همه را به هم وصل کنیم؟
تصویر زیر به درک شما کمک خواهد کرد را ببینید:
(ببخشید که بهتر از این نتوانستم گرافیک مورد نظر را بسازم!)
فرایندی که سعی کردم با تصویر بالا به نمایش بکشم، این است:
۱- یک View از زوی فایل xml قالب ساخته میشود.
۲- این View با اطلاعات یک رکورد اطلاعاتی (در اینجا یک Person) بروزرسانی میشود.
۳- این View به لیستویو داده میشود تا لیستویو این View را به همراه سایر ویوها نمایش دهد.
اما همه این کارها را چطور انجام دهیم؟
با استفاده از کلاس Adapter مخصوص لیستویو! برای لیستویو هم باید یک کلاس Adapter بنویسیم که همه این کارها را انجام میدهد. این کلاس از BaseAdapter ارث میبرد (یا طبق ادبیات جاوا BaseAdapter را توسعه میدهد):
public class PersonListAdapter extends BaseAdapter { @Override public int getCount() { return 0; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { return null; } }
این کلاس حداقل ۴ متد دارد که کارهای مربوط به لیستویو را انجام میدهد.
۱- تابع getCount به لیستویو میگوید که تعداد کل اطلاعاتی که قرار است لیستویو نمایش دهد چند تا است.
۲- تابع getItem به لیستویو میگوید که در موقعیت position ام لیستویو چه آیتمی قرار است نمایش داده شود.
۳- تابع getItemId به لیستویو میگوید که Id شیای که در موقعیت position ام لیستویو قرار دارد چیست.
۴- getView یک شی View برای موقعیت position ام لیستویو میسازد.
برای شروع کار دو فیلد جدید به این کلاس اضافه میکنیم:
private ArrayList<Person> persons; private Context context;
حالا یک متد سازنده یا constructor به این کلاس اضافه میکنیم که دو فیلد بالایی را مقدار دهی کند:
public PersonListAdapter(ArrayList<Person> persons, Context context) { this.persons = persons; this.context = context; }
حالا میرویم سراغ تکمیل کردن چهار تابع اصلی کلاس PersonListAdapter.
۱- تابع getCount: همانطور که گفتم این تابع تعداد آیتمیهایی که قرار است لیستویو نمایش دهد را به لیستویو میگوید. در اینجا این تعداد برابر است با تعداد اشیای موجود در آرایه persons. بنابراین پیادهسازی آن به شکل زیر است:
@Override public int getCount() { return persons.size(); }
این کد مستعد بروز خطا یا exception است. چرا؟ ممکن است آرایه persons مقداردهی نشده باشد (اصطلاحا null باشد) و صدا زدن تابع یک شی null خطای NullPointerException میدهد. برای رفع این مشکل کد تابع را به شکل زیر بازنویسی میکنیم:
@Override public int getCount() { if( persons == null) { return 0; } return persons.size(); }
۲- تابع getItem: همانطور که گفتم این تابع شیای که قرار است در موقعیتی که توسط position مشخص شده است نمایش داده شود را به لیستویو میدهد. در مثال ما کافی است از آرایه persons شی با اندیس position را برگردانیم:
@Override public Object getItem(int position) { return persons.get(position); }
۳- تابع getItemId: این تابع به لیستویو میگوید که Id شیای که در موقعیت position قرار دارد چیست. پیادهسازی این تابع هم بسیار ساده است:
@Override public long getItemId(int position) { return persons.get(position).getId(); }
۴- تابع getView: اصلیترین تابع کلاس Adapter است. کار این تابع خواندن فایل xml قالب و پر کردن آن با اطلاعات است. مراحل گام به گام تکمیل این تابع را دنبال کنید:
الف- ساختن یک LayoutInflater:
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
کار این شی این است که یک فایل layout که در شاخه res/layout قرار دارد را بخواند و یک شی View را به ما بدهد.
ب- ساختن یک View از روی قالب layout با استفاده از شی inflater:
View view = inflater.inflate(R.layout.item_person_list, parent, false);
این خط به طور خلاصه این کارها را میکند: یک شی View از روی قالب item_position_list میسازد.
ج- پیدا کردن دو TextView درون view ومقداردهی کردن آنها:
TextView personNameTextView = (TextView) view.findViewById(R.id.personNameTextView); personNameTextView.setText(persons.get(position).getName()); TextView personFamilyTextView = (TextView) view.findViewById(R.id.personFamilyTextView); personFamilyTextView.setText(persons.get(position).getFamily());
د- پس فرستادن view به لیستویو:
return view;
لیستویو این شی را در موقعیت position ام لیست نمایش خواهد داد.
شکل کامل کلاس PersonListAdapter این است:
public class PersonListAdapter extends BaseAdapter { private ArrayList persons; private Context context; public PersonListAdapter(ArrayList persons, Context context) { this.persons = persons; this.context = context; } @Override public int getCount() { if (persons == null) { return 0; } return persons.size(); } @Override public Object getItem(int position) { return persons.get(position); } @Override public long getItemId(int position) { return persons.get(position).getId(); } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.item_person_list, parent, false); TextView personNameTextView = (TextView) view.findViewById(R.id.personNameTextView); personNameTextView.setText(persons.get(position).getName()); TextView personFamilyTextView = (TextView) view.findViewById(R.id.personFamilyTextView); personFamilyTextView.setText(persons.get(position).getFamily()); return view; } }
خب، کار ما با کلاس PersonListAdapter تمام شد! حالا فقط یک کار کوچک باقی مانده است: ساختن یک شی از روی این کلاس و دادن آن به لیستویو. برای این کار به PersonListActivity برمیگردیدم و این دو خط را به انتهای تابع onCreate اضافه میکنیم:
PersonListAdapter personListAdapter = new PersonListAdapter(persons, this); personListView.setAdapter(personListAdapter);
شکل کلی این متد این است:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_person_list); personListView = (ListView) findViewById(R.id.personListView); databaseAdapter = new PersonDatabaseAdapter(this); persons = databaseAdapter.readAllPerson(); PersonListAdapter personListAdapter = new PersonListAdapter(persons, this); personListView.setAdapter(personListAdapter); }
حالا وقت آن رسیده است که برنامه را تست کنیم!
با اجرای برنامه در شبیه ساز، صفحه ایجاد Person نمایش داده میشود:
چندین اسم وارد کنید و بعد دکمه «لیست افراد» را بزنید تا به اکتیویتی PersonListView برویم:
هنوز با دیتابیس و لیستویو کار زیاد داریم! اسمارتلب را دنبال کنید و در کانال تلگرام آن عضو شوید!
خیلی خوب و کامل بود… ممنون واقعا…
اما یک سوال!
من میخوام درون SQLite Expert Professional جدول بسازم و بعدش اونو در پوشه ی assets درون اندروید استدیو ذخیره کنم… ینی میخوام یه لیست ویو بسازم که مقدارشو از قبل درون جدول های دیتابیسم ریخته باشم و میخوا تو لیست ویوم اونارو نشون بدم.. اونوقت دقیقا باید چیکار کنم؟؟
فکر نکنم با این آموزش بتونم این کارو بکنم و باید منتظر اموزش های بعدی تون باشم… درسته؟
می تونید از این کتابخونه استفاده کنید:
https://github.com/jgilfelt/android-sqlite-asset-helper
با سلام و درود
از زحمات شما تشکر می کنم
من وقتی این پروژه رو دنبال کردم کلی error خورد
لطفا کمکم کنید
اگه میشه سورس کلی رو بزارید چون مطالب خیلی خیلی زیاد و پراکنده است
با تشکر فراوان از شما
سلام
ارورها رو بنویسید تا ببینم چی بودن.
کل کد رو تو گیتهاب میذارم تا بتونید دانلود کنید.
سپاسگزارم.
سلام. خسته نباشید واقعا.
ممنونم بابت آموزش کاملتون. ازش خیلی استفاده کردم و واقعا برام مفید بود. امیدوارم بازم ادامه داشته باشه.
سپاسگزارم و خوشحالم که براتون مفید بود.
سلام.من دستوراتو کامل اجرا کردم ولی وقتی دکمه ی نمایش و میزنم اررور میده و بسته میشه
سلام
اروری که میده رو دقیق بنویسید تا ببینم مشکلش چیه.
با سلام و تشکر. در شکل کلی کلاس PersonListAdapter باید ArrayList اصلاح شود.
با سلام
ممنون از آموزشتون ….
من مطابق آموزش ها رفتم جلو اما در کلاس PersonListAdapter متاسفانه getId , getName و getFamily رو نمیشناسه …. دلیل این مشکل چیه ؟؟
سلام، با کد برنامه تو گیتهاب مقایسه کنید.
سلام استاد
واقعا توضیحاتتون عالی بود
من برنامه رو از روی گیت ها نوشتم و ب نتیجه هم رسیدم فقط اینکه نگفته بودید چطور اطلاعات رو از دیتابیس پاک کنیم یا ویرایش کنیم!!!!
اگه میشه این قسمت ها رو هم بهش اضافه کنید
ممنون
ایا دستورات مربوط به برنامه فرایاد رو هم توضیح دادین
اگه دادین از کجا میتونیم دانلودش کنیم؟؟؟
با تشکر
دوست عزیز و دوستان عزیز
با سلام
ابتدا از زحمات این دوست عزیز کمال تشکر را دارم
دوم اینکه یک جای برنامه ایراد داره که باعث خطا دادن در هنگام اجرا میشه لطفا آن را اصلاح کنید
در قسمت “شکل کامل کلاس PersonListAdapter این است:”
دستور : private ArrayList persons; ایراد دارد و باید به صورت زیر باشد :
private ArrayList persons;
بازم از شما متشکرم
ظاهرا تگ من رو حذف میکنه منظورم این بود در اون خط باید persones از نوع آرایه Person تعریف شود
میشه دقیق تر توضیح بدید یا کدشو بزارید من که نفهمیدم
کد کامل پروژه را در گیتهاب اسمارتلب آپلود کردم: دانلود کد از گیتهاب
ممنون
ان شاالله که همیشه سالم و تندرست باشید،واقعا مفید و کاربری بود
سلام
مرسی از آموزش خوبتون…
به یه مشکل بر خوردم، getId و getName و getFamily رو نمیتونم داخل کلاس PersonListAdapter فراخوانی کنم…
ممنون میشم کمک کنید
سلام متشکرم از شما بابت آموزش خوب و کاربردیتون
فقط بخش ویرایش حذف رو نگفتین !!!!
اگه این دو بخش رو هم بگین من ممنون میشم ازتون
سلام، ویرایش و حذف در بخش قبلی مطلب آمده است:
آموزش اندروید-فصل ۲۴: پایگاه داده در اندروید (قسمت دوم)
سلام ممنون از آموزش های خوبتون ببخشید من میخوام یک فرم login درست کنم که پسورد و رمز رو اعتبار سنجی کنه اینکارو چگونه باید انجام بدم ؟
وقتی به سوال یا مشکلی میخورم و سرچ میکنم از اینکه گوگل سایت شما رو پیشنهاد میده یه قوت قلبی میگیرم چون میدونم خیلی خووووب توضیح دادید
تشکر از محتواهای خوب و مفید همراه با آموزش کاملتون
متشکرم امیر
این پیغامهای شما مایه دلگرمی منه
سلام و خداقوت. ممنونم بابت مطالب خوب و مفیدتون. عالی بود. فقط من اخر کا چندجا به ارور خوردم، از گت هاب هم هر کاری می کنم نمی تونم کدها رو دانلود کنم، راهنمایی کنید باید کدکامل پروژه رو از کجا وچطوری دانلود کنم؟ ممنون
سلام استاد شما دکمه نمایش افراد رو داخل آموزش قرار ندادید لطفا برسی کنید متشکرم
سلام
ممنون از آموزش خوبتون
اگر لطف کنید و من رو راهنمایی بفرمایید ممنون میشم.
اگر من بخوام برنامه ای بنویسم که در یک جدول اطلاعات فردی بیماران ذخیره بشه و در جدول دیگه اطلاعات مربوط به معاینات اونها در روزهای مختلف و بعد این اطلاعات فراخوانی وپردازش بشن مثلا نمودار فشار خون بیمار در سه نوبت ویزیت رسم بشه ، آیا منبع آموزشی برای نوشتن این برنامه سراغ دارین که به من معرفی کنید؟
ممنون