في هذا البرنامج التعليمي، سنناقش نوايا Android وتنفيذها باستخدام Kotlin في تطبيقنا.
ما الذي ستتعلمه؟
- ما هي النوايا؟
- أنواع النوايا؟
- استخدام النوايا بين الأنشطة
- إرسال البيانات باستخدام Android Intents
- استخدام Parcelable و Serializable لتمرير الكائنات
- إنشاء نوايا اختصارية
Android Intents
كما يقول الاسم، النية هي شيء يتم استخدامه لأداء بعض الإجراءات فيما يتعلق بتدفق تطبيق Android. يمكن استخدام النوايا ل:
- بدء نشاط جديد وتمرير بعض البيانات.
- بدء Fragments/التواصل بين الفراغات.
- بدء/إنهاء الخدمة.
- بدء الأنشطة من مستقبل البث
في هذا البرنامج التعليمي، سننظر بشكل رئيسي إلى النوايا للتعامل مع الأنشطة. تتكون تعريف النية بشكل رئيسي من مثيل للنشاط الحالي. نحدد اسم المكون الذي يمكن أن يكون: الاسم الكامل المؤهل للفئة للنشاط المراد استدعاؤه. هذا النوع من النية هو نية صريحة. إجراء مثل عنوان URL أو رقم هاتف أو موقع. سيتم عرض جميع التطبيقات المتاحة من هذه الأنواع. يندرج ذلك تحت فئة النية الضمنية. في Kotlin، يتبع الطريقة التالية لإنشاء نشاط.
val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)
startActivity
ستضيف OtherActivity
على مكدس الأنشطة وتطلقها. كيف يدرك تطبيقنا أي نشاط يتم استدعاؤه أولا؟ في AndroidManifest.xml نقوم بتعيين عامل التصفية النائية مع الإجراء android.intent.action.MAIN
والفئة android.intent.category.LAUNCHER
على أول نشاط يتم تشغيله عند فتح تطبيقنا. finish()
يستخدم لتدمير نشاط وإزالته من المكدس.
علامات النوايا
العلامات مثل الخيارات التي يمكن تعيينها على النوايا لتخصيص عملية الإطلاق. إذا بدأت نفس النشاط في كل مرة، سيتم إنشاء نسخة جديدة وإضافتها إلى مكدس الأنشطة. لمنع هذا، يمكنك استخدام العلامات: FLAG_ACTIVITY_SINGLE_TOP
– إذا تم تعيينها، لن يتم تشغيل النشاط إذا كان قيد التشغيل بالفعل في أعلى مكدس الأنشطة.
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
بالمثل، باستخدام علامة FLAT_ACTIVITY_CLEAR_TOP
لن يتم تشغيل نسخة أخرى من النشاط إذا كان موجودا بالفعل. ستقوم هذه العلامة بمسح جميع الأنشطة التي تسبق النشاط الذي تم استدعاؤه وتعيينه في أعلى المكدس.
تمرير البيانات من خلال النوايا
لتمرير البيانات إلى الأنشطة الجديدة، نستخدم أزواج المفاتيح والقيم داخل الدالة putExtra
، putStringArrayListExtra
وما إلى ذلك. تمرر putExtra عادة الأنواع الأساسية مثل Int، Float، Char، Double، Boolean، String بالإضافة إلى IntArray… الخ
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("keyString", "Androidly String data")
تكون حقول Extras هذه تحت الغطاء ملفوفة داخل كائن Bundle
الذي يحتوي في النهاية على جميع البيانات التي سيتم تمريرها. لاسترجاع البيانات في النشاط الآخر، نحتاج إلى استخدام خاصية extras
على bundles
. استرجاع البيانات في النشاط الجديد
val bundle: Bundle? = intent.extras
val string: String? = intent.getString("keyString")
val myArray: ArrayList<String>? = intent.getStringArrayList("myArray")
intent
، extras
يعادلان getIntent()
، getExtras()
في Java. قد استخدمنا نوعًا قابلًا للتعيين Bundle؟
لمنع حدوث NullPointerExceptions
عندما لا توجد بيانات. بالمثل، للبيانات التي تم جلبها باستخدام المفاتيح، قد استخدمنا الأنواع القابلة للتعيين لتجنب NPE التي يمكن أن تحدث عندما يكون المفتاح غير صحيح.
استخدام بيانات Parcelable وSerializable
في بعض الأحيان نحتاج إلى تمرير كائن كامل من نشاط إلى آخر. لا يمكن فعل ذلك ما لم نقم بتنفيذ واجهة Parcelable أو Serializable. الفرق بين Parcelable وSerializable
- واجهة Parcelable هي جزء من Android SDK. Serializable هي واجهة قياسية في Java.
- في Parcelable، تحتاج إلى تعيين جميع البيانات التي تحتاج إلى تمريرها في كائن Parcel وأيضاً تجاوز أساليب writeToParcel() وما إلى ذلك. أما في Serializable، فإن تنفيذ الواجهة يكفي لتمرير البيانات.
- Parcelable أسرع من Serializable.
إرسال بيانات Parcelable
تأتي Kotlin مع بعض التعليقات المفيدة لتوفيرنا من تجاوز طريقة writeToParcel() لتعيين البيانات على Parcelable. بدلاً من ذلك، يمكننا استخدام @Parcelize كما هو موضح أدناه:
@Parcelize
data class Student(
val name: String = "Anupam",
val age: Int = 24
) : Parcelable
ملاحظة: حاليًا في build.gradle الخاص بك، يجب عليك إضافة الكود التالي لعمل تعليق @Parcelize:
android {
androidExtensions {
experimental = true
}
//..
....
}
في نشاطك، قم بفعل الآتي:
val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)
إرسال بيانات Serializable
data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable
val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)
لنستخدم المعرفة أعلاه في مشروعنا في Android Studio.
هيكل المشروع
رمز التخطيط
الرمز الخاص بتخطيط activity_main.xml
يُظهر أدناه:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btnSimpleIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SIMPLE INTENT" />
<Button
android:id="@+id/btnSimpleIntentAndData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SIMPLE INTENT WITH DATA" />
<Button
android:id="@+id/btnParcelableIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Parcelable Intent" />
<Button
android:id="@+id/btnSerializableIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Serializable Intent" />
<Button
android:id="@+id/btnBrowserIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Browser Intent" />
<Button
android:id="@+id/btnMapsIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Maps Intent" />
<Button
android:id="@+id/btnGenericIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Generic Intent" />
</LinearLayout>
الرمز الخاص بتخطيط activity_other.xml
يُظهر أدناه:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Intent Data goes here" />
</LinearLayout>
رمز النشاط
الرمز الخاص بفئة MainActivity.kt يُظهر أدناه:
package net.androidly.androidlyintents
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_main.*
import java.io.Serializable
@Parcelize
data class Student(
val name: String = "Anupam",
val age: Int = 24
) : Parcelable
data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable
class MainActivity : AppCompatActivity(), View.OnClickListener {
fun Context.gotoClass(targetType: Class<*>) =
ComponentName(this, targetType)
fun Context.startActivity(f: Intent.() -> Unit): Unit =
Intent().apply(f).run(this::startActivity)
inline fun <reified T : Activity> Context.start(
noinline createIntent: Intent.() -> Unit = {}
) = startActivity {
component = gotoClass(T::class.java)
createIntent(this)
}
var arrayList = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnSimpleIntent.setOnClickListener(this)
btnSimpleIntentAndData.setOnClickListener(this)
btnParcelableIntent.setOnClickListener(this)
btnSerializableIntent.setOnClickListener(this)
btnBrowserIntent.setOnClickListener(this)
btnMapsIntent.setOnClickListener(this)
btnGenericIntent.setOnClickListener(this)
arrayList.add("Androidly")
arrayList.add("Android")
arrayList.add("Intents")
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnSimpleIntent -> {
val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)
}
R.id.btnSimpleIntentAndData -> {
val intent = Intent(this, OtherActivity::class.java)
with(intent)
{
putExtra("keyString", "Androidly String data")
putStringArrayListExtra("arrayList", arrayList)
putExtra("keyBoolean", true)
putExtra("keyFloat", 1.2f)
}
startActivity(intent)
}
R.id.btnParcelableIntent -> {
val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)
}
R.id.btnSerializableIntent -> {
val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)
}
R.id.btnBrowserIntent -> {
val url = "https://www.androidly.net"
val uri = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, uri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
}
}
R.id.btnMapsIntent -> {
val loc = "12.9538477,77.3507442"
val addressUri = Uri.parse("geo:0,0?q=" + loc)
val intent = Intent(Intent.ACTION_VIEW, addressUri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
}
}
else -> start<OtherActivity> {
putExtra("keyString", "Androidly Generic Intent")
}
}
}
}
في الرمز أعلاه، قمنا باستخدام الأزرار لكل نوع من النوايا (Intents). لقد استخدمنا تعبير with
في Kotlin لمنع تعيين البيانات عبر كائن النية intent
في كل مرة. بالإضافة إلى ذلك، قمنا بإنشاء ثلاث نيات مختلفة بصرف النظر عن تلك التي تم مناقشتها بالفعل. يُستخدم توجيه المتصفح لفتح عنوان URL الموجود في النية في تطبيق المتصفح. ويستخدم Intent(Intent.ACTION_VIEW، uri)
. تُستخدم النية المتعلقة بالموقع لفتح الموقع في تطبيق الخرائط. كلاهما نيات ضمنية. أخيرًا، قمنا باستخدام نية عامة نستخدم فيها وظائف التمديد وتعبيرات اللامبدا في Kotlin لإنشاء وظيفة مختصرة لفتح نية. لهذا الغرض، نستخدم الوظائف التالية:
fun Context.gotoClass(targetType: Class<*>) =
ComponentName(this, targetType)
fun Context.startActivity(createIntent: Intent.() -> Unit): Unit =
Intent().apply(createIntent).run(this::startActivity)
inline fun <reified T : Activity> Context.start(
noinline createIntent: Intent.() -> Unit = {}
) = startActivity {
component = gotoClass(T::class.java)
createIntent(this)
}
startActivity هو دالة تمديد تبحث عن دالة ترتيب عالية كمعلمة لها. بفضل هذا، يمكننا الآن إطلاق النوايا في عدد قليل من الأسطر كما في: start<OtherActivity>
الشيفرة لفئة OtherActivity.kt معطاة أدناه.
package net.androidly.androidlyintents
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_other.*
class OtherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_other)
val bundle: Bundle? = intent.extras
bundle?.let {
bundle.apply {
//Intent مع بيانات
val string: String? = getString("keyString")
textView.text = string
val myArray: ArrayList? = getStringArrayList("myArray")
showToast(message = "MyArrayList size:${myArray?.size}")
val arrayList: ArrayList? = getStringArrayList("arrayList")
showToast(message = "ArrayList size:${arrayList?.size}")
val float: Float? = bundle.get("keyFloat") as Float?
var boolean = bundle.get("boolean") as? Boolean
showToast(message = "Float data is:$float")
showToast(message = "Boolean data is:$boolean")
boolean = bundle.get("keyBoolean") as? Boolean
showToast(message = "Boolean correct key data is:$boolean")
}
bundle.apply {
//بيانات قابلة للتسلسل
val blog = getSerializable("blogData") as Blog?
if (blog != null) {
textView.text = "Blog name is ${blog?.name}. Year started: ${blog?.year}"
}
}
bundle.apply {
//بيانات Parcelable
val student: Student? = getParcelable("studentData")
if (student != null) {
textView.text = "Name is ${student?.name}. Age: ${student?.age}"
}
}
}
}
private fun showToast(context: Context = applicationContext, message: String, duration: Int = Toast.LENGTH_SHORT) {
if (!message.contains("null"))
Toast.makeText(context, message, duration).show()
}
}
لقد استخدمنا let
و apply
للتعامل مع أنواع nullable وتجنب عمل bundle.field في كل سطر. إخراج التطبيق أعلاه في العمل معروض أدناه: هذا ينهي هذا البرنامج التعليمي حول النوايا في Android بلغة Kotlin. يمكنك تحميل المشروع من الرابط أدناه.