MyWebViewDemo【封裝Webview經常使用配置和選擇文件、打開相機、錄音、打開本地相冊的用法】

版權聲明:本文爲HaiyuKing原創文章,轉載請註明出處!javascript

前言

封裝webview的經常使用配置和選擇文件、打開相機、錄音、打開本地相冊的用法。【若是想要使用簡單的預覽功能,能夠參考《MyBridgeWebViewDemo【集成JsBridge開源庫的的封裝的webview】》】css

注意:若是使用選擇文件、打開相機、錄音、打開本地相冊的功能,那麼就須要搭配《Android6.0運行時權限(基於RxPermission開源庫)》的申請運行時權限(相機、錄音、存儲權限)、《AppUtils【獲取手機的信息和應用版本號、安裝apk】》的適配7.0FileProvider功能。html

效果圖

  

代碼分析

1、申請運行時權限主要涉及到如下文件:

app中的build.gradle

AndroidManifest.xml

MainActivity.java

2、適配7.0File Provider主要涉及到如下文件:

 AndroidManifest.xml

 

xml/provider_paths.xml

使用步驟

1、項目組織結構圖

注意事項:java

一、 導入類文件後須要change包名以及從新import R文件路徑android

二、 Values目錄下的文件(strings.xml、dimens.xml、colors.xml等),若是項目中存在,則複製裏面的內容,不要整個覆蓋git

2、導入步驟

0-一、申請運行時權限,參考《Android6.0運行時權限(基於RxPermission開源庫)

0-二、適配Android7.0FileProvider功能,參考《AppUtils【獲取手機的信息和應用版本號、安裝apk】

一、將assets文件夾複製到項目中

404.html【自定義404頁面,會調用WebViewJSInterface中的refresh方法】github

<!DOCTYPE html>
<html>
<head>
  <meta charset=utf-8 />
  <meta http-equiv="keywords" content="404">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="renderer" content="webkit">

  <title>404喲</title>
</head>
<body>
  <div class="demo">
    <p><span>4</span><span>0</span><span>4</span></p>
    <p>網絡正在開小差(´・ω・`)</p><br/>
    <p><a onclick="window.androidMethod.refresh();">從新加載</a></p>
  </div>
</body>
</html>


<style type="text/css">
body {
  background-color: #ECECEC;
  font-family: 'Open Sans', sans-serif;
  font-size: 14px;
  color: #3c3c3c;
}

.demo p:first-child {
  text-align: center;
  font-family: cursive;
  font-size: 150px;
  font-weight: bold;
  line-height: 100px;
  letter-spacing: 5px;
  color: #fff;
}

.demo p:first-child span {
  cursor: pointer;
  text-shadow: 0px 0px 2px #686868,
    0px 1px 1px #ddd,
    0px 2px 1px #d6d6d6,
    0px 3px 1px #ccc,
    0px 4px 1px #c5c5c5,
    0px 5px 1px #c1c1c1,
    0px 6px 1px #bbb,
    0px 7px 1px #777,
    0px 8px 3px rgba(100, 100, 100, 0.4),
    0px 9px 5px rgba(100, 100, 100, 0.1),
    0px 10px 7px rgba(100, 100, 100, 0.15),
    0px 11px 9px rgba(100, 100, 100, 0.2),
    0px 12px 11px rgba(100, 100, 100, 0.25),
    0px 13px 15px rgba(100, 100, 100, 0.3);
  -webkit-transition: all .1s linear;
  transition: all .1s linear;
}

.demo p:first-child span:hover {
  text-shadow: 0px 0px 2px #686868,
    0px 1px 1px #fff,
    0px 2px 1px #fff,
    0px 3px 1px #fff,
    0px 4px 1px #fff,
    0px 5px 1px #fff,
    0px 6px 1px #fff,
    0px 7px 1px #777,
    0px 8px 3px #fff,
    0px 9px 5px #fff,
    0px 10px 7px #fff,
    0px 11px 9px #fff,
    0px 12px 11px #fff,
    0px 13px 15px #fff;
  -webkit-transition: all .1s linear;
  transition: all .1s linear;
}

.demo p:not(:first-child) {
  text-align: center;
  color: #666;
  font-family: cursive;
  font-size: 20px;
  text-shadow: 0 1px 0 #fff;
  letter-spacing: 1px;
  line-height: 2em;
  margin-top: -50px;
}
</style>

demo.html【用於演示選擇文件、打開相機、錄音、打開本地相冊功能】web

<html>
    <head>
        <meta content="text/html; charset=utf-8" http-equiv="content-type">
        <meta http-equiv="keywords" content="測試">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        <title>
            webview
        </title>
    </head>
    
    <body>
        <p>
            <input type="file" value="打開文件" />
        </p>

        <p>
            點擊下面的按鈕,獲取的文件路徑:
        </p>
        <p>
            <input type="text" id="filePath" value="文件路徑" style="width:100%"/>
        </p>
        <p>
            <input type="button" id="openRecord" value="打開錄音" onclick="window.androidMethod.openRecord();"/>
        </p>
        <p>
            <input type="button" id="takePicture" value="打開相機" onclick="window.androidMethod.takePicture();"/>
        </p>
        <p>
            <input type="button" id="choosePic" value="打開本地相冊" onclick="window.androidMethod.choosePic();"/>
        </p>
        <p>
            <a href='tel:10010'>撥打電話:10010</a>
        </p>
    </body>
    <script>
        //打開錄音、打開相機、打開本地相冊,選擇文件後返回的路徑
        function setInputText(urlPath){
            document.getElementById("filePath").value = urlPath;
        }
    </script>

</html>
demo.html

二、將customwebview包複製到項目中

三、將mywebview_progress_dialog_img_drawable.xml複製到項目中

<?xml version="1.0" encoding="utf-8"?>
<!-- WebView使用的進度加載對話框進度圓圈 -->
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/mywebview_progress_dialog_img"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromDegrees="0.0"
    android:toDegrees="360.0"
    android:repeatMode="restart"
     />
mywebview_progress_dialog_img_drawable.xml

四、將mywebview_progress_dialog_img.png圖片複製到項目中

五、將mywebview_dialog_webviewprogress.xml複製到項目中

<?xml version="1.0" encoding="utf-8"?>
<!-- WebView使用的進度加載對話框佈局文件 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dialog_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <!-- 自定義圓形進度條 -->
    <!-- android:indeterminateDrawable自定義動畫圖標 -->
    <ProgressBar
        android:id="@+id/loadProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:indeterminateDrawable="@drawable/mywebview_progress_dialog_img_drawable"
        />

</RelativeLayout>
mywebview_dialog_webviewprogress.xml

六、在styles.xml文件中添加如下代碼

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <!-- ==================MyWebview========網頁加載時的進度對話框========================== -->
    <style name="mywebview_loading_style" parent="android:style/Theme.Dialog">
        <!-- Dialog的windowFrame框爲無 -->
        <item name="android:windowFrame">@null</item>
        <!-- 是否顯示title -->
        <item name="android:windowNoTitle">true</item>
        <!-- 是否浮如今activity之上 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 設置dialog的背景:#00000000透明色 -->
        <item name="android:windowBackground">@android:color/transparent</item>
        <!-- 半透明 -->
        <item name="android:windowIsTranslucent">true</item>
        <!-- 背景變灰:整個屏幕變灰,配合setCanceledOnTouchOutside(false) -->
        <item name="android:backgroundDimEnabled">false</item>
        <!-- 對話框是否有遮蓋 -->
        <item name="android:windowContentOverlay">@null</item>
    </style>

</resources>

七、在AndroidManifest.xml中添加如下代碼

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.why.project.mywebviewdemo">

    <!-- ======================(MyWebView)========================== -->
    <!-- 容許程序打開網絡套接字 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!-- ======================拍照用到的========================== -->
    <uses-permission android:name="android.permission.CAMERA" />
    <!-- ======================錄音用到的========================== -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!-- 向SD卡寫入數據權限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!-- =================7.0上讀取文件========================== -->
        <!--參考資料https://blog.csdn.net/lmj623565791/article/details/72859156-->
        <!--authorities:{app的包名}.provider grantUriPermissions:必須是true,表示授予 URI 臨時訪問權限 exported:必須是false resource:中的@xml/provider_paths是咱們接下來要添加的文件-->
        <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
        </provider>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- 網頁頁面 -->
        <activity android:name=".MyWebviewActivity">
        </activity>
    </application>

</manifest>

3、使用方法

在佈局文件activity_mywebview.xml中聲明【實際項目中實際新的完整路徑

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.why.project.mywebviewdemo.customwebview.mywebview.MyWebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">

    </com.why.project.mywebviewdemo.customwebview.mywebview.MyWebView>

</android.support.constraint.ConstraintLayout>

在Activity中使用以下

package com.why.project.mywebviewdemo;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.widget.Toast;

import com.why.project.mywebviewdemo.customwebview.mywebview.MyWebView;
import com.why.project.mywebviewdemo.customwebview.mywebview.WebViewJSInterface;
import com.why.project.mywebviewdemo.customwebview.utils.GetPathFromUri4kitkat;
import com.why.project.mywebviewdemo.customwebview.utils.WebviewGlobals;

import java.io.File;

/**
 * Created by HaiyuKing
 * Used webview
 */

public class MyWebviewActivity extends AppCompatActivity {
    private static final String TAG = MyWebviewActivity.class.getSimpleName();

    private MyWebView myWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mywebview);

        initViews();
        initDatas();
        initEvents();
    }

    @Override public void onDestroy() { //銷燬webview控件
 myWebView.removeAllViews(); myWebView.destroy(); super.onDestroy(); } private void initViews() {
        myWebView = findViewById(R.id.web_view); myWebView.setCanBackPreviousPage(true,MyWebviewActivity.this);//能夠返回上一頁
    }

    private void initDatas() {
        String openUrl = getIntent().getExtras().getString("urlKey");
        if(TextUtils.isEmpty(openUrl)){
            myWebView.loadLocalUrl("demo.html");
        }else {
            myWebView.loadWebUrl(openUrl);
        }
    }

    private void initEvents() {

    }

    /*=========================================實現webview調用相機、打開文件管理器功能==============================================*/ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.w(TAG, "{onActivityResult}resultCode="+resultCode); Log.w(TAG, "{onActivityResult}requestCode="+requestCode); Log.w(TAG, "{onActivityResult}data="+data); if (resultCode == Activity.RESULT_OK) { //webview界面調用打開本地文件管理器選擇文件的回調
            if (requestCode == WebviewGlobals.CHOOSE_FILE_REQUEST_CODE ) { Uri result = data == null ? null : data.getData(); Log.w(TAG,"{onActivityResult}文件路徑地址:" + result.toString()); //若是mUploadMessage或者mUploadCallbackAboveL不爲空,表明是觸發input[type]類型的標籤
                if (null != myWebView.getMyWebChromeClient().getmUploadMessage() || null != myWebView.getMyWebChromeClient().getmUploadCallbackAboveL()) { if (myWebView.getMyWebChromeClient().getmUploadCallbackAboveL() != null) { onActivityResultAboveL(requestCode, data);//5.0++
                    } else if (myWebView.getMyWebChromeClient().getmUploadMessage() != null) { myWebView.getMyWebChromeClient().getmUploadMessage().onReceiveValue(result);//將文件路徑返回去,填充到input中
                        myWebView.getMyWebChromeClient().setmUploadMessage(null); } }else{ //此處代碼是處理經過js方法觸發的狀況
                    Log.w(TAG,"{onActivityResult}文件路徑地址(js):" + result.toString()); String filePath = GetPathFromUri4kitkat.getPath(MyWebviewActivity.this, Uri.parse(result.toString())); setUrlPathInput(myWebView,"打開本地相冊:" + filePath);//修改網頁輸入框文本
 } } //由於拍照指定了路徑,因此data值爲null
            if(requestCode == WebviewGlobals.CAMERA_REQUEST_CODE){ File pictureFile = new File(WebViewJSInterface.mCurrentPhotoPath); Uri uri = Uri.fromFile(pictureFile); Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(uri); MyWebviewActivity.this.sendBroadcast(intent);  // 這裏咱們發送廣播讓MediaScanner 掃描咱們制定的文件 // 這樣在系統的相冊中咱們就能夠找到咱們拍攝的照片了【可是這樣一來,就會執行MediaScanner服務中onLoadFinished方法,因此須要注意】 //拍照 // String fileName = FileUtils.getFileName(WebViewJSInterface.mCurrentPhotoPath);
                Log.e(TAG,"WebViewJSInterface.mCurrentPhotoPath="+ WebViewJSInterface.mCurrentPhotoPath); setUrlPathInput(myWebView,"打開相機:" + WebViewJSInterface.mCurrentPhotoPath);//修改網頁輸入框文本
 } //錄音
            if(requestCode == WebviewGlobals.RECORD_REQUEST_CODE){ Uri result = data == null ? null : data.getData(); Log.w(TAG,"錄音文件路徑地址:" + result.toString());//錄音文件路徑地址:content://media/external/audio/media/111
 String filePath = GetPathFromUri4kitkat.getPath(MyWebviewActivity.this, Uri.parse(result.toString())); Log.w(TAG,"錄音文件路徑地址:" + filePath); setUrlPathInput(myWebView,"打開錄音:" + filePath);//修改網頁輸入框文本
 } }else if(resultCode == RESULT_CANCELED){//resultCode == RESULT_CANCELED 解決不選擇文件,直接返回後沒法再次點擊的問題
            if (myWebView.getMyWebChromeClient().getmUploadMessage() != null) { myWebView.getMyWebChromeClient().getmUploadMessage().onReceiveValue(null); myWebView.getMyWebChromeClient().setmUploadMessage(null); } if (myWebView.getMyWebChromeClient().getmUploadCallbackAboveL() != null) { myWebView.getMyWebChromeClient().getmUploadCallbackAboveL().onReceiveValue(null); myWebView.getMyWebChromeClient().setmUploadCallbackAboveL(null); } } } //5.0以上版本,因爲api不同,要單獨處理
 @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void onActivityResultAboveL(int requestCode, Intent data) { if (myWebView.getMyWebChromeClient().getmUploadCallbackAboveL() == null) { return; } Uri result = null; if (requestCode == WebviewGlobals.CHOOSE_FILE_REQUEST_CODE) {//打開本地文件管理器選擇圖片
            result = data == null ? null : data.getData(); } else if (requestCode == WebviewGlobals.CAMERA_REQUEST_CODE) {//調用相機拍照
            File pictureFile = new File(WebViewJSInterface.mCurrentPhotoPath); Uri uri = Uri.fromFile(pictureFile); Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(uri); MyWebviewActivity.this.sendBroadcast(intent);  // 這裏咱們發送廣播讓MediaScanner 掃描咱們制定的文件 // 這樣在系統的相冊中咱們就能夠找到咱們拍攝的照片了【可是這樣一來,就會執行MediaScanner服務中onLoadFinished方法,因此須要注意】
 result = Uri.fromFile(pictureFile); } Log.w(TAG,"{onActivityResultAboveL}文件路徑地址:"+result.toString()); myWebView.getMyWebChromeClient().getmUploadCallbackAboveL().onReceiveValue(new Uri[]{result});//將文件路徑返回去,填充到input中
        myWebView.getMyWebChromeClient().setmUploadCallbackAboveL(null); return; } //設置網頁上的文件路徑輸入框文本
    private void setUrlPathInput(WebView webView, String urlPath) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { webView.evaluateJavascript("setInputText('"+ urlPath +"')", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Log.i(TAG, "onReceiveValue value=" + value); }}); }else{ Toast.makeText(MyWebviewActivity.this,"當前版本號小於19,沒法支持evaluateJavascript,須要使用第三方庫JSBridge", Toast.LENGTH_SHORT).show(); } }
}

混淆配置

注意:根據實際項目的路徑修改下面表紅色的文字chrome

# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

#====WebView + js==== -keepclassmembers class com.why.project.mywebviewdemo.customwebview.mywebview.MyWebView { public *; } -keepclassmembers class com.why.project.mywebviewdemo.customwebview.mywebview.MyWebChromeClient { public *; } -keepclassmembers class com.why.project.mywebviewdemo.customwebview.mywebview.MyWebViewClient { public *; } # keep 使用 webview 的類 -keepclassmembers class com.why.project.mywebviewdemo.MyWebviewActivity { public *; } -keepattributes *Annotation* #解決:android sdk api >= 17 時須要加@JavascriptInterface」所出現的問題。 -keepattributes *JavascriptInterface*

參考資料

Android-WebView-解決對選擇文件 input type="file"無響應api

項目demo下載地址

https://github.com/haiyuKing/MyWebviewDemo

相關文章
相關標籤/搜索