สร้างเว็บแอปใน 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 ซึ่งเป็นไลบรารีแบบคงที่คุณเพิ่มลงในแอปพลิเคชันเพื่อใช้ API android.webkit ที่ไม่พร้อมใช้งานสำหรับแพลตฟอร์มเวอร์ชันก่อนหน้าได้

เพิ่มไลบรารีลงในไฟล์ build.gradle ดังนี้

Kotlin

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

ดึงดูด

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

ดูรายละเอียดเพิ่มเติมได้ที่ตัวอย่าง the WebView example ใน GitHub

เพิ่ม WebView ลงในแอป

หากต้องการเพิ่ม WebView ลงในแอป คุณสามารถใส่องค์ประกอบ <WebView> ใน เลย์เอาต์กิจกรรมหรือตั้งค่าหน้าต่าง Activity ทั้งหมดเป็น WebView ใน onCreate()

เพิ่ม WebView ในเลย์เอาต์กิจกรรม

หากต้องการเพิ่ม WebView ลงในแอปในเลย์เอาต์ ให้เพิ่มโค้ดต่อไปนี้ลงในไฟล์ XML ของเลย์เอาต์ของกิจกรรม

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_hei>ght="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 =
     &<quot><;htm>lbody'%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 =
     &<quot><;htm>lbody'%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 ดังที่แสดงในตัวอย่างต่อไปนี้

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

คุณปรับแต่ง WebView ได้โดยทำอย่างใดอย่างหนึ่งต่อไปนี้

  • เปิดใช้การรองรับแบบเต็มหน้าจอโดยใช้ WebChromeClient ระบบจะเรียกใช้คลาสนี้ด้วยเมื่อ WebView ต้องได้รับสิทธิ์ในการเปลี่ยนแปลง UI ของแอปโฮสต์ เช่น การสร้างหรือปิดหน้าต่าง หรือการส่งกล่องโต้ตอบ JavaScript ไปยังผู้ใช้ หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการแก้ไขข้อบกพร่องในบริบทนี้ โปรดอ่าน แก้ไขข้อบกพร่องของเว็บ แอป
  • จัดการเหตุการณ์ที่ส่งผลต่อการแสดงผลเนื้อหา เช่น ข้อผิดพลาดในการส่งแบบฟอร์ม หรือการนำทางโดยใช้ WebViewClient นอกจากนี้ คุณยังใช้คลาสย่อยนี้เพื่อสกัดกั้นการโหลด URL ได้ด้วย
  • เปิดใช้ JavaScript โดยแก้ไข WebSettings.
  • ใช้ JavaScript เพื่อเข้าถึงออบเจ็กต์เฟรมเวิร์ก Android ที่คุณแทรกลงใน WebView

ใช้ JavaScript ใน WebView

หากหน้าเว็บที่ต้องการโหลดใน WebView ใช้ JavaScript คุณต้องเปิดใช้ JavaScript สำหรับ WebView หลังจากเปิดใช้ JavaScript แล้ว คุณจะสร้างอินเทอร์เฟซระหว่างโค้ดแอปกับโค้ด JavaScript ได้

เปิดใช้ JavaScript

JavaScript จะปิดใช้ใน WebView โดยค่าเริ่มต้น คุณเปิดใช้ JavaScript ได้ผ่าน 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 คุณสามารถกำหนดสตริง User Agent ที่กำหนดเองด้วย setUserAgentString() จากนั้นค้นหาสตริง User Agent ที่กำหนดเองในหน้าเว็บเพื่อยืนยันว่าไคลเอ็นต์ที่ขอหน้าเว็บคือ แอป Android ของคุณ

ผูกโค้ด JavaScript กับโค้ด Android

เมื่อพัฒนาเว็บแอปพลิเคชันที่ออกแบบมาโดยเฉพาะสำหรับ WebView ในแอป Android คุณสามารถสร้างอินเทอร์เฟซระหว่างโค้ด JavaScript กับโค้ด Android ฝั่งไคลเอ็นต์ได้ ตัวอย่างเช่น โค้ด JavaScript สามารถเรียกใช้เมธอดใน โค้ด Android เพื่อแสดง Dialog แทนการใช้ฟังก์ชัน alert() ของ JavaScript

หากต้องการผูกอินเทอร์เฟซใหม่ระหว่าง JavaScript กับโค้ด Android ให้เรียกใช้ addJavascriptInterface() โดยส่งอินสแตนซ์คลาสที่จะผูกกับ JavaScript และชื่ออินเทอร์เฟซที่ JavaScript สามารถเรียกใช้เพื่อเข้าถึง คลาส

ดูข้อมูลเพิ่มเติมเกี่ยวกับการสื่อสารระหว่าง JavaScript กับโค้ดแบบเนทีฟ รวมถึง API ที่ทันสมัยและปลอดภัยยิ่งขึ้นได้ที่เข้าถึง Native API ด้วย JSBridge

ตัวอย่างเช่น คุณสามารถใส่คลาสต่อไปนี้ในแอป 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 บางส่วนที่สร้างข้อความ Toast โดยใช้อินเทอร์เฟซใหม่เมื่อผู้ใช้แตะปุ่ม

<input type="button" value="Say hello" onClick="showAndroidT>oa<st('Hello Android!')&>quot; /

script type="text/javascript"
    function showAndroidToast(t<oast) {>
        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 Scheme ที่กำหนดเอง ตัวอย่างเช่น หากคุณใช้การเรียกกลับ เช่น shouldOverrideUrlLoading() หรือ shouldInterceptRequest() แล้ว WebView จะเรียกใช้การเรียกกลับเหล่านี้สำหรับ URL ที่ถูกต้องเท่านั้น

ตัวอย่างเช่น WebView อาจไม่เรียกใช้เมธอด shouldOverrideUrlLoading() สำหรับลิงก์ต่อไปนี้

<a href="showPro>file"Sh<ow> Profile/a

WebView จะจัดการ URL ที่ไม่ถูกต้อง เช่น URL ที่แสดงในตัวอย่างก่อนหน้าอย่างไม่สอดคล้องกัน ดังนั้นเราขอแนะนำให้ใช้ URL ที่มีรูปแบบถูกต้องแทน คุณสามารถใช้ Scheme ที่กำหนดเองหรือ URL HTTPS สำหรับโดเมนที่องค์กรของคุณควบคุม

แทนที่จะใช้สตริงธรรมดาในลิงก์ ดังที่แสดงในตัวอย่างก่อนหน้า คุณสามารถใช้ Scheme ที่กำหนดเอง เช่น รูปแบบต่อไปนี้

<a href="example-app:showPro>file"Sh<ow> 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;
}

API shouldOverrideUrlLoading() มีไว้เพื่อเปิดใช้ Intent สำหรับ URL ที่เฉพาะเจาะจงเป็นหลัก เมื่อใช้ API นี้ ให้ตรวจสอบว่าได้แสดงค่าเป็น false สำหรับ URL ที่ WebView จัดการ อย่างไรก็ตาม คุณไม่ได้จำกัดอยู่แค่การเปิดใช้ Intent คุณสามารถแทนที่การเปิดใช้ Intent ด้วยลักษณะการทำงานที่กำหนดเองในตัวอย่างโค้ดก่อนหน้า

เมื่อ 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_B&&ACK  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_BA&&CK)  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 ใน Manifest ดูข้อมูลเพิ่มเติมเกี่ยวกับ การจัดการการเปลี่ยนแปลงการกำหนดค่าในระหว่างรันไทม์ได้ที่ จัดการการเปลี่ยนแปลง การกำหนดค่า

จัดการหน้าต่าง

โดยค่าเริ่มต้น ระบบจะละเว้นคำขอเปิดหน้าต่างใหม่ ไม่ว่าคำขอจะเปิดโดย JavaScript หรือแอตทริบิวต์เป้าหมายในลิงก์ คุณสามารถปรับแต่ง WebChromeClient เพื่อกำหนดลักษณะการทำงานของคุณเองสำหรับการเปิดหน้าต่างหลายหน้าต่าง

วิธีที่ดีที่สุดคือป้องกันไม่ให้ป๊อปอัปและหน้าต่างใหม่เปิดขึ้นเพื่อรักษาความปลอดภัยของแอป วิธีที่ปลอดภัยที่สุดในการใช้ลักษณะการทำงานนี้คือการส่ง "true" ไปยัง setSupportMultipleWindows() แต่ไม่ลบล้างเมธอด onCreateWindow() ซึ่ง setSupportMultipleWindows() ต้องใช้ ตรรกะนี้จะป้องกันไม่ให้หน้าเว็บที่ใช้ target="_blank" ในลิงก์โหลด