إنشاء تطبيقات ويب في 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. هذه مكتبة static يمكنك إضافتها إلى تطبيقك لاستخدام واجهات برمجة تطبيقات 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()، باسم كما هو موضح في المثال التالي:

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

إضافة WebView في onCreate()

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

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

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

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

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

KotlinJava
// 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")
// 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().

راجِع المثال التالي:

KotlinJava
val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true
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:

KotlinJava
/** 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()
    }
}
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()، كما هو موضّح في المثال التالي:

KotlinJava
val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")
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.

KotlinJava
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
    }
}
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:

KotlinJava
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

عندما ينقر المستخدم على رابط ما، يستدعي النظام الآن shouldOverrideUrlLoading() التي تتحقّق مما إذا كان مضيف عنوان URL مطابقًا مجال معين، كما هو موضح في المثال السابق. إذا كان يتطابق، تعرض الطريقة القيمة false ولا تلغي تحميل عنوان 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() مثل التالي:

KotlinJava
// 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
    }
}
// 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 زر الرجوع في الجهاز للتنقّل للخلف:

KotlinJava
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)
}
@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);
}

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

KotlinJava
onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}
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" في روابطها.