مقدّمة
عند زيارتك لموقع ما، وليكن موقع برمج على سبيل المثال (وذلك من خلال العنوان www.barmej.com)، فإنَّ متصفِّحك، سواء أكان Mozilla Firefox، أو Google Chrome، أو Safari، أو غيرهم، سيقوم بطلب ملفّات صفحة الواجهة (Homepage) الخاصّة بالموقع من خادم هذا الموقع، وهذه الملفّات هي عبارة عن ملفّات مكتوبة بلغة HTML، وهي لغة ترميز تُبيِّن كيفيّة ظهور الصفحة، بالإضافة إلى صور (كشعار موقع برمج) ومقاطع فيديو وغير ذلك. أخيراً، يقوم المتصفِّح برسم الصفحة كما يجب أن تبدو على شاشة المستخدم.
إنَّ كل هذه العمليّة تحدث خلف الكواليس في متصفِّح الويب من خلال برنامج يُسمّى مُحرِّك التصميم (Browser engine)، وكل متصفِّح يستخدم مُحرِّك مُختلف عن الآخر، ومن أشهر هذه المحرِّكات هو محرِّك Blink المُستخدم في متصفِّح Google Chrome، ومتصفِّح Gecko المُستخدم في متصفِّح Firefox. إنَّ العمليّة التي ذكرناها هي عمليّة مُعقّدة جدّاً بحيث أنَّ عدد مُحرِّكات التصميم الموجودة حاليّاً والتي لازالت قيد الاستخدام يُمكِن عدُّها على أصابع اليد.
من أجل ذلك، فقد وفَّرت شركة جوجل لمطوّري تطبيقات نظام آندرويد آليّة بسيطة تُمكِّنهم من إدراج مُتصفِّح بسيط في تطبيقاتهم يمكن من خلاله زيارة المواقع وإظهار محتواها ببضع أسطر من الكود فقط، دون الحاجة للخوض في أي تفاصيل معقّدة. ويُمكن الوصول إلى هذا المتصفِّح من خلال عنصر WebView.
إدراج عنصر WebView
قبل أن نتمكَّن من استخدام عنصر WebView في تطبيقنا، يجب علينا تفعيل صلاحيّات استخدام الإنترنت من خلال إضافة السطر التالي إلى ملفّ الManifest في المشروع، وتحديداً في وسم <manifest>:
1 2 3 4 |
<manifest> <uses-permission android:name="android.permission.INTERNET" /> ... </manifest> |
الآن يمكننا إضافة عنصر WebView إلى تطبيقنا من خلال مُصمِّم برنامج Android Studio، ويمكن إيجاد العنصر في تصنيف Widgets:
بحيث سيصبح ملفّ الXML الخاصّ بواجهتنا كما يلي:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <WebView android:id="@+id/myWebView" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.constraint.ConstraintLayout> |
زيارة موقع
للانتقال إلى موقع معيَّن في الWebView، نقوم باستخدام دالّة ()loadUrl الموجودة في كلاس WebView. ولكن يجب علينا أوّلاً أن نقوم بتعريف كائن (Object) ليشير إلى عنصر WebView في الواجهة حتّى نتمكَّن من الوصول إليه برمجيّاً، فنقوم بتعريف كائن من نوع WebView، ومن ثمَّ نستدعي دالّة ()findViewById في ()onCreate لوصل العنصر بهذا الكائن فور بدء التطبيق، فيصبح الكود في ملفّ MainActivity.java كالتالي:
1 2 3 4 5 6 7 8 |
WebView myWebView_ref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myWebView_ref = findViewById(R.id.myWebView); |
الآن أصبح بإمكاننا الوصول إلى عنصر myWebView واستدعاء الدالّة ()loadUrl، وهذه الدالّة تأخذ معاملاً واحداً وهو عنوان الموقع المُراد زيارته، فلزيارة موقع برمج، نقوم بكتابة التالي:
1 2 3 4 5 6 7 8 9 10 11 |
WebView myWebView_ref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myWebView_ref = findViewById(R.id.myWebView); myWebView_ref.loadUrl("http://www.barmej.com"); } |
ولكن ستظهر لنا مشكلة هنا وهي أنَّ التطبيق سيقوم باستدعاء متصفِّح الويب الموجود في الجهاز لفتح الموقع، ولن يتم فتح الموقع في التطبيق نفسه، وسبب ذلك هو أنَّ عنصر WebView الذي قمنا بتعريفه لا يحتوي على كائن WebViewClient الذي يمكّنه من عرض المواقع، لذلك، يجب علينا استدعاء دالّة ()setWebViewClient قبل دالّة ()loadUrl لنتمكَّن من زيارة المواقع من خلال عنصر WebView، ونقوم باستدعاؤه كالتالي:
1 2 3 4 5 6 7 8 9 10 11 12 |
WebView myWebView_ref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myWebView_ref = findViewById(R.id.myWebView); myWebView_ref.setWebViewClient(new WebViewClient()); myWebView_ref.loadUrl("http://www.barmej.com"); } |
الآن عند تجربتنا للتطبيق، سيتم زيارة موقع برمج كما في الصورة التالية:
التنقُّل بين الصفحات
يمكننا استخدام الدالّتين ()goBack و ()goForward للرجوع للصفحة السابقة أو العودة للصفحة اللاحقة، ولكن هاتين الدالّتين لن تكونا ذات أهميّة إن لم نربطهما بأزرار أو وسيلة لتفعيلهما من قِبَل المستخدم، لذلك، فسنقوم بإضافة زرّين في أسفل الواجهة أحدهما للرجوع والآخر للتقدُّم. يمكننا استخدام LinearLayout أفقيّة لاحتواء الزرّين، بحيث تظهر واجهتنا كما في الصورة:
ما يلي هو كود XML الخاص بالواجهة:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <WebView android:id="@+id/myWebView" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/buttons" app:layout_constraintTop_toTopOf="parent"> </WebView> <LinearLayout android:id="@+id/buttons" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="parent"> <Button android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="الرجوع" /> <Button android:id="@+id/forward" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="التقدُّم" /> </LinearLayout> </android.support.constraint.ConstraintLayout> |
الآن لنقم بتعريف الزرّين في كلاس MainActivity باستخدام ()findViewById:
1 2 3 4 5 6 7 8 9 10 11 12 |
WebView myWebView_ref; Button back_ref; Button forward_ref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); back_ref = findViewById(R.id.back); forward_ref = findViewById(R.id.forward); |
الآن سنقوم بتعريف onClickListener لكلا الزرّين حتّى نتتبَّع حالة كل زرّ، وعند الضغط على زرّ العودة إلى الخلف، نقوم باستدعاء دالّة ()goBack لنرجع للصفحة السابقة، ونفس الشيء في حالة زرّ التقدُّم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
back_ref = findViewById(R.id.back); forward_ref = findViewById(R.id.forward); myWebView_ref = findViewById(R.id.myWebView); myWebView_ref.setWebViewClient(new WebViewClient()); back_ref.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myWebView_ref.goBack(); } }); forward_ref.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myWebView_ref.goForward(); } }); myWebView_ref.loadUrl("http://www.barmej.com"); |
بذلك أصبح بإمكاننا الرجوع للخلف أو التقدُّم من خلال الزرّين.
إضافة حقل للعنوان
بإمكاننا تحسين المشروع البسيط من خلال إضافة حقل لكتابة عناوين المواقع (Address Bar)، وسنحتاج لذلك عنصر EditText أعلى الزرّين السابقين، بالإضافة إلى زرّ جديد لإتمام كتابة العنوان (Go button)، وبإمكاننا وضع كلا العنصرين في LinearLayout أفقيّة. ستصبح الواجهة كالتالي:
ما يلي كود XML الخاصّ بالواجهة:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <WebView android:id="@+id/myWebView" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/addressBar" app:layout_constraintTop_toTopOf="parent"> </WebView> <LinearLayout android:id="@+id/buttons" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="parent"> <Button android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="الرجوع" /> <Button android:id="@+id/forward" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="التقدُّم" /> </LinearLayout> <LinearLayout android:id="@+id/addressBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="horizontal" android:weightSum="10" app:layout_constraintBottom_toTopOf="@+id/buttons" tools:layout_editor_absoluteX="59dp"> <EditText android:id="@+id/address" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="9" android:ems="10" android:inputType="textUri" android:text="أدخل العنوان هنا" /> <Button android:id="@+id/go" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" android:text="GO" /> </LinearLayout> </android.support.constraint.ConstraintLayout> |
الآن سنقوم بتعريف كِلا العنصرين في ملفّ MainActivity.java من خلال دالّة ()findViewById:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
EditText address_ref; Button go_ref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); back_ref = findViewById(R.id.back); forward_ref = findViewById(R.id.forward); address_ref = findViewById(R.id.address); go_ref = findViewById(R.id.go); |
وسنقوم بتعريف OnClickListener لزرّ GO ونجعله يقوم باستدعاء دالّة ()loadUrl لزيارة الموقع الموجود عنوانه في عنصر EditText:
1 2 3 4 5 6 |
go_ref.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myWebView_ref.loadUrl(address_ref.getText().toString()); } }); |
فقد أصبح الكود كاملاً كما يلي:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
public class MainActivity extends AppCompatActivity { WebView myWebView_ref; Button back_ref; Button forward_ref; EditText address_ref; Button go_ref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); back_ref = findViewById(R.id.back); forward_ref = findViewById(R.id.forward); address_ref = findViewById(R.id.address); go_ref = findViewById(R.id.go); myWebView_ref = findViewById(R.id.myWebView); myWebView_ref.setWebViewClient(new WebViewClient()); go_ref.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myWebView_ref.loadUrl(address_ref.getText().toString()); } }); back_ref.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myWebView_ref.goBack(); } }); forward_ref.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myWebView_ref.goForward(); } }); myWebView_ref.loadUrl("http://www.barmej.com"); } } |
يمكنك تجربة التطبيق وكتابة عنوان موقع معيَّن وزيارته، ولا تنسى سبق العنوان بعبارة “//:http” حتّى نُحدِّد البروتوكول المُستخدم.
بذلك نكون قد قمنا بعمل متصفِّح ويب بسيط باستخدام عنصر WebView.
معالجة كود HTML بشكل مباشر
يتيح عنصر WebView إمكانيّة معالجة كود مكتوب بلغة HTML بشكل مباشر وإظهار محتوى الصفحة الناتجة على الشاشة، ويتم ذلك من خلال دالّة ()loadData كالآتي:
1 2 3 |
String htmlCode = "<html><body>أهلا بك في موقع برمج</body></html>"; myWebView_ref.loadData(htmlCode, "text/html", null); |