משתמשים ב-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. זוהי ספרייה סטטית שאפשר להוסיף לאפליקציה כדי להשתמש בממשקי API של android.webkit
שלא זמינים לגרסאות קודמות של הפלטפורמה.
מוסיפים אותו לקובץ build.gradle
באופן הבא:
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()
, כפי שמוצג בדוגמה הבאה:
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()
של פעילות, צריך להשתמש בלוגיקה דומה לזו:
val myWebView = WebView(activityContext) setContentView(myWebView)
WebView myWebView = new WebView(activityContext); setContentView(myWebView);
לאחר מכן טוענים את הדף:
myWebView.loadUrl("http://www.example.com")
myWebView.loadUrl("https://www.example.com");
לחלופין, אפשר לטעון את כתובת ה-URL ממחרוזת HTML:
// 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 framework שהחדרתם ל-
WebView
.
שימוש ב-JavaScript ב-WebView
אם דף האינטרנט שברצונך לטעון ב-WebView
משתמש ב-JavaScript, עליך
להפעיל את JavaScript עבור WebView
. אחרי שמפעילים את JavaScript, אפשר ליצור ממשקים בין קוד האפליקציה לקוד JavaScript.
הפוך JavaScript לפעיל
JavaScript מושבת ב-WebView
כברירת מחדל. אפשר להפעיל אותו דרך ה-WebSettings
שמצורף ל-WebView
. מאחזרים את WebSettings
באמצעות getSettings()
, ואז מפעילים את JavaScript באמצעות setJavaScriptEnabled()
.
דוגמה:
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 יכול לקרוא ל-method ב-
את הקוד של Android כדי להציג Dialog
,
במקום להשתמש בפונקציה alert()
של JavaScript.
כדי לקשר ממשק חדש בין קוד JavaScript לקוד Android, צריך לבצע קריאה ל-addJavascriptInterface()
, ולהעביר לה מופע של הכיתה שרוצים לקשר ל-JavaScript ושם של ממשק ש-JavaScript יכול לקרוא אליו כדי לגשת לכיתה.
לדוגמה, אפשר לכלול את הכיתה הבאה באפליקציה ל-Android:
/** 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()
, כמו בדוגמה הבאה:
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
כדי לקרוא ל-method WebAppInterface.showToast()
.
הכינוי של הניווט בדפים
כשהמשתמש מקייש על קישור מדף אינטרנט ב-WebView
, כברירת מחדל, מערכת Android מפעילה אפליקציה שמטפלת בכתובות URL. בדרך כלל, דפדפן האינטרנט שמוגדר כברירת מחדל נפתח ומטעין את כתובת היעד. עם זאת, אפשר לשנות את ההתנהגות הזו ב-WebView
כך שהקישורים ייפתחו ב-WebView
. לאחר מכן תוכלו לאפשר למשתמש לנווט אחורה וקדימה בהיסטוריית דפי האינטרנט שלו, שמתוחזקת על ידי WebView
.
כדי לפתוח קישורים שהמשתמש הקיש עליהם, צריך לספק WebViewClient
ל-WebView
באמצעות setWebViewClient()
.
כל הקישורים שהמשתמש מקייש עליהם נטענים ב-WebView
. אם אתם רוצים יותר שליטה על המיקום שבו נטען הקישור שנלחץ עליו, תוכלו ליצור WebViewClient
משלכם שיחליף את השיטה shouldOverrideUrlLoading()
. בדוגמה הבאה ההנחה היא ש-MyWebViewClient
היא מחלקה פנימית
מתוך Activity
.
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
:
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 מותאמת אישית. לדוגמה, אם מטמיעים קריאות חזרה (callbacks) כמו 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()
, למשל
הזה:
// 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; }
ה-API של shouldOverrideUrlLoading()
מיועד בעיקר להפעלת כוונות לכתובות URL ספציפיות. כשמיישמים אותו, חשוב להחזיר את הערך false
לכתובות URL ש-WebView
מטפל בהן. עם זאת, אתם לא מוגבלים להפעלת כוונות. אפשר
החלפת ההפעלה של Intents בכל התנהגות מותאמת אישית שמופיעה בקוד הקודם
דוגמאות.
ניווט בהיסטוריית דפי האינטרנט
כשהערך של WebView
מבטל את טעינת כתובת ה-URL, הוא צובר באופן אוטומטי
היסטוריה של דפי אינטרנט שבהם ביקרת. אפשר לנווט אחורה וקדימה דרך
היסטוריה עם goBack()
ועם
goForward()
לדוגמה, למטה אפשר לראות איך Activity
יכול להשתמש במכשיר 'הקודם'
הלחצן כדי לנווט אחורה:
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); }
אם האפליקציה שלכם משתמשת ב-AndroidX AppCompat
מגרסה 1.6.0 ואילך, תוכלו לפשט עוד יותר את קטע הקוד הקודם:
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()
מחזירה True אם יש היסטוריית דפי אינטרנט שהמשתמש יכול לבקר בו. בדומה לכך,
יכול להשתמש ב-canGoForward()
כדי
לבדוק אם יש היסטוריית העברה. אם לא מבצעים את הבדיקה הזו, אחרי שהמשתמש מגיע לסוף ההיסטוריה, goBack()
ו-goForward()
לא עושים כלום.
טיפול בשינויים בהגדרות המכשיר
במהלך זמן הריצה, שינויים במצב הפעילות מתרחשים כשבהגדרת המכשיר
שינויים, למשל כשמשתמשים מסובבים את המכשיר או סוגרים כלי לעריכת שיטות קלט
(IME). השינויים האלה גורמים להשמדת הפעילות של אובייקט WebView
,
פעילות חדשה שתיווצר, פעולה שיוצרת גם אובייקט WebView
חדש שנטען
כתובת ה-URL של האובייקט שהושמד. כדי לשנות את התנהגות ברירת המחדל של הפעילות, אפשר
יש לשנות את האופן שבו הוא מטפל בשינויים ב-orientation
במניפסט. למידע נוסף על טיפול בשינויים בהגדרות במהלך זמן הריצה, קראו את המאמר טיפול בשינויים בהגדרות.
ניהול החלונות
כברירת מחדל, המערכת מתעלמת מבקשות לפתיחת חלונות חדשים. הדבר נכון בין
נפתחים על ידי JavaScript או על ידי מאפיין היעד בקישור. אתם יכולים להתאים אישית את WebChromeClient
כדי לקבוע את ההתנהגות שלכם כשאתם פותחים כמה חלונות.
כדי לאבטח את האפליקציה, מומלץ למנוע הצגה של חלונות קופצים וחלונות חדשים
פתיחה. הדרך הבטוחה ביותר להטמיע את ההתנהגות הזו היא להעביר את "true"
אל setSupportMultipleWindows()
, אבל לא לשנות את השיטה onCreateWindow()
, ש-setSupportMultipleWindows()
תלויה בה. הלוגיקה הזו מונעת את הטעינה של כל דף שמשתמש ב-target="_blank"
בקישורים שלו.