أرشيف لـ24 مارس, 2013

28 – حفظ المعطيات ضمن قاعدة المعطيات SQL

ملاحظة : الموضوع عبارة عن حلقة من حلقات سلسلة برمجة وتطوير اندرويد Android Development

اندرويد : حفظ المعطيات ضمن قاعدة المعطيات SQL  

السلام عليكم ورحمة الله وبركاته

سوف نتعلم ضمن هذا الدرس الامور التالية :

  • تعريف Schema و Contract
  • تعريف قاعدة معطيات باستخدام SQL Helper
  • وضع المعلومات ضمن قاعدة المعطيات put information into a Database
  • قراءة المعلومات من قاعدة المعطيات Read information from a Database
  • حذف المعلومات من قاعدة المعطيات Delete Information from a Database
  • تحديث قاعدة المعطيات Update a Database

تعتبر عملية تخزين المعطيات ضمن قاعدة المعطيات عملية مناسبة للمعطيات المتكررة والمهيكلة, مثل معلومات الاتصال.

نفترض من هذا الدرس بأن على المام ومعرفة بقواعد المعطيات SQL بشكل عام , ويساعدك على البدء بقواعد المعطيات SQLite على اندرويد.

الواجهات البرمجية API التي سوفت تحتاج لاستخدامها على اندرويد متوافرة ضمن حزمة android.database.sqlite.

تعريف Scheme  و Contract

تعتبر ال schema احد اهم الامور الاساسية ضمن قواعد معطيات SQL: وهي عبارة عن تصريح رسمي عن كيفية تنظيم قاعدة المعطيات.

تظهر وتنعكس السكيما Schema بواسطة عبارة SQL التي سوف تستخدمها لإنشاء قاعدة المعطيات.

قد تجد بأنه من المفيد ان تقوم بإنشاء صف مرافق companion class ويعرف ب Contract class.والذي يقوم بشكل صريح بتحديد تنسيق السكيما بطريقة ممنهجة وبسيطة.

Contract class عبارة عن container للثوابت التي تعرف اسماء خاصة ب URLs, الجداول tables, والاعمدة columns.
يمكنك صف contract من استخدام نفس الثوابت عبر كل الصفوف ضمن نفس الحزمة.هذا يمكنك من تغيير اسم العمود في مكان واحد وسوف ينتقل التغيير عبر كامل الكود.

من احد الطرق الجيدة لتنظيم صف contract تكمن في وضع التعاريف العامة على مستوى قاعدة المعطيات كلها ضمن مستوى الجذر من الصفroot level of the class. ومن ثم تنشأ الصف الداخلي لكل جدول مع اعمدته.

ملاحظة : عبر تحقيق الواجهةinterface  BaseColumns, فإنه يصبح بامكان صفوفك الداخلية ان ترث الحقل المفتاحي primary key field والذي يدعى _ID والذي تتوقع بعض صفوف اندرويد الداخلية (مثل cursor adaptors ) امتلاكه. هذا الحقل ليس مطلوب, ولكن هذا يستطيع مساعدة قاعدة معطياتك لان تعمل بشكل متجانس مع اطار عمل اندرويد.

على سبيل المثال, المقطع ادناه يعرف اسم جدول واسماء اعمده لجدول واحد.

public static abstract class FeedEntry implements BaseColumns {
public static final String TABLE_NAME = “entry”;
public static final String COLUMN_NAME_ENTRY_ID = “entryid”;
public static final String COLUMN_NAME_TITLE = “title”;
public static final String COLUMN_NAME_SUBTITLE = “subtitle”;

}

لكي تمنع أي شخص من ان يقوم باستنساخ contract class بشكل عرضي, نقوم بإعطائه باني فارغ.

// Prevents the FeedReaderContract class from being instantiated.
private FeedReaderContract() {}

انشاء قاعدة معطيات عبر استخدام SQL Helper

ما إن تقرر كيف ستبدو قاعدة معطياتك , فإنه يتوجب عليك عندها تحقيق implement التوابع التي تقوم بإنشاء وحفظ قاعدة المعطيات والجداول. فيما يلي بعض العبارات القياسية التي تقوم بإنشاء وحذف جدول:

 

private static final String TEXT_TYPE = ” TEXT”;
private static final String COMMA_SEP = “,”;
private static final String SQL_CREATE_ENTRIES =
“CREATE TABLE ” + FeedReaderContract.FeedEntry.TABLE_NAME + ” (” +
FeedReaderContract.FeedEntry._ID + ” INTEGER PRIMARY KEY,” +
FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
… // Any other options for the CREATE command
” )”;

private static final String SQL_DELETE_ENTRIES =
“DROP TABLE IF EXISTS ” + TABLE_NAME_ENTRIES;

بشكل مشابه للملفات التي نخزنها على تخزين الداخلي internal storage, فإن اندرويد يقوم بخزن قاعدة المعطيات التي تقوم بإنشائها على على مساحة قرص خاصة مرتبطة بتطبيقك. وتعتبر معطياتك آمنة, لأنه – وبشكل افتراضي- المكان الذي يتم حفظ قاعدة المعطيات فيه غير قابل للنفاذ من قبل بقية التطبيقات.

هنالك مجموعة مفيدة من واجهات التطبيقات البرمجية APIs متوافرة في صف SQLiteOpenHelper. عندما تستخدم هذا الصف للحصول على مرجع إلى قاعدة معطياتك,فإن النظام يقوم بإنجاز العمليات – التي من المحتمل أن تستغرق وقت طويل – الخاصة بإنشاء وتحديث قاعدة المطيات فقط عندما الحاجة وليس خلال مرحلة startup الخاصة بالتطبيق. كل ما أنت بحاجة لفعله هو استدعاء التابع getWritableDatabase()أو التابع getReadableDatabase().

 ملاحظة : لأن تلك العمليات قد تستغرق وقت طويل, تأكد من ان تقوم باستدعاء أي من التابعين getWritableDatabase() أو getReadableDatabase()في مجرى خلفي background thread, مثل ذلك الذي نحصل عليه مع AsyncTask  أو IntentService.

 لكي تستخدم SQLiteOpenHelper, قم بإنشاء صف فرعي يقوم ب override لتوابع الاستدعاء التالية  onCreate(),onUpgrade()و onOpen(). قد ترغب ايضا بالقيام بتنجيز Implemnet الواجهة التالية onDowngrade(), ولكن هذا العمل ليس مطلوبا او ضروريا.

على سبيل المثال, فيما يلي تحقيق implementation للصف SQLiteOpenHelper  والذي يستخدم بعض الاوامر الموضحة اعلاه:

public class FeedReaderDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.

public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = “FeedReader.db”;

public FeedReaderDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL(SQL_DELETE_ENTRIES);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
}

لكي تستطيع النفاذ إلى قاعدة المعطيات, قم باستنساخ الصف الفرعي SQLiteOpenHelper:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

وضع المعلومات ضمن قاعدة المعطيات Put Information into a Database

يتم ادخال المعلومات insert ضمن قاعدة المعطيات عبر تمرير غرض ContentValues  إلى التابع insert():

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content);

// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
FeedReaderContract.FeedEntry.TABLE_NAME,
FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE,
values);

إن اول معامل لتابع insert()عبارة عن اسم الجدول table name.اما المعامل الثاني فيزود باسم العمود الذي يستطيع اطار العمل ان يدخل فيه null في حال كان ContentValues  فارغة (في حال قمت باسناد القيمة في هذا المعامل إلى “null”, عندها لن يقوم اطار العمل باضافة السطر عندما لا يكون هنالك قيم).

قراءة المعلومات من قاعدة المعطيات Read Information form a Database

لكي تقرأ المعلومات من قاعدة المعطيات, استخدم تابع query(), مرر له خصايص الاختيار التي تريد والاعمدة المطلوبة.

يتم اعادة نتائج الاستعلام إلى غرض Cursor  :

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
FeedReaderContract.FeedEntry._ID,
FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE,
FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED,

};

// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + ” DESC”;

Cursor c = db.query(
FeedReaderContract.FeedEntry.TABLE_NAME,  // The table to query
projection,                               // The columns to return
selection,                                // The columns for the WHERE clause
selectionArgs,                            // The values for the WHERE clause
null,                                     // don’t group the rows
null,                                     // don’t filter by row groups
sortOrder                                 // The sort order
);

لكي تستطيع رؤية الاسطر ضمن cursor, بإمكانك استخدام احد توابع النتقل move methods  الخاصة بغرض Cursor,  والتي يتوجب عليك دوما استدعاءها قبل ان تبدأ بقراءة القيم.

بشكل عام يجب ان تبدأ باستدعاء التابع moveToFirst(), والذي يقوم بتوضيع “read position” على أول مدخل ضمن النتائج. وبالنسبة لكل سطر, بإمكانك قراءة قيم الاعمدة عبر استدعاء احد توابع get الخاصة ب Cursor, مثل getString()أو getLong().

بالنسبة لكل تابع من توابع ال get هذه, يجب ان تمرر دليل الموضع للعمود المطلوب index position , والذي بإمكانك الحصول عليه عبر استدعاء التابع getColumnIndex()أو التابع getColumnIndexOrThrow().

على سبيل المثال :

cursor.moveToFirst();
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
);

حذف معلومات من قاعدة المعطيات Delete Information form a Database

لحذف سطر من جدول, عليك ان تحدد خاصية الاختيار selection criteria التي تحدد ذلك السطر.

تزودنا API الخاصة بقاعدة المعطيات بآلية لإنشاء خاصية الاختيار selection criteria  والتي تحمينا من SQL injection.

تقسم تلك الآلية خصائص الاختيار selection specification إلى عبارة الاختيار selection clause و معاملات الاختيار selection arguments. تعرف العبارة selection clause الاعمدة التي نريد الاطلاع عليها, وايضا تمكنك من الجمع بين اختبارات الاعمدة.

المعاملات arguments عبارة عن القيم التي تريد الاختبار نسبة إليها والتي يتم الربط بينها وبين عبارة الاختيار clause.

وبما ان النتيجة لا تتم معاملتها بنفس الطريقة التي تتم فيها معاملة عبارة SQL النظامية, لذلك فهي منيعة ضد SQL injection.

// Define ‘where’ part of query.
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + ” LIKE ?”;
// Specify arguments in placeholder order.
String[] selelectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);

تحديث قاعدة المعطيات Update a Database

عندما ترغب بتعديل مجموعة جزئية من قيم قاعدة المعطيات, يجب عليك استخدام التابع update().

إن عملية التعديل تجمع بين ال syntax قيم المحتويات الخاصة بتابعinsert()  مع syntax الخاص ب where  للتابع delete().

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// New value for one column
ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the ID
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + ” LIKE ?”;
String[] selelectionArgs = { String.valueOf(rowId) };

int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);

وبذلك نكون قد انتهينا من تقديم شرح بسيط وموجز ومفيد عن حفظ المعطيات

وإلى لقاء جديد مع حلقة جديدة من هذه السلسلة

وإلى ذلك الحين استودعكم الله والسلام عليكم ورحمة الله وبركاته

المصطلح

الترجمة

Thread

مسرب أو مجرى

Implementation

تحقيق او تنجيز

class

صف

Advertisements

, , , , , , , , , , , , , , , , , , , , , ,

أضف تعليق

28 – حفظ الملفات Saving Files

ملاحظة : الموضوع عبارة عن حلقة من حلقات سلسلة برمجة وتطوير اندرويد Android Development

اندرويد : حفظ الملفات Saving Files

السلام عليكم ورحمة الله وبركاته

سوف نتعلم في هذا الدرس مايلي:

  • الاختيار بين التخزين الداخلي او الخارجي choose internal or external Storage
  • الحصول على الصلاحيات الازمة للتخزين الخارجي Obtain Permissions for External Storage
  • حفظ ملف على تخزين داخلي Save file on internal storage
  • حفظ ملف على تخزين خارجي Save file on External storage
  • الاستعلام عن المساحة الفارغة Query free space
  • حذف ملف Delete a File

يفضل الاطلاع على  موضوع “خيارات التخزين Storage Options”

يستخدم نظام اندرويد نظام ملفات file system  مشابه لنظام الملفات في النظم المكتبية او اية منصات عمل اخرى.

يوصف لنا هذا الدرس كيفية العمل مع نظام ملفات اندرويد بهدف قراءة وكتابة الملفات عبر استخدام واجهات الملفات File APIs.

إن غرض File  مناسب لقراءة وكتابة كميات كبيرة من المعطيات بترتيب ( البداية –إلى – النهاية) بدون تخطي الترتيب. على سبيل المثال, هذا الاسلوب مناسب فيما يخص ملفات الصور او أي شيء يتم تبادله عبر الانترنت.

يبين لنا هذا الدرس كيفية اتمام المهام الاساسية المتعلقة بالملفات ضمن تطبيقك.

يفترض الدرس بأن على اطلاع باساسيات نظام الملفات بلينوكس , بواجهات file input/output APIs ضمن java.io.

الاختيار بين التخزين الداخلي والتخزين الخارجي choose internal or external storage

تحوي كل اجهزة اندرويد على منطقتي تخزين خاصة بالملفات: “تخزين داخلي internal” و “تخزين خارجي External storage”.

جاءت هذه التسميات من الأيام الاولى لاندرويد, حيث كانت اغلب التجهيزات تقدم ذاكرة مستقرة مدمجة مع الجهاز (التخزين الداخلي ), بالاضافة إلى وسيط تخزين يمكن ازالته مثل “بطاقة الذاكرة الرقمية SD card” (التخزين الخارجي ).

قسمت بعض الاجهزة مساحة التخزين الدائم إلى موضعي “داخلي internal ” و “خارجي external” , وبذلك حتى في حال عدم وجود وسيط تخزين قابل للازالة , يكون لدينا مساحتي تخزين مختلفتين , ويكون ايضا سلوك واجهات التطبيقات البرمجية API واحدا سواء كان التخزين الخارجي قابل للازالة اولا.

القائمة التالية تخلص الحقائق حول كل من مساحتي التخزين:

 التخزين الداخلي Internal Storage

  • متاح دوما
  • الملفات التي تخزن هنا تكون متاحة فقط لتطبيقك بشكل افتراضي
  • عندما يزيل المستخدم تثبيت تطبيقك, يقوم النظام بازالة كل ملفات التطبيق من التخزين الداخلي.

يعتبر التخزين الداخلي افضل خيار عندما ترغب بالتاكد من عدم تمكن كل من التطبيقات الاخرى والمستخدم من النفاذ إلى ملفاتك.

التخزين الخارجي External storage

  • لا يكون متاح دوما , لأن المستخدم قد يشبكا التخزين الخارجي على شكل USB , وقد يزيله  في بعض الحالات من الجهاز.
  • يمكن قراءته من الوسط الخارجي word-readable , وبذلك فإن الملفات المحفوظة هنا يمكن قرائتها بشكل خارج عن سيطرتك.
  • عندما يقوم المستخدم بإزالة تثبيت تطبيقك, يقوم عندها النظام بازالة ملفات تطبيقك فقط في حال كنت قد حفظتهم ضمن مسار من مسارات getExternalFilesDir().

يعتبر التخزين الخارجي أفضل كان لحفظ الملفات التي لا يوجد قيود على النفاذ إليها , وللملفات التي ترغب بمشاركتها مع بقية التطبيقات أو تسمح للمستخدم بالنفاذ إليها بواسطة الحاسب.

تنويه: بالرغم من ان التطبيقات يتم تثبيتها على التخزين الداخلي internal storage بشكل افتراضي, ولكن بإمكانك ان تحدد من خاصية android:installLocationضمن ملف manifest مكان التثبيت , وبالتالي من الممكن تثبيت تطبيقك على التخزين الخارجي external storage.

يقدر المستخدمون جدا هذا الخيار عندما يكون حجم الا APK كبير جدا , ويكون لديهم مساحة تخزين خارجي اكبر من مساحة التخزين الداخلي. لمزيد من المعلومات بالامكان الاطلاع على App Install Location.

 الحصول على الصلاحيات اللازمة للتخزين الخارجي Obtain permissions for external storage

لكي تكتب ضمن التخزين الخارجي external storage , يتوجب عليك طلب صلاحية WRITE_EXTERNAL_STORAGEضمن ملف

manifest.

<manifest …>
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />

</manifest>

تنبيه : حاليا , تملك كل التطبيقات امكانية القراءة ضمن التخزين الخارجي بدون الحاجة إلى صلاحيات خاصة.

على كل الاحوال, هذا سوف يتغير في المستقبل القريب في الاصدارات المستقبلية.في حال احتاج تطبيقك للقراءة من التخزين الخارجي external storage ( بدون الكتابة عليه), عندها سوف تحتاج للتصريح عن الصلاحية READ_EXTERNAL_STORAGE. والآن , ولكي تضمن ان يعمل تطبيقك كما هو متوقع , يتوجب عليك التصريح عن هذه الصلاحية الآن , وقبل ان تاخذ التطويرات المستقبلية مكانها المزعوم.

 

<manifest …>
<uses-permission android:name=”android.permission.READ_EXTERNAL_STORAGE” />

</manifest>

على كل الاحوال, في حال استخدم تطبيقك صلاحية WRITE_EXTERNAL_STORAGE, عندها فإنه بشكل ضمني يملك الصلاحية للقراءة من التخزين الخارجي.

لا تحتاج اية صلاحيات لحفظ الملفات على الخزين الداخلي internal storage. يملك تطبيقك دوما الصلاحية لقراءة وكتابة الملفات ضمن مسار الخزين الداخلي لديه.

حفظ الملف على التخزين الداخلي save a File on internal storage

عندما تقوم بتخزين ملف على التخزين الداخلي , بإمكانك الحصول على المسار المناسب “كملف” عبر استدعاء احد التابعين التاليين:

getFilesDir() يعيد ملف File  يمثل المسار الداخلي لتطبيقك.

getCacheDir()يعيد ملف File  يمثل المسار الداخلي لملفات الكاش المؤقتة لتطبيقك. تأكد من حذف كل ملف ما ان يصبح غير ضروريا. لانه في حال وقع النظام في حالة بطىء اثناء عمله على التخزين , عندها فمن الممكن ان يحذف ملفات الكاش لديك بدون ان يقم بتنبيهك.

لإنشاء ملف جديد ضمن احد هذه المسارات, بإمكانك استخدام الباني File()وتمرر له الملف Fileالمزود من قبل احد التوابع المذكورة اعلاه والتي تحدد مسار التخزين الداخلي الخاص بك.

على سبيل المثال:

File file = new File(context.getFilesDir(), filename);

بشكل بديل , بإمكانك استدعاء التابع openFileOutput()للحصول على FileOutputStream  الذي يقوم بدوره بكتابة الملف ضمن مسارك الداخلي. على سبيل المثال, فيما يلي مثال لآلية كتابة بعض النصوص ضمن ملف:

String filename = “myfile”;
String string = “Hello world!”;
FileOutputStream outputStream;

try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(string.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}

او , في حال رغبت بحفظ بعض الملفات ضمن الكاش, يجب ان تستخدم التابع createTempFile()بدلا من ذلك. على سبيل المثال, يقوم التابع التالي باستخلاص اسم الملف من عنوان ما URL وينشأ ملف بنفس الاسم ضمن مسار الكاش الداخلي ضمن تطبيقك:

public File getTempFile(Context context, String url) {
File file;
try {
String fileName = Uri.parse(url).getLastPathSegment();
file = File.createTempFile(fileName, null, context.getCacheDir());
catch (IOException e) {
// Error while creating file
}
return file;
}

ملاحظة : يتم تحديد مسار التخزين الداخلي ضمن تطبيقك app’s internal directory من قبل اسم حزمة تطبيقك ضمن موقع محدد ضمن نظام ملفات اندرويد.

بشكل تقني, فإن اي تطبيق اخر يستطيع قراءة ملفاتك الداخلية في حال اسندت نمط الملف file mode إلى “قابل للقراءة” readable. على كل الاحوال, فإن بقية التطبيقات الاخرى تحتاج ايضا إلى معرفة اسم حزمة تطبيقك app package name واسم الملف file name.  لا تستطيع بقية التطلبيقات استعراض المسارات الداخلية , وكلك لا تملك إمكانية النفاذ بهدف القراءة او الكتابة مالم تصرح عن ذلك بشكل صريح عبر وضع الملفات قابلة للقراءة اوالكتابة.

اذن, مادامت تستخدم MODE_PRIVATE  لملفاتك فيما يخص التخزين الداخلي, عندها لا تستطيع التطبيقات الاخرى الوصول إليها.

حفظ الملف على التخزين الخارجي save a file on external storage

بما ان التخزين الخارجي ليس متاح دوما- مثل الحالة التي يشبك فيها المستخدم بطاقة الذاكرة SD card او يزيلها – لذلك يتوجب عليك دوما التحقق من ان التخزين الخارجي متاح دوما قبل النفاذ اليه.

بإمكانك الاستعلام عن حالة التخزين الخارجي عبر استدعاء التابع getExternalStorageState() .في حال كانت الحالة المعادة تساوي MEDIA_MOUNTED , عندها بإمكانك قراءة وكتابة ملفاتك.

على سبيل المثال, تعتبر التوابع التالية مفيدة في تحديد فيما اذا كان التخزين متاحا:

 

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}

بالرغم من التخزين الخارجي يمكن تعديله من قبل المستخدم ومن قبل التطبيقات الاخرى, لكن هنالك تصنيفين من الملفات التي تستطيع حفظها هنا:

ملفات عامة Public files:

وهي الملفات التي يجب ان تكون متاحة للتطبيقات الاخرى وللمستخدم.وعندما يقوم المتسخدم بإزالة تثبيت التطبيق, يجب ان تبقى هذه الملفات متاحة للمستخدم.

على سبيل المثال, الصور التي يتم التقاطها من قبل تطبيقك او الملفات التي يتم تحميلها.

ملفات خاصة Private files:

الملفات التي تعود بشكل قانوني إلى تطبيقك ويجب ان يتم حذفها عندما يقوم المستخدم بإزالة تثبيت تطبيقك.

بالرغم من انه يمكن الوصول إلى هذه الملفات – بشكل تقني – من قبل المستخدم وبقية التطبيقات الاخرى , وذلك بسبب كونها تقع ضمن التخزين الخارجي, ولكنها واقعيا لا تزود قيم للمستخدم خارج تطبيقك.

عندما يقوم المستخدم بإزالة تثبت تطبيقك, فإن النظام يقوم بحذف كل الملفات ضمن مسار الملفات الخارجي الخاصةapp’s External private directory  ضمن تطبيقك.

على سبيل المثال, الموارد الاضافية التي يتم تحميلها من قبل تطبيقك أو ملفات الوسائط المتعددة المؤقتة.

في حال رغبت بحفظ ملفات عامة ضمن التخزين الخارجي, استخدم التابع getExternalStoragePublicDirectory() للحصول على File  الذي يمثل المسار المناسب على التخزين الخارجي.

ياخذ هذا التابع معامل يحدد نوع الملف الذي ترغب بحفظه , وبذلك يصبح بإمكانك تصنيف الملفات بشكل منطقي مع بقية الملفات العامة, مثل DIRECTORY_MUSICأو DIRECTORY_PICTURESعلى سبيل المثال:

 public File getAlbumStorageDir(String albumName) {
// Get the directory for the user’s public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, “Directory not created”);
}
return file;
}

في حال رغبت بتخزين الملفات التي تعتبر خاصة بتطبيقك, بإمكانك الحصول على المسار المناسب عبر استدعاء وتمرر له اسم يشير إلى نوع المسار (في حال رغبت بذلك).

كل مسار يتم انشائه بهذه الطريقة تتم اضافته إلى المسار الاب الذي يغلف كل ملفات التخزين الخارجية الخاصة بتطبيقك, التي يقوم النظام بحذفها في حال قام المستخدم بإزالة تثبيت تطبيقك.

على سبيل المثال, فيما يلي تابع بإمكانك استخدامه لانشاء مسار لالبوم الصور الخاصة.

public File getAlbumStorageDir(Context context, String albumName) {
// Get the directory for the app’s private pictures directory.
File file = new File(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, “Directory not created”);
}
return file;
}

في حال لم يناسب أي من المسارات الفرعية المعرفة مسبقا ملفاتك, بإمكانك بدلا من ذلك استدعاء التابع getExternalFilesDir(), وتمرر له القيمة null. سوف يعيد هذا الاستدعاء المسار الجذر لمسار الملفات الخاصة الخاصة بتطبيقك على تخزين خارجي.

تذكر , بأن التابع getExternalFilesDir()ينشأ مسار ضمن المسار الذي يقوم المستخدم بحذفه عندما يقوم بازالة تثبيت تطبيقك. في حال كانت الملفات التي تحفظها يجب ان تبقى بعد ازالة المستخدم للتطبيق – مثل في حال كان التطبيق عبارة عن كاميرا , وقد المستخدم يرغب بالاحتفاظ بالصور – عندها يتوجب ان تستخدم بدلا من ذاك التابع التابع  getExternalStoragePublicDirectory().

بغض النظر فيما اذا كنت تستخدم التابع getExternalStoragePublicDirectory()فيما يخص الملفات المشتركة أو التابع getExternalFilesDir()فيما يخص الملفات الخاصة بتطبيقك, فإنه من المهم عندها استخدام اسماء ملفات من تلك المزودة من قبل ثوابت API  مثل DIRECTORY_PICTURES.تؤكد اسماء المسارات هذه بأن هذه الملفات تعامل بالشكل الائق والمطلوب من قبل النظام.

على سبيل المثال, الملفات المحفوظة ضمن DIRECTORY_RINGTONESيتم تصنيفها من قبل النظام على انها نغمات بدل من موسيقا.

الاستعلام عن المساحة الفارغة query free space

في حال كنت تعمل مسبقا عن مقدار المعطيات التي ترغب بحفظها, بإمكانك عندها ان تعرف فيما اذا كانت المساحة المتوفرة كافية , ولن تسبب IOException  , وذلك عبر استدعاء getFreeSpace()أو getTotalSpace().

تزودنا هذه التوابع بمقدار المساحة المتاحة الحالية , والمساحة الاجمالية ضمن حجم التخزين, بالترتيب.

تعتبر هذه المعلومات ايضا مفيدة بهدف تجنب تعبئة حجم التخزين بكمية اكبر من الحد الاعلى المسموح به.

 على كل الاحوال, لا يضمن لك النظام بأنه بإمكانك كتابة القدر من البايتات ذاته المصرح عنه ضمن التابع getFreeSpace()على انه متاح.

في حال كان الرقم المعاد عبارة عن بضعة MB اكثر من الحجم المطلوب من قبل معطياتك التي ترغب بحفظها, أو في حال كان نظام الملفات اقل ممتلأ بنسبة اقل بقليل من 90%, عندها فإن الوضع على الارحج امن لان تستمر. وإلا فإنه يفضل بأن لا تتابع علمية الكتابة ضمن التخزين.

ملاحظة : لا يتوجب عليك التحقق من المساحة الفارغة المتاحة قبل حفظ ملفاتك.

بإمكانك بدلا من ذلك محاولة كتابة الملف بشكل مباشر, ومن ثم تقوم ب catch ل IOException  في حال حدث خلل ما. قد تحتاج للقيام بذلك في حال لم تكن تعلم مسبقا ماهو مقدار المساحة التي قد تحتاجها. على سبيل المثال, في حال غيرت ترميز الملف قبل حفظه عبر تحويل صورة من نمط PNG إلى JPEG, عندها فلن تعلم مسبقا حجم الملف بعد التحويل.

حذف ملف Delete a file

يتوجب عليك دوما حذف الملفات التي لم تعد بحاجة لها.الطريقة المباشرة لحذف ملف هي ان تملك مرجع الملف المفتوح وتستدعي التابع delete()على ذاك المرجع.

  myFile.delete();

في حال كان الملف قد حفظ على تخزين داخلي, بإمكانك ايضا ان تسأل ال Context  بأن يجد لك الملف ويحذفه عبر استدعاء deleteFile():

myContext.deleteFile(fileName);

ملاحظة : عندما يقوم المستخدم بإزالة تثبيت تطبيقك, يقوم اندرويد بحذف مايلي:

  • ·         كل الملفات التي قمت بحفظها ضمن التخزين الداخلي
  • ·         كل الملفات التي قمت بحفظها على التخزين الخارجي باستخدام getExternalFilesDir().

على كل الاحوال, يتوجب عليك ان تقوم بحذف كل ملفات الكاش التي تم انشائها بواسطة التابع بشكل دائم, ويتوجب عليك ان تقوم بشكل نظامي بحذف بقية الملفات التي لم تعد بحاجة لها.

وعند هذا القدر نكتفي بهذه الحلقة ,وإلى لقاء قريب في حلقة قادمة تتحدث عن حفظ المعطيات ضمن قاعدة المعطيات

وإلى ذلك الحين استودعكم الله والسلام عليكم ورحمة الله وبركاته.

, , , , , , , , , , , , , , , , , , , , ,

أضف تعليق

27 – حفظ المعطيات ضمن مجموعات (مفتاح – قيمة ) Saving Key-Value Sets

ملاحظة : الموضوع عبارة عن حلقة من حلقات سلسلة برمجة وتطوير اندرويد Android Development

اندرويد : حفظ المعطيات ضمن مجموعات (مفتاح – قيمة ) Saving Key-Value Sets

السلام عليكم ورحمة الله وبركاته

سوف نتعلم في هذا الدرس مايلي :new prof

  • الحصول على مرجع لغرض SharedPreferences
  • الكتابة في التفضيلات المشتركة Write to Shared Preferences
  • القراءة من التفضيلات المشتركة Read from Shared Preferences

يفضل الاطلاع على موضوع “خيارات التخزين Storage Options” قبل البدء

في حال كان لديك مجموعة صغيرة نسبيا من الثنائيات (مفتاح – قيمة) (key-value) التي ترغب بحفظها, فإنه يتوجب عليك استخدام واجهات SharedPreferences APIs.

يشير غرض SharedPreferences  إلى ملف يحوي على ثنائيات (مفتاح – قيمة ) (key-value) ويزودنا هذا الغرض بطرق بسيطة سهلة لقراءة وكتابة هذه الثنائيات.

تتم ادراة كل ملف SharedPreferences  من قبل اطار العمل , ومن الممكن ان يكن خاص او مشترك.

يوضح لنا هذا الدرس كيفية استخدام SharedPreferences APIs لتخزين واستعادة القيم البسيطة.

ملاحظة : ان واجهات SharedPreferences APIsتستخدم فقط بهدف قراءة وكتابة الثنائيات (مفتاح- قيمة)ويجب الا يتم الخلط بينهم وبين واجهات Preference, والتي تساعدك على بناء واجهة مستخدم خاصة باعدادات تطبيقك app settings . لمزيد من المعلومات حول واجهات Preference APIs, بالامكان الاطلاع على الرابط التاليSettings.

الحصول على مرجع لغرض SharedPreferences

بإمكانك انشاء ملف تفضيلات مشتركة shared preference file او النفاذ إلى احد الملفات الموجودة عبر استدعاء احد التوابع التالية:

–          getSharedPreferences()–  استخدم هذا التابع في حال كنت ترغب في الحصول على عدة ملفات تفضيلات مشتركة , ويميز كل واحد منها عبر اسم, ويتحدد الاسم عبر المعامل الأول. بإمكانك استدعاء هذا التابع ضمن اي سياق Context  ضمن تطبيقك.

–          getPreferences()  – استخدم هذا التابع ضمن الفعالية في حال كنت تحتاج لاستخدام ملف تفضيلات مشتركة وحيد خاص بتلك الفعالية. وبما ان هذا التابع يستعيد ملف التفضيلات المشتركة الافتراضي التابع لهذه الفعالية, فإنك لست بحاجة لذكر اسم.

 

على سبيل المثال, يتم تنفيذ الكود التالي ضمن Fragment.حيث يقوم بالنفاذ إلى ملف التفضيلات المشتركة والمحدد بواسطة سلسلة الموارد المحرفية resource string  R.string.preference_file_key, ويفتح الملف باستخدام النمط الخاص private mode  وبذلك يصبح التطبيق متاحا فقط ضمن تطبيقك.

Context context = getActivity();
SharedPreferences sharedPref = context.getSharedPreferences(
getString(R.string.preference_file_key), Context.MODE_PRIVATE);

عندما تقوم بتسمية ملف التفضيلات المشتركة, يتوجب عليك استخدام اسم فريد ويشير إلى تطبيقك, مثل “com.example.myapp.PREFERENCE_FILE_KEY”.

أما في حال رغبت باستخدام ملف تفضيلات مشتركة وحيد لفعاليتك, فإنه بامكانك استخدام التابع getPreferences():

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

تنبيه : في حال انشأت ملف تفضيلات مشتركة باستخدام النمط MODE_WORLD_READABLE أو MODE_WORLD_READABLEعندها فإن اي تطبيق اخر يعلم معّرف الملف file identifier  يستطيع النفاذ إلى معطياتك.

الكتابة في ملف التفضيلات المشتركة Write to Shared Preferences

للكتابة بملف التفضيلات المشتركة , يجب انشاء SharedPreferences.Editorعبر استدعاء edit()على SharedPreferences.

وتقوم بعد ذلك بتمرير المفاتيح والقيم التي ترغب بكاتبتها عبر استخدام التوابع المناسبة مثل putInt() او putString(). ومن ثم تقوم باستدعاء التابع commit() لحفظ التعديلات. على سبيل المثال:

 

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(getString(R.string.saved_high_score), newHighScore);
editor.commit();

القراءة من ملف التفضيلات المشتركة Read form shared preferences file

لاستعادة القيم من ملف التفضيلات المشتركة , يتم استدعاء توابع مثل التابع getInt()أو التابع getString(), ونمرر لهم مفتاح القيمة المطلوبة, وبشكل خياري يمكن تمرير قيمة افتراضية ليتم اعادتها في حال لم يتواجد المفتاح.

على سبيل المثال:

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
int defaultValue = getResources().getInteger(R.string.saved_high_score_default);
long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);

وعند هذا النقطة  نكتفي بما تقدم من شرح حول حفظ المعطيات باستخدام ثنائيات (مفتاح – قيمة)

لنتطرق في الدرس القادم للحديث عن “حفظ الملفات Saving Files”

وإلى ذلك الحين استودعكم الله والسلام عليكم ورحمة الله وبركاته.

, , , , , , , , , , , , , , , , , ,

2 تعليقان

26 – حفظ المعطيات Saving Data

ملاحظة : الموضوع عبارة عن حلقة من حلقات سلسلة برمجة وتطوير اندرويد Android Development

اندرويد : حفظ المعطيات Saving Data 

السلام عليكم ورحمة الله وبركاته

المتطلبات

  • Android 1.6 (API Level 4)  أو أعلى
  • المام ومعرفة بالمجموعات التي تطابق بين (قيمة – مفتاح ) key-value collections
  • المام ومعرفة بواجهات Java file I/O
  • المام ومعرفة بقاعدة المعطيات SQL

يجب ايضا الاطلاع على المقال الذي يتحدث عن “خيارات التخزين ” Storage Options .

فيما يلي سنذكر لمحة موجزة عن “خيارات التخزين Storage Options ” ولكن يفضل الاطلاع على المقال كاملا:

خيارات التخزين :

يزودنا اندرويد بخيارات متعددة لحفظ معطيات التطبيق بشكل مستمر.

يعتمد اختيارك لطريقة التخزين على احتياجاتك , مثلا بحسب المعطيات :

 هل المعطيات خاصة بالتطبيق ؟ ام يمكن الوصول إليها من تطبيقات أخرى ( ومن المستخدم)؟ ويعتمد ايضا على الحجم الذي يتطلبه تخزين معطياتك.

فيما يلي خيارات التخزين المتاحة:

  1. Shared Preferences  (التفضيلات المشتركة)
    1. تخزن المعطيات الأولية ضمن ثنائيات (مفتاح – قيمة)( تستخدم لتخزين كميات محدودة وصغيرة من المعلومات)
  2. Internal Storage  (التخزين الداخلي )
    1. تخزن المعطيات الخاصة ضمن ذاكرة الجهاز device memory(خاصة بتطبيقك ولا يمكن الوصول إليها من قبل التطبيقات الاخرى , ولا يستطيع المستخدم الوصول إليها)
  3. External Storage  ( التخزين الخارجي)
    1. تخزين المعطيات العامةعلى تخزين خارجي مشترك( كل التطبيقات بإمكانها الكتابة والقراءة من التخزين الخارجي , وحتى بإمكان المستخدم حذف الملفات).
  4. SQLite Database
    1. تخزن المعطيات بشكل مهيكل ضمن قاعدة معطيات خاصة.(لا يمكن الوصول لقاعدة المعطيات من خارج تطبيقك).
  5. Network Connection (شبكة اتصال)
    1. تخزن المعطيات على الانترنت على مخدمك الخاص own network server

يزودنا اندرويد بطريقة تتيح لنا عرض معطياتنا الخاصة للتطبيقات الآخرى – وذلك عبر استخدام ما يدعى ب content provider.

Content provider  عبارةعن مكون خياري يتيح لنا خيار النفاذ (قراءة – كتابة) على معطيات التطبيق, ويعتمد طبعا على القيود التي تفرضها على معطياتك.

لمزيد من المعلومات حول Content Provider , بالامكان مراجعة التوثيق التالي الخاص به على الرابط Content Providers.

 ——————————————–

والآن نعود إلى موضوع حفظ المعطيات بعد هذه اللمحة المقتضبة عن خيارات التخزين

تحتاج اغلب تطبيقات اندريد إلى حفظ المعطيات, وحتى لو كانت اقتصرت عملية الحفظ على المعلومات الخاصة بحالة التطبيق, على سبيل المثال نحتاج إلى تخزين بعض المعلومات عند التابع onPause(), بحيث يتابع المستخدم تقدمه من حيث توقف من دون ان يفقد اي معلومات.

كما ان اغلب التطبيقات تحتاج ايضا الى حفظ اعدادات المستخدم,  كما انه يتوجب على بعض التطبيقات ان تدير كمية كبيرة من المعلومات ضمن ملفات وضمن قواعد معطياتdatabases .

تزودنا هذه السلسلة من الحلقات بالخيارات الاساسية حول تخزين المعطيات ضمن اندرويد, وتتضمن مايلي:

  1. حفظ المعطيات على شكل ثنائيات (مفتاح –قيمة ) وذلك فيما يتعلق بانماط المعطيات البسيطة وذلك ضمن ملف التفضيلات المشتركة.
  2. حفظ الملفات العشوائية ضمن نظام ملفات اندرويدAndroid’s file system .
  3. استخدام قواعد المعطيات المدارة من قبل SQLite.

سنتناول في الدروس القادمة الامور التالية :

  1. حفظ مجموعات (مفتاح – قيمة ) key-value sets
  2. نتعلم في هذا الدرس عن كيفية استخدام ملف التفضيلات المشتركة بهدف تخزين كميات صغيرة من المعلومات على شكل ثنائيات (مفتاح – قيمة) key-value pairs .
  3. حفظ الملفات saving files
  4. نتعلم في هذا الدرس عن طريقة تخزين الملف على شكل تتالي طويل من المعطيات التي غالبا ما تتم قرائتها بالترتيب.
  5. حفظ المعطيات ضمن قواعد معطيات SQL saving data in SQL Databases
  6. نتعلم في هذا الدرس عن كيفية استخدام قاعدة معطيات SQLite  بهدف قراءة وكتابة المعطيات المهيكلة فيها.

وإلى لقاء قريب في الدرس المقبل

وإلى ذلك الحين استودعكم الله والسلام عليكم روحمة الله وبركاته

, , , , , , , , , , , , , , , , , , , , , , , , ,

أضف تعليق

25 -اندرويد – Fragments: التواصل مع بقية ال Fragments

ملاحظة : الموضوع عبارة عن حلقة من حلقات سلسلة برمجة وتطوير اندرويد Android Development

اندرويد : Fragments: التواصل مع بقية ال Fragments

السلام عليكم ورحمة الله وبركاتهnew prof

سوف نتحدث في هذا الدرس عن آلية التواصل بين ال Fragments  مع الفعالية المستضيفة لها , وبين ال Fragments بين بعضهم البعض.

الدرس قيم جدا ويحوي رغم قصره على كم كبير من المعلومات المفيدة فيما يخص ال Fragments ارجو لكم الفائدة.

سوف نتعلم في هذا الدرس ما يلي :

  1. تعريف واجهة Define an Interface
  2. تحقيق الواجهة ( اي كتابة الكود اللازم لها)Implement the Interface
  3. نقل رسالة إلى ال Fragment , Deliver a Message to a Fragment

لكي تستطيع اعادة استخدام ال Fragments  التي تمثل مكونات واجهة المستخدم UI components  , يتوجب عليك بناء كل fragments على شكل كينونة مستقلة بشكل كامل, مكون وحدوي modular component تقوم بتعريف تنسيقهاتها بنفسها بالاضافة إلى سلوكها.

ما إن تقوم بتعريف هذه ال fragments  القابلة لاعادة الاستخدام , حتى يصبح باستطاعتك ارفاقهم مع اي فعالية activity  وربطهم مع منطق التطبيق لتحقق واجهة المستخدم المركبة المطلوبة.

غالبا ما تحتاج إلى ان تتواصل وتتفاعل ال Fragments  مع بعضها البعض, على سبيل المثال لتغيير المحتوى اعتمادا على احداث المستخدم.

تتم كل اساليب التواصل بين Fragments-to-Fragment من خلال ال Activity  المرفقين بها.

ولا يتوجب ابدا ان تتواصل ال Fragments مع بعضها البعض بشكل مباشر.

تعريف واجهة Define an Interface

لكي نمكن ال fragments  من التواصل مع فعاليتها,بإمكاننا ان نعرف واجهة Interface  ضمن صف ال Fragment ونقوم بتحقيق implement هذا الصف ضمن الفعالية.

تلتقط ال Fragment تحقيق الواجهة الخاصة بها ضمن مرحلة استدعاء التابع onAttach() من دورة حياة ال fragment  , ومن ثم يصبح باستطاعتها استدعاء توابع الواجهة لكي تتواصل مع الفعالية.

فيما يلي مثال عن طريقة التواصل بين ال Fragment والفعالية Activity :

public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}@Override
public void onAttach(Activity activity) {
super.onAttach(activity);// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ ” must implement OnHeadlineSelectedListener”);
}
}…
}

الآن اصبح بإمكان ال Fragment نقل الرسائل إلى الفعالية عبر استدعاء التابع onArticleSelected()  (أو اية تواقع اخرى ضمن الواجهة) باستخدام مستنسخ mCallback الخاص بالواجهة  OnHeadlineSelectedListener.

على سبيل المثال, يتم استدعاء التابع التالي الموجود ضمن ال fragment  عندما ينقر المستخدم على عناصر القائمة.

يستخدم ال fragment  واجهة الاستدعاء لينقل الأحداث إلى الفعالية الأب.

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}

تحقيق الواجهة Implement the Interface

لكي نستطيع استقبال استدعاءات الاحداث من ال fragment,  فإنه يتوجب على الفعالية activity التي تستضيف ال fragment ان تقوم بتحقيق Implement الواجهة المعرفة ضمن صف ال fragment.

على سبيل المثال, تقوم الفعالية التالية بتحقيق الواجهة المعرفة في ال fragment  ضمن المثال السابق.

 

public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
…public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
}

توصيل رسالة إلى ال Fragment , Deliver a Message to a Fragment

بإمكان الفعالية المستضيفة لل fragment ان تقوم بتوصيل رسائل إلى ال fragment عبر التقاط مستنسخ ال Fragment باستخدام التابع findFragmentById(), ومن ثم تقوم بشكل مباشر باستدعاء التوابع العامة لهذا ال Fragment.

على سبيل المثال, تخيل بأن الفعالية المبينة اعلاه قد تحتوي على fragment اخر يستخدم لعرض العنصر المحدد عبر المعطيات المعادة ضمن تابع الاستدعاء اعلاه. في هذه الحالة , بإمكان الفعالية تمرير المعلومات المستقبلة عبر تابع الاستدعاء إلى fragment  آخر والذي بدوره سيقوم بعرض العنصر المطلوب.

public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{

public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article

ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);

if (articleFrag != null) {
// If article frag is available, we’re in two-pane layout…

// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// Otherwise, we’re in the one-pane layout and must swap frags…

// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();
}
}
}

وعند هذه النقطة نكون قد انتهينا من شرح المثال البرمجي الذي استمر معنا على ما يقارب الثلاث حلقات.

وسنتوقف عند هذا القدر مع المعلومات حول ال Fragment  لنتحول إلى موضوع اخر مهم ألا وهو ” تخزين المعطيات” Saving Data
وإلى ذلك الحين استودعكم الله والسلام عليكم ورحمة  لله وبركاته.

المصطلح الترجمة

implement

تحقيق , اي كتابة الكود البرمجي اللازم

Interface

واجهة

Instance

مستنسخ

modular component

مكون وحدوي – ان جاز التعبير-

, , , , , , , , , , , , , , , , , ,

أضف تعليق

24 -اندرويد – Fragments – بناء واجهة مستخدم مرنة Building a Flexible UI

ملاحظة : الموضوع عبارة عن حلقة من حلقات سلسلة برمجة وتطوير اندرويد Android Development

اندرويد : Fragments: بناء واجهة مستخدم مرنة Building a Flexible UI

السلام عليكم ورحمة الله وبركاته

سوف نتعلم في هذا الدرس مايلي:new prof

  1. اضافة Fragment  إلى فعالية في زمن التنفيذ
  2. استبدال Fragment  ب Fragment  أخر

يفضل الاطلاع على مايلي :

  1. Fragments

بالامكان تحميل المثال عبر النقر على الرابط التالي : Download the sample

عندما تصمم تطبيقك لكي يدعم طيف واسع من قياسات الشاشات, فإنه بإمكانك عندها استخدام ال Fragments  الخاصين بك في عدة تنسيقات مختلفة وذلك لكي يحصل المستخدم على افضل النتائج مهما كان حجم شاشته.

على سبيل المثال, في الاجهزة اليدوية , handset ذات قياس الشاشة الصغير نسبيا , فإنه من الانسب عرض fragment واحد ضمن واجهة المستخدم الواحدة.

وبشكل مناقض تماما لما سبق, فإنك قد ترغب بإعداد مجموعة من fragments احدهم بجوار الاخر على الاجهزة ذات الشاشات الاعرض tablet, والتي تملك قياش شاشة اعرض يتسع لعرض كل هذه المعلومات للمستخدم.

Fragments

الشكل 1 : يبين two fragments ,معروضين ضمن اعدادات مختلفة وذلك بالنسبة لنفس الفعالية نسبةً إلى اجهزة ذات قياسات مختلفة.
ضمن الشاشاة الكبيرة , يتوضع ال fragments بجوار بعضهم البعض, ولكن على الاجهزة الاصغر, تتسع الشاشة إلى fragment وحيد فقد في نفس الوقت, وبالتالي في حال احتجنا إلى عرض ال fragment الآخر فانه سوف يستبدل ال fragment الأول.

يزودنا صف FragmentManager  بعدة توابع تسمح لنا بإضافة أو ازالة او تبديل fragments  ضمن فعالية ما وذلك اثناء زمن التنفيذ –طبعا الهدف من ذلك الحصول على واجهة مستخدم تفعالية.

اضافة Fragment  إلى فعالية في زمن التنفيذ

بدلا من تعريف fragment ضمن ملف تنسيق الفعالية التابع لها – كما تم الشرح في الدرس السابق- وذلك عبر استخدام عنصر <fragment>, بإمكانك اضافة fragment إلى فعالية ما اثناء زمن تنفيذ الفعالية. هذا الموضوع مفيد جدا في حال خططت لاستبدال ال fragment  اثناء دورة حياة الفعالية.

لكي تقوم بمناقلات , مثل تلك الهادفة إلى اضافة أو ازالة fragment , فأنت بحاجة لاستخدام FragmentManager  وذلك لكي تنشأ FragmentTransaction, والذي بدوره يزودنا ب API لاضافة او ازالة او استبدال وانجاز اي مناقلات اخرى خاصة ب fragments.

في حال سمحت فعاليتك لان تتم ازالة ال fragments  واستبدالها ب fragments  اخرى, فإنه يتوجب عليك تهيئة ال  Fragment(s) واضافتها للفعالية عبر التابع onCreate()الخاص بالفعالية.

هنالك قاعدة هامة اثناء التعامل مع ال fragments – وخصوصا ال fragments  التي تضاف إلى الفعالية في زمن التنفيذ- الا وهي :

كل fragment يجب ان يملك container View  ضمن التنسيق , وهو المكان حيث سيتوضع فيه تنسيق ال fragment.

التنسيق التالي عبارة عن تنسيق  عبارة عن تنسيق بديل للتنسيق الذي تم عرضه في الدرس السابق, والذي يظهر فقط fragment  واحد في آن واحد.

يحوي تنسيق الفعالية على FrameLayout  فارغ, والذي بدوره يتصرف على انه حاول container لل fragment, وذلك لكي نقوم باستبدال هذا ال fragment  الوحيد ب fragment  آخر.

لاحظ بأن اسم الملف هو نفسه اسم التنسيق في الدرس الماضي, ولكن مسار التنسيق لا يحوي على “large”, لذلك فإن هذا التنسيق فقط للشاشات الصغيرة , وذلك لان الشاشات الصغيرة لا
تتسع ل two fragments في نفس الوقت.

res/layout/news_articles.xml:

<FrameLayout xmlns:android=”http://schemas.android.com/apk/res/android&#8221;
android:id=”@+id/fragment_container”
android:layout_width=”match_parent”
android:layout_height=”match_parent” />

أما داخل الفعالية ,فيتم استدعاء التابع getSupportFragmentManager()وذلك لكي يحضر FragmentManager   باستخدام Support Library APIs . ومن ثم يستدعي التابع beginTransaction() لانشاء FragmentTransaction, ومن ثم يستدعي التابع add() لاضافة ال fragment.

بإمكانك القيام بعدة منقلات خاصة بال fragments لفعالية ما باستخدام نفس ال FragmentTransaction. عندما تكون جاهز لاحداث التغيرات, عندها يتوجب عليك استدعاء التابع commit().

فيما يلي مثال على كيفية اضافة fragment إلى التنسيق السابق

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);

// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {

// However, if we’re being restored from a previous state,
// then we don’t need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}

// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();

// In case this activity was started with special instructions from an Intent,
// pass the Intent’s extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());

// Add the fragment to the ‘fragment_container’ FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
}

وبما ان ال fragment قد تمت اضافته إلى  container   FrameLayout في زمن التنفيذ – بدل من تعريفه ضمن ملف تنسيق الفعالية عبر استخدام العنصر <fragment> – لذلك فإنه باستطاعة الفعالية ازالة ال fragment واستبداله ب fragment  اخر.

استبدال Fragment  ب Fragment  آخر

إن سيناريو واجرائية استبدال ال fragment  مشابه لسيناريو اضافة fragment  , ولكنها تتطلب استخدام التابع replace()بدل من التابع add().

لا تنسى بانه عندما تقوم بمناقلات ال fragment , مثل ازالة fragment  او استبدال واحد باخر, فإنه من الانسب ان تسمح للمستخدم بالرجوع والتراجع ” Undo” عن هذه التغيرات.

لكي تسمح للمستخدم بالتجول بشكل تراجعي خلال مناقلات ال fragment , يتوجب عليك استدعاء التابع addToBackStack()قبل تثبيت المناقلة FragmentTransaction.

ملاحظة : عندما تقوم بازالة او استبدال fragment  وتضيف المناقلة إلى المكدس المخصص لعملية التراجع backStack , عندها يتوقف ال fragment  الذي تتم ازالته (يتوقف ولا يهدم stopped but not destroyed).

في حال تجول المستخدم رجوعا بهدف استعادة ال fragment , عندها فإنه يقوم ب restarts من جديد.

في حال لم تقم باضافة المناقلة إلى مكدس التراجع back stack  , عندها سيتم تدمير ال fragment في حال تمت ازالته او استبداله.

مثال على استبدال fragment باخر:

// Create fragment and give it an argument specifying the article it should show
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

ياخذ التابع addToBackStack()معامل اضافي عبارة عن سلسلة محرفية , ويقوم هذا المعامل بتحديد اسم فريد يميّز هذه المناقلة. لا تحتاج بشكل عام لهذا الاسم إلا في حال كنت تخطط للقيام بعمليات متقدمة لى ال fragment باستخدام واجهات FragmentManager.BackStackEntry APIs.

لتحميل مثال هذا الدرس انقر هنا

 وإلى اللقاء في الحلقة القادمة من هذه السلسلة والتي تتحدث عن “التواصل بين بقية ال fragments”

وإلى ذلك الحين استودعكم الله والسلام عليكم ورحمة الله وبركاته.

 المصطلح  الترجمة

transactions

مناقلة

Stack

مكدس

fragment

fragment   سنبقيها كما هي حتى لا تتحول الترجمة إلى ترجمة مضللة

, , , , , , , , , , , , , , , , , , , , , , ,

أضف تعليق

23 -اندرويد – Fragments – إنشاء Fragments

ملاحظة : الموضوع عبارة عن حلقة من حلقات سلسلة برمجة وتطوير اندرويد Android Development

اندرويد : إنشاء ال  Fragments

السلام عليكم ورحمة الله وبركاته

سوف نتعلم ضمن هذا الدرس مايلي:

  • إنشاء صف Fragment
  • اضافة Fragment إلى فعالية activity عبر استخدام XML.

بإمكاننا تخيل ال fragment على انه عبارة عن جزء وحدوي modular section  ضمن فعالية ما, ويكون لهذا ال fragment  دورة حياته الخاصة به lifecycle, ويتلقى احداث الادخال الخاصة به input events , والتي بإمكاننا اضافتها او ازالتها اثناء عمل الفعالية (اي نوع من “الفعالية الجزئية sub activity” التي يمكن اعادة استخدامها ضمن فعاليات مختلفة).

يبين لنا هذا الدرس كيف بإمكاننا القيام ب “extend” لصف Fragmentعبر استخدام مكتبة الدعم Support Library بحيث يبقى تطبيقك متوافقا مع الاجهزة التي تشغل انظمة باصدارات قديمة مثل اصدار Android 1.6.

ملاحظة : في حال قررت لسبب او لآخر بأن الحد الادنى الذي يحتاجه تطبيقك من واجهة التطبيقات البرمجية API level هو 11 او اعلى , عندها فأنت لست بحاجة لاستخدام مكتبة الدعم Support Library وبإمكانك بدلا من ذلك استخدام صف Fragmentالموجود ضمن اطار العمل وبذلك سوف تستخدم APIs المترافقة معه.

ولكن ينبغي الانتباه إلى ان هذا الدرس يقوم بالتركيز على استخدام APIs الموجودة ضمن مكتبة الدعم Support Library, والتي بدورها تستخدم توقيع حزمة محدد , وقد تستخدم في بعض الاحيان اسماء مختلفة ل API عن تلك المتضمنة ضمن الاصدار المترافق مع منصة العمل.

fragments

إنشاء صف Fragment

لكي ننشأ fragment , يتوجب علينا وراثة الصف Fragment  , اي فعليا القيام ب extentd للصف Fragment,ومن ثم نقوم ب override للتوابع المتعلقة بدورة الحياة lifecycle , بحيث لنضع المنطق المناسب لتطبيقنا ضمنها, وذلك بشكل مشابه للطريقة التي نتعامل فيها مع صف Activity.

هنالك فرق وحيد عند إنشاء Fragment, ويكمن في انه يتوجب علينا عندها استخدام الاستدعاء onCreateView()لتحديد التنسيق layout.

في الواقع, هذا عبارة عن الاستدعاء الوحيد الذي تحتاجه لكي يعمل ال fragment.

على سبيل المثال , فيما يلي ادناه fragment بسيط يقوم بتحديد تنسيقه الخاص به.

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.ViewGroup;

public class ArticleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.article_view, container, false);
}
}

وبشكل مشابه لأي فعالية activity , فإنه يتوجب على ال fragment ان يقوم بتنجيز بقية الاستدعاءات الخاصة بدورة الحياة lifecycle callbacks والتي تمكنك من ادارة حالة ال fragment عندما تتم اضافتها او ازالتها من الفعالية, وكذلك عندما يتم نقل الفعالية بين حالات دورة حياة الفعاليةactivity lifecycle .

على سبيل المثال, عندما يتم استدعاء التابع onPause(), فإن كل fragment ضمن الفعالية تتلقى ايضا الاستدعاء onPause().

لمزيد من المعلومات حول دورة حياة ال fragment  وحول توابع الاستدعاء الخاصة بها , بإمكانكم مراجعة الرابط التالي Fragments.

اضافة Fragment إلى فعالية ما باستخدام XML

بما ان ال fragment  تتميز بأنها من مكونات واجهة المستخدم القابلة لاعادة الاستخدام, فإنه يجب ان يترافق كل مستنسخ من صف Fragment  مع الأب  FragmentActivity. بإمكانك تحقيق هذا الترافق والتلازم عبر تعريف كل fragment ضمن ملف التنسيق XML الخاص بالفعالية.

ملاحظة : FragmentActivityعبارة عن فعالية خاصة مزودة من قبل مكتبة الدعم للتعامل مع ال fragments وذلك على اصدارات النظم القديمة , وتحديدا الاقدم من API level 11. في حال كان اقدم اصدار للنظام مدعوم من قبل الجهاز هو API level 11 أو اعلى, عندها فإنه بالامكان استخدام Activityالاعتيادية.

فيما يلي مثال على ملف التنسيق الذي يقوم باضافة اثنين من ال fragments  إلى فعالية ما , وذلك عندما يتم اعتبار شاشة الجهاز “كبيرة” large ( ويتم ذلك عبر اضافة موصف large إلى اسم المسار الخاص بالتنسيق كما هو موضح ادناه).

res/layout-large/news_articles.xml:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android&#8221;
android:orientation=”horizontal”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”>

<fragment android:name=”com.example.android.fragments.HeadlinesFragment”
android:id=”@+id/headlines_fragment”
android:layout_weight=”1″
android:layout_width=”0dp”
android:layout_height=”match_parent” />

<fragment android:name=”com.example.android.fragments.ArticleFragment”
android:id=”@+id/article_fragment”
android:layout_weight=”2″
android:layout_width=”0dp”
android:layout_height=”match_parent” />

</LinearLayout>

ملاحظة : لمزيد من المعلومات حول نشاء التنسيقات الخاصة باحجام الشاشات المختلفة بإمكانكم الرجوع إلى الرابط Supporting Different Screen Sizes.

فيما يلي ادناه ترميز يبين لنا فعالية activity  تقوم بإضافة هذا التنسيق :

 import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
}
}

ملاحظة : عندما تقوم بإضافة fragment  إلى تنسيق فعالية عبر تعريف ال fragment  ضمن ملف تنسيق XML, فإنك لن تستطيع ان تقوم بإزالة ال fragment في زمن التنفيذ.

في حال كنت تخطط للتبديل في عرض ال fragments اثناء تفاعل المستخدم مع التطبيق, فإنه يتوجب عليك اضافة ال fragment  إلى الفعالية عند بدء الفعالية كما سنوضح في الدرس القادم.

وإلى لقاء قريب في حلقة قادمة , وإلى ذلك الحين استودعكم الله والسلام عليكم ورحمة الله وبركاته.

المصطلح

الترجمة

module

وحدة

Encapsulate

يغلف

Implement

تنجيز – اي كتابة الرماز المناسب

Fragment

سوف نبقي على المصطلح كما هو حتى لا تتحول الترجمة إلى ترجمة مضللة.

, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

أضف تعليق