آموزش اندروید-فصل ۲۵: پایگاه داده در اندروید (قسمت سوم)

در دو مطلب قبلی (اینجا و اینجا) از دیتابیس در اندروید حرف زدیم و با کلیات آن آشنا شدیم ولی هنوز از پایگاه داده در برنامه‌ای به صورت واقعی استفاده نکردیم. حالا در این مطلب می‌خواهیم از دیتابیس به صورت واقعی استفاده کنیم. در این مطلب می‌خواهیم اسامی افراد را وارد دیتابیس کنیم و بعد آن‌ها را در لیست‌ویو (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>

حالا لیست‌ویو را داریم، داده‌ها را داریم و قالبی که هر رکورد اطلاعاتی را قرار است نمایش دهد. فقط یک چیز کم داریم: چطور این‌ها همه را به هم وصل کنیم؟

تصویر زیر به درک شما کمک خواهد کرد را ببینید:

ch25_01_listview-adapter

(ببخشید که بهتر از این نتوانستم گرافیک مورد نظر را بسازم!)

فرایندی که سعی کردم با تصویر بالا به نمایش بکشم، این است:

۱- یک 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 نمایش داده می‌شود:

ch25-02-add-person-form

چندین اسم وارد کنید و بعد دکمه «لیست افراد» را بزنید تا به اکتیویتی PersonListView برویم:

ch25-03-person-listview-activity

هنوز با دیتابیس و لیست‌ویو کار زیاد داریم! اسمارت‌لب را دنبال کنید و در کانال تلگرام آن عضو شوید!

27 فکر می‌کنند “آموزش اندروید-فصل ۲۵: پایگاه داده در اندروید (قسمت سوم)

  1. رضا

    خیلی خوب و کامل بود… ممنون واقعا…
    اما یک سوال!
    من میخوام درون SQLite Expert Professional جدول بسازم و بعدش اونو در پوشه ی assets درون اندروید استدیو ذخیره کنم… ینی میخوام یه لیست ویو بسازم که مقدارشو از قبل درون جدول های دیتابیسم ریخته باشم و میخوا تو لیست ویوم اونارو نشون بدم.. اونوقت دقیقا باید چیکار کنم؟؟
    فکر نکنم با این آموزش بتونم این کارو بکنم و باید منتظر اموزش های بعدی تون باشم… درسته؟

    پاسخ
  2. omid

    با سلام و درود
    از زحمات شما تشکر می کنم
    من وقتی این پروژه رو دنبال کردم کلی error خورد
    لطفا کمکم کنید
    اگه میشه سورس کلی رو بزارید چون مطالب خیلی خیلی زیاد و پراکنده است
    با تشکر فراوان از شما

    پاسخ
    1. علی بهزادیان نژاد نویسنده

      سلام
      ارورها رو بنویسید تا ببینم چی بودن.
      کل کد رو تو گیت‌هاب می‌ذارم تا بتونید دانلود کنید.
      سپاسگزارم.

      پاسخ
  3. شهلا

    سلام. خسته نباشید واقعا.
    ممنونم بابت آموزش کاملتون. ازش خیلی استفاده کردم و واقعا برام مفید بود. امیدوارم بازم ادامه داشته باشه.

    پاسخ
  4. علیرضا*

    با سلام
    ممنون از آموزشتون ….
    من مطابق آموزش ها رفتم جلو اما در کلاس PersonListAdapter متاسفانه getId , getName و getFamily رو نمیشناسه …. دلیل این مشکل چیه ؟؟

    پاسخ
  5. ahmad

    سلام استاد
    واقعا توضیحاتتون عالی بود
    من برنامه رو از روی گیت ها نوشتم و ب نتیجه هم رسیدم فقط اینکه نگفته بودید چطور اطلاعات رو از دیتابیس پاک کنیم یا ویرایش کنیم!!!!
    اگه میشه این قسمت ها رو هم بهش اضافه کنید
    ممنون

    پاسخ
  6. ahmad

    ایا دستورات مربوط به برنامه فرایاد رو هم توضیح دادین
    اگه دادین از کجا میتونیم دانلودش کنیم؟؟؟
    با تشکر

    پاسخ
  7. سامان

    دوست عزیز و دوستان عزیز
    با سلام
    ابتدا از زحمات این دوست عزیز کمال تشکر را دارم
    دوم اینکه یک جای برنامه ایراد داره که باعث خطا دادن در هنگام اجرا میشه لطفا آن را اصلاح کنید
    در قسمت “شکل کامل کلاس PersonListAdapter این است:”
    دستور : private ArrayList persons; ایراد دارد و باید به صورت زیر باشد :
    private ArrayList persons;
    بازم از شما متشکرم

    پاسخ
  8. شاهرخ

    سلام
    مرسی از آموزش خوبتون…
    به یه مشکل بر خوردم، getId و getName و getFamily رو نمیتونم داخل کلاس PersonListAdapter فراخوانی کنم…

    ممنون میشم کمک کنید

    پاسخ
  9. Mr.mousapour

    سلام متشکرم از شما بابت آموزش خوب و کاربردیتون
    فقط بخش ویرایش حذف رو نگفتین !!!!
    اگه این دو بخش رو هم بگین من ممنون میشم ازتون

    پاسخ
  10. طاها

    سلام ممنون از آموزش های خوبتون ببخشید من میخوام یک فرم login درست کنم که پسورد و رمز رو اعتبار سنجی کنه اینکارو چگونه باید انجام بدم ؟

    پاسخ
  11. amir

    وقتی به سوال یا مشکلی میخورم و سرچ میکنم از اینکه گوگل سایت شما رو پیشنهاد میده یه قوت قلبی میگیرم چون میدونم خیلی خووووب توضیح دادید
    تشکر از محتواهای خوب و مفید همراه با آموزش کاملتون

    پاسخ
  12. محمد

    سلام و خداقوت. ممنونم بابت مطالب خوب و مفیدتون. عالی بود. فقط من اخر کا چندجا به ارور خوردم، از گت هاب هم هر کاری می کنم نمی تونم کدها رو دانلود کنم، راهنمایی کنید باید کدکامل پروژه رو از کجا وچطوری دانلود کنم؟ ممنون

    پاسخ
  13. وحید

    سلام
    ممنون از آموزش خوبتون
    اگر لطف کنید و من رو راهنمایی بفرمایید ممنون میشم.
    اگر من بخوام برنامه ای بنویسم که در یک جدول اطلاعات فردی بیماران ذخیره بشه و در جدول دیگه اطلاعات مربوط به معاینات اونها در روزهای مختلف و بعد این اطلاعات فراخوانی وپردازش بشن مثلا نمودار فشار خون بیمار در سه نوبت ویزیت رسم بشه ، آیا منبع آموزشی برای نوشتن این برنامه سراغ دارین که به من معرفی کنید؟
    ممنون

    پاسخ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *