إنشاء تطبيقات ويب في WebView

استخدِم WebView لتقديم تطبيق ويب أو صفحة ويب كجزء من تطبيق عميل. الفئة WebView هي إضافة لفئة View على Android، وتتيح لك عرض صفحات الويب كجزء من تنسيق نشاطك. ولا يشمل ميزات متصفح ويب مطور بالكامل، مثل عناصر التحكم في التنقل أو شريط العناوين. بشكل تلقائي، كل ما يفعله WebView هو عرض صفحة ويب.

يمكن أن تساعدك ميزة WebView في تقديم المعلومات التي قد تحتاج إلى تعديلها في تطبيقك، مثل اتفاقية المستخدم النهائي أو دليل المستخدم. داخل تطبيق Android، يمكنك إنشاء Activity يحتوي على WebView، ثم استخدامه لعرض المستند المستضاف على الإنترنت.

يمكن أن تساعدك "WebView" أيضًا عندما يقدّم تطبيقك بيانات للمستخدم والتي تتطلب اتصالاً بالإنترنت لاسترداد البيانات، مثل البريد الإلكتروني. في هذه الحالة، قد تجد أنّه من الأسهل إنشاء WebView في تطبيق Android الخاص بك ويعرض صفحة ويب تتضمّن جميع بيانات المستخدمين، بدلاً من تنفيذ طلب للشبكة، ثم تحليل البيانات وعرضها بتنسيق Android. يمكنك بدلاً من ذلك تصميم صفحة ويب مخصَّصة للأجهزة التي تعمل بنظام التشغيل Android، ثم تنفيذ علامة WebView في تطبيق Android الذي يُحمِّل صفحة الويب.

يشرح هذا المستند كيفية بدء استخدام WebView، وكيفية ربط JavaScript من صفحة الويب بالرمز من جهة العميل في تطبيق Android، وكيفية التعامل مع التنقل في الصفحات، وكيفية إدارة النوافذ عند استخدام WebView.

استخدام مكوّن WebView في إصدارات Android السابقة

لاستخدام إمكانات WebView الأحدث بأمان على الجهاز الذي يعمل عليه تطبيقك، أضِف مكتبة AndroidX Webkit. هذه مكتبة ثابتة يمكنك إضافتها إلى تطبيقك لاستخدام واجهات برمجة تطبيقات android.webkit غير المتوفّرة للإصدارات السابقة من النظام الأساسي.

أضِفها إلى ملف build.gradle على النحو التالي:

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}

رائع

dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

يمكنك استكشاف مثال WebView على GitHub لمزيد من التفاصيل.

إضافة WebView إلى تطبيقك

لإضافة WebView إلى تطبيقك، يمكنك تضمين العنصر <WebView> في تنسيق النشاط، أو ضبط نافذة Activity بأكملها على أنّها WebView في onCreate().

إضافة WebView في تنسيق النشاط

لإضافة WebView إلى تطبيقك بالتنسيق، أضِف الرمز التالي إلى ملف XML لتنسيق نشاطك:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

لتحميل صفحة ويب في WebView، استخدِم loadUrl()، كما هو موضّح في المثال التالي:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

إضافة WebView في onCreate()

لإضافة WebView إلى تطبيقك في طريقة onCreate() للنشاط بدلاً من ذلك، استخدِم منطقًا مشابهًا لما يلي:

Kotlin

val myWebView = WebView(activityContext)
setContentView(myWebView)

Java

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

بعد ذلك، حمِّل الصفحة:

Kotlin

myWebView.loadUrl("http://www.example.com")

Java

myWebView.loadUrl("https://www.example.com");

أو يمكنك تحميل عنوان URL من سلسلة HTML:

Kotlin

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Java

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

يجب أن يتصل التطبيق بالإنترنت. للاتصال بالإنترنت، اطلب إذن INTERNET في ملف البيان، كما هو موضّح في المثال التالي:

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

يمكنك تخصيص WebView من خلال تنفيذ أي من الإجراءات التالية:

  • تفعيل إتاحة وضع ملء الشاشة باستخدام WebChromeClient. وتُسمّى هذه الفئة أيضًا عندما يحتاج WebView إلى إذن لتغيير واجهة المستخدم للتطبيق المضيف، مثل إنشاء نوافذ أو إغلاقها أو إرسال مربّعات حوار JavaScript إلى المستخدم. ولمزيد من المعلومات عن تصحيح الأخطاء في هذا السياق، يمكنك الاطّلاع على تصحيح أخطاء تطبيقات الويب.
  • التعامل مع الأحداث التي تؤثّر في عرض المحتوى، مثل الأخطاء في عمليات إرسال النماذج أو التنقّل باستخدام WebViewClient يمكنك أيضًا استخدام هذه الفئة الفرعية لاعتراض عملية تحميل عناوين URL
  • تفعيل JavaScript عن طريق تعديل WebSettings.
  • استخدام JavaScript للوصول إلى كائنات إطار عمل Android التي تم إدخالها في WebView

استخدام JavaScript في WebView

إذا كانت صفحة الويب التي تريد تحميلها في WebView تستخدم JavaScript، يجب تفعيل JavaScript في WebView. بعد تفعيل JavaScript، يمكنك إنشاء واجهات بين رمز تطبيقك ورمز JavaScript.

تفعيل JavaScript

تكون JavaScript متوقفة تلقائيًا في WebView. يمكنك تفعيل هذه الميزة من خلال WebSettings المرفقة بـ WebView. يمكنك استرداد WebSettings باستخدام getSettings()، ثم تفعيل JavaScript من خلال setJavaScriptEnabled().

اطّلِع على المثال التالي:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

يتيح WebSettings الوصول إلى مجموعة متنوعة من الإعدادات الأخرى التي قد تفيدك. على سبيل المثال، إذا كنت تطوّر تطبيق ويب مصمّمًا خصيصًا لـ WebView في تطبيق Android، يمكنك تحديد سلسلة وكيل مستخدم مخصّصة باستخدام setUserAgentString()، ثم طلب البحث عن وكيل المستخدم المخصّص في صفحة الويب للتحقّق من أنّ العميل الذي يطلب صفحة الويب هو تطبيقك على Android.

ربط رمز JavaScript برمز Android

عند تطوير تطبيق ويب مصمّم خصيصًا لـ WebView في تطبيق Android لديك، يمكنك إنشاء واجهات بين رمز JavaScript ورمز Android من جهة العميل. على سبيل المثال، يمكن لرمز JavaScript استدعاء طريقة في رمز Android لعرض Dialog، بدلاً من استخدام دالة alert() في JavaScript.

لربط واجهة جديدة بين رمز JavaScript ورمز Android، استدعِ addJavascriptInterface()، واضبط له مثيل فئة لربطه بلغة JavaScript واسم واجهة يمكن لJavaScript استدعاءه للوصول إلى الصف.

على سبيل المثال، يمكنك تضمين الصف التالي في تطبيق Android:

Kotlin

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Java

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context. */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page. */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

في هذا المثال، تسمح الفئة WebAppInterface لصفحة الويب بإنشاء رسالة Toast باستخدام الطريقة showToast().

يمكنك ربط هذه الفئة بملف JavaScript الذي يتم تشغيله في WebView باستخدام addJavascriptInterface()، كما هو موضّح في المثال التالي:

Kotlin

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")

Java

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

يؤدّي هذا إلى إنشاء واجهة تُسمى Android لترميز JavaScript يتم تشغيلها في WebView. في هذه المرحلة، يمكن لتطبيق الويب الوصول إلى الصف "WebAppInterface". على سبيل المثال، إليك بعض نصوص HTML وJavaScript التي تُنشئ رسالة إعلامية باستخدام الواجهة الجديدة عندما ينقر المستخدم على أحد الأزرار:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

ليس هناك حاجة إلى إعداد واجهة Android من JavaScript. ويتيح WebView صفحتك على الويب تلقائيًا. لذلك، عندما ينقر المستخدم على الزر، تستخدم الدالة showAndroidToast() واجهة Android لاستدعاء طريقة WebAppInterface.showToast().

التعامل مع التنقل في الصفحة

عندما ينقر المستخدم على رابط من صفحة ويب في WebView، يشغِّل Android تلقائيًا تطبيقًا يعالج عناوين URL. ويتم عادةً فتح متصفّح الويب التلقائي وتحميل عنوان URL المقصود. ومع ذلك، يمكنك إلغاء هذا السلوك في WebView، وبالتالي يتم فتح الروابط ضمن WebView. يمكنك بعد ذلك السماح للمستخدم بالانتقال للخلف وللأمام من خلال سجلّ صفحات الويب الذي يحفظه WebView.

لفتح الروابط التي نقر عليها المستخدم، يمكنك تقديم WebViewClient لـ WebView باستخدام setWebViewClient(). يتم تحميل جميع الروابط التي ينقر عليها المستخدم في WebView. إذا أردت مزيدًا من التحكّم في مكان تحميل الرابط الذي تم النقر عليه، أنشِئ WebViewClient الخاص بك الذي يلغي طريقة shouldOverrideUrlLoading(). يفترض المثال التالي أن MyWebViewClient هي فئة داخلية من Activity.

Kotlin

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Java

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

بعد ذلك، عليك إنشاء مثيل لـ WebViewClient الجديدة هذه من أجل WebView:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

والآن عندما ينقر المستخدم على رابط، يستدعي النظام الطريقة shouldOverrideUrlLoading() التي تتحقق مما إذا كان مضيف عنوان URL يطابق نطاقًا محددًا، كما هو موضّح في المثال السابق. وفي حال تطابق الرمز، ستعرض الطريقة خطأ ولا تلغي تحميل عنوان URL. يتيح هذا الإجراء لـ WebView تحميل عنوان URL كالمعتاد. وفي حال عدم تطابُق مضيف عنوان URL، يتم إنشاء Intent لتشغيل Activity التلقائي للتعامل مع عناوين URL، ما يؤدي إلى حلّ مشكلة متصفّح الويب التلقائي لدى المستخدم.

التعامل مع عناوين URL المخصّصة

يطبق WebView القيود عند طلب الموارد وحل الروابط التي تستخدم نظام عنوان URL المخصص. على سبيل المثال، إذا نفّذت عمليات معاودة الاتصال مثل shouldOverrideUrlLoading() أو shouldInterceptRequest()، ستستدعي WebView هذه الطلبات فقط لعناوين URL الصالحة.

على سبيل المثال، قد لا يستدعي WebView طريقة shouldOverrideUrlLoading() عند الوصول إلى روابط مثل ما يلي:

<a href="showProfile">Show Profile</a>

يتم التعامل مع عناوين URL غير الصالحة، مثل العناوين المعروضة في المثال السابق، بشكل غير متناسق في WebView، لذا ننصح باستخدام عنوان URL بتنسيق جيّد بدلاً من ذلك. يمكنك استخدام مخطط مخصص أو عنوان URL يستخدم HTTPS لنطاق تتحكم فيه مؤسستك.

فبدلاً من استخدام سلسلة بسيطة في رابط، كما في المثال السابق، يمكنك استخدام مخطط مخصص، مثل ما يلي:

<a href="example-app:showProfile">Show Profile</a>

يمكنك بعد ذلك معالجة عنوان URL هذا بطريقة shouldOverrideUrlLoading() على النحو التالي:

Kotlin

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Java

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

تم تصميم واجهة برمجة التطبيقات shouldOverrideUrlLoading() في المقام الأول لإطلاق الأهداف لعناوين URL محدّدة. وعند تنفيذها، احرص على عرض false لعناوين URL التي تعرض أسماء WebView. ومع ذلك، لا تقتصر على إطلاق الأهداف. يمكنك استبدال أغراض الإطلاق بأيّ سلوك مخصّص في عيّنات التعليمات البرمجية السابقة.

عندما تلغي WebView تحميل عنوان URL، يتم تلقائيًا تجميع سجلّ من صفحات الويب التي تمت زيارتها. يمكنك التنقّل للخلف وللأمام في السجلّ باستخدام goBack() وgoForward().

على سبيل المثال، يوضّح ما يلي طريقة استخدام Activity لزر "رجوع" في الجهاز للانتقال للخلف:

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

إذا كان التطبيق يستخدم الإصدار AndroidX AppCompat 1.6.0 أو الإصدارات الأحدث، يمكنك تبسيط المقتطف السابق أكثر من ذلك:

Kotlin

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Java

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

تعرِض طريقة canGoBack() القيمة "صحيح" إذا كان هناك سجلّ صفحات ويب يمكن للمستخدم زيارتها. يمكنك أيضًا استخدام canGoForward() للتحقّق مما إذا كان هناك سجلّ إعادة توجيه. إذا لم تقم بإجراء هذا الفحص، فبعد وصول المستخدم إلى نهاية السجل، goBack() وgoForward() لا تفعل أي شيء.

التعامل مع التغييرات في إعدادات الجهاز

أثناء وقت التشغيل، تحدث تغييرات حالة النشاط عند تغيُّر إعدادات الجهاز، على سبيل المثال عندما يتدوير المستخدمون الجهاز أو يرفضون أداة تعديل أسلوب الإدخال (IME). تؤدي هذه التغييرات إلى إتلاف نشاط العنصر WebView وإنشاء نشاط جديد، ما يؤدي أيضًا إلى إنشاء عنصر WebView جديد يُحمِّل عنوان URL للعنصر الذي تم إتلافه. لتعديل السلوك التلقائي لنشاطك، يمكنك تغيير طريقة التعامل مع تغييرات orientation في ملف البيان. لمزيد من المعلومات حول التعامل مع تغييرات الإعدادات أثناء وقت التشغيل، يُرجى الاطّلاع على التعامل مع التغييرات في إعدادات الإعدادات.

إدارة النوافذ

ويتم تلقائيًا تجاهل طلبات فتح نوافذ جديدة. هذا صحيح سواء تم فتحها بواسطة JavaScript أو بواسطة السمة المستهدفة في رابط. يمكنك تخصيص WebChromeClient لتقديم أسلوبك الخاص عند فتح نوافذ متعددة.

للحفاظ على أمان تطبيقك، من الأفضل منع فتح النوافذ المنبثقة والنوافذ الجديدة. إنّ الطريقة الأكثر أمانًا لتنفيذ هذا السلوك هي إدخال "true" إلى طريقة setSupportMultipleWindows() بدون تجاهل طريقة onCreateWindow() التي تعتمد عليها السمة setSupportMultipleWindows(). يمنع هذا المنطق تحميل أي صفحة تستخدم target="_blank" في روابطها.