Android整合騰訊X5WebView
阿新 • • 發佈:2019-01-24
專案說明:最近在開發Android原生巢狀H5實現混合開發,剛開始採用原生的WebView各種相容性問題,之後決定採用騰訊的x5瀏覽器來開發,遇到的一些問題列一下:
- x5Webview與H5的互動問題
- x5同步cookie問題
- WebView載入進度條問題處理
- H5呼叫Android攝像頭進行錄製視訊、H5呼叫Android相機進行拍照
- x5WebView-WebChromeClient的方法onShowFileChooser只執行一次的問題
- X5WebView的Setting需要配置那些東西
先看效果圖
1、Android整合X5核心
public class MyApplication extends BaseApplication { @Override public void onCreate() { super.onCreate(); preInitX5Core(); } private void preInitX5Core() { //預載入x5核心 Intent intent = new Intent(this, X5NetService.class); startService(intent); } }
public class X5NetService extends IntentService { public static final String TAG = LogTAG.x5webview; public X5NetService(){ super(TAG); } public X5NetService(String name) { super(TAG); } @Override public void onHandleIntent(@Nullable Intent intent) { initX5Web();//初始化 } public void initX5Web() { if (!QbSdk.isTbsCoreInited()) { // 設定X5初始化完成的回撥介面 QbSdk.preInit(getApplicationContext(), null); } QbSdk.initX5Environment(getApplicationContext(), cb); } QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() { @Override public void onViewInitFinished(boolean arg0) { // TODO Auto-generated method stub } @Override public void onCoreInitFinished() { // TODO Auto-generated method stub } }; }
//自定義x5WebView public class X5WebView extends WebView { private static final String TAG = "x5webview"; int progressColor = 0xFFFF4081; ProgressView mProgressview; //自定義WebView載入進度條 TextView title; Context context; @SuppressLint("SetJavaScriptEnabled") public X5WebView(Context arg0, AttributeSet arg1) { super(arg0, arg1); this.context = arg0; initWebViewSettings();//初始化setting配置 this.setWebViewClient(client) ; this.setWebChromeClient(chromeClient); this.setDownloadListener(downloadListener); initProgressBar();//初始化進度條 this.getView().setClickable(true); } //setting配置 private void initWebViewSettings() { WebSettings webSetting = this.getSettings(); webSetting.setJavaScriptEnabled(true);//允許js呼叫 webSetting.setJavaScriptCanOpenWindowsAutomatically(true);//支援通過JS開啟新視窗 webSetting.setAllowFileAccess(true);//在File域下,能夠執行任意的JavaScript程式碼,同源策略跨域訪問能夠對私有目錄檔案進行訪問等 webSetting.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);//控制頁面的佈局(使所有列的寬度不超過螢幕寬度) webSetting.setSupportZoom(true);//支援頁面縮放 webSetting.setBuiltInZoomControls(true);//進行控制縮放 webSetting.setAllowContentAccess(true);//是否允許在WebView中訪問內容URL(Content Url),預設允許 webSetting.setUseWideViewPort(true);//設定縮放密度 webSetting.setSupportMultipleWindows(false);//設定WebView是否支援多視窗,如果為true需要實現onCreateWindow(WebView, boolean, boolean, Message) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //兩者都可以 webSetting.setMixedContentMode(webSetting.getMixedContentMode());//設定安全的來源 } webSetting.setAppCacheEnabled(true);//設定應用快取 webSetting.setDomStorageEnabled(true);//DOM儲存API是否可用 webSetting.setGeolocationEnabled(true);//定位是否可用 webSetting.setLoadWithOverviewMode(true);//是否允許WebView度超出以概覽的方式載入頁面, webSetting.setAppCacheMaxSize(Long.MAX_VALUE);//設定應用快取內容的最大值 webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND);//設定是否支援外掛 webSetting.setCacheMode(WebSettings.LOAD_NO_CACHE);//重寫使用快取的方式 webSetting.setAllowUniversalAccessFromFileURLs(true);//是否允許執行在一個file schema URL環境下的JavaScript訪問來自其他任何來源的內容 webSetting.setAllowFileAccessFromFileURLs(true);//是否允許執行在一個URL環境 } //進度條 private void initProgressBar() { mProgressview = new ProgressView(context); mProgressview.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 6)); mProgressview.setDefaultColor(progressColor); addView(mProgressview); } //客戶端配置 private WebViewClient client = new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(com.tencent.smtt.sdk.WebView view, String url) { //這裡直接載入url view.loadUrl(url); return true; } @Override public void onPageStarted(WebView webView, String s, Bitmap bitmap) { super.onPageStarted(webView, s, bitmap); } @Override public void onPageFinished(com.tencent.smtt.sdk.WebView view, String url) { //處理客戶端與WebView同步,具體細節問題請看最上面傳送門 CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); String endCookie = cookieManager.getCookie(url); Log.i(TAG, "onPageFinished: endCookie : " + endCookie); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { CookieSyncManager.getInstance().sync();//同步cookie } else { CookieManager.getInstance().flush(); } super.onPageFinished(view, url); } @Override public void onReceivedError(WebView webView, int i, String s, String s1) { super.onReceivedError(webView, i, s, s1); //網頁問題報錯的時候執行 webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); webView.setVisibility(View.VISIBLE); } @Override public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) { super.onReceivedSslError(webView, sslErrorHandler, sslError); if(sslError.getPrimaryError() == android.net.http.SslError.SSL_INVALID ){// 校驗過程遇到了bug //這裡直接忽略ssl證書的檢測出錯問題,選擇繼續執行頁面 sslErrorHandler.proceed(); }else{ //不是證書問題時候則停止執行載入頁面 sslErrorHandler.cancel(); } } }; //x5瀏覽器配置可視訊播放、檔案下載 private WebChromeClient chromeClient = new WebChromeClient() { @Override public boolean onJsConfirm(com.tencent.smtt.sdk.WebView arg0, String arg1, String arg2, com.tencent.smtt.export.external.interfaces.JsResult arg3) { return super.onJsConfirm(arg0, arg1, arg2, (com.tencent.smtt.export.external.interfaces.JsResult) arg3); } View myVideoView; View myNormalView; IX5WebChromeClient.CustomViewCallback callback; /** * 全屏播放配置 */ @Override public void onShowCustomView(View view, IX5WebChromeClient.CustomViewCallback customViewCallback) { FrameLayout normalView = null; ViewGroup viewGroup = (ViewGroup) normalView.getParent(); viewGroup.removeView(normalView); viewGroup.addView(view); myVideoView = view; myNormalView = normalView; callback = customViewCallback; } @Override public void onHideCustomView() { if (callback != null) { callback.onCustomViewHidden(); callback = null; } if (myVideoView != null) { ViewGroup viewGroup = (ViewGroup) myVideoView.getParent(); viewGroup.removeView(myVideoView); viewGroup.addView(myNormalView); } } @Override public void onProgressChanged(com.tencent.smtt.sdk.WebView webView, int i) { super.onProgressChanged(webView, i); mProgressview.setProgress(i); } @Override public boolean onJsAlert(com.tencent.smtt.sdk.WebView arg0, String arg1, String arg2, com.tencent.smtt.export.external.interfaces.JsResult arg3) { /** * 這裡寫入你自定義的window alert */ return super.onJsAlert(null, arg1, arg2, arg3); } }; //下載監聽器 DownloadListener downloadListener = new DownloadListener() { @Override public void onDownloadStart(String arg0, String arg1, String arg2, String arg3, long arg4) { new AlertDialog.Builder(context) .setTitle("allow to download?") .setPositiveButton("yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText( context, "fake message: i'll download...", Toast.LENGTH_LONG).show(); } }) .setNegativeButton("no", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub Toast.makeText( context, "fake message: refuse download...", Toast.LENGTH_SHORT).show(); } }) .setOnCancelListener( new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { // TODO Auto-generated method stub Toast.makeText( context, "fake message: refuse download...", Toast.LENGTH_SHORT).show(); } }).show(); } }; @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean ret = super.drawChild(canvas, child, drawingTime); canvas.save(); Paint paint = new Paint(); paint.setColor(0x7fff0000); paint.setTextSize(24.f); paint.setAntiAlias(true); canvas.restore(); return ret; } public X5WebView(Context arg0) { super(arg0); setBackgroundColor(85621); } }
//進度條
public class ProgressView extends View {
int defaultColor = 0xFFFF4081;
Paint progressPaint = null;
Paint progressCircle = null;
int currentProgress = 0;
int totalProgress = 0;
boolean isHide = false;
public ProgressView(Context context) {
this(context, null);
}
public ProgressView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public ProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setLayerType(LAYER_TYPE_SOFTWARE, null);
progressPaint = new Paint();
progressPaint.setColor(defaultColor);
progressCircle = new Paint();
progressCircle.setColor(defaultColor);
progressCircle.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));
}
int viewWidth = 0;
int viewHeight = 0;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
viewHeight = getMeasuredHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(currentProgress<=100&&isHide){
isHide = false;
this.setAlpha(1);
}
canvas.drawRect(0, 0, (float) (viewWidth * (currentProgress / 100.0)), viewHeight, progressPaint);
canvas.drawCircle((float) (viewWidth * (currentProgress / 100.0)) - viewHeight / 2, viewHeight / 2, viewHeight, progressCircle);
if (currentProgress >= 100) {
hideSelf();
}
}
private void hideSelf() {
this.postDelayed(new Runnable() {
@Override
public void run() {
ViewCompat.animate(ProgressView.this).alpha(0);
isHide=true;
ProgressView.this.currentProgress = 0;
}
}, 100);
}
public int getDefaultColor() {
return defaultColor;
}
public void setDefaultColor(int defaultColor) {
this.defaultColor = defaultColor;
}
ValueAnimator animator;
public void setProgress(int progress) {
totalProgress = progress;
if (animator != null) {
if (animator.isRunning()) {
animator.cancel();
}
}
animator = ValueAnimator.ofInt(currentProgress, totalProgress);
animator.setDuration(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentProgress = (int) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
}
2、Android同步cookie
//這裡直接採用okhttp做同步cookie操作,具體可看上面cookie同步傳送門
public class OkHttpRequestUtil {
private volatile static OkHttpRequestUtil netRequest;
private static OkHttpClient okHttpClient; // OKHttp網路請求
private Handler mHandler;
final String TAG = LogTAG.okhttp;
private boolean checkNet;
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
private OkHttpRequestUtil() {
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.addNetworkInterceptor(new HttpLoggingInterceptor().
setLevel(HttpLoggingInterceptor.Level.BODY))
.addInterceptor(new AddCookiesInterceptor())//這裡是關鍵!!!
.addInterceptor(new SaveCookiesInterceptor())//這裡是關鍵!!!
.cookieJar(new SaCookieManger(MyApplication.context()))//這裡是關鍵!!!
.build();
mHandler = new Handler(Looper.getMainLooper());
}
private static OkHttpRequestUtil getInstance() {
if (netRequest == null) {
netRequest = new OkHttpRequestUtil();
}
return netRequest;
}
/**
* 非同步get請求(Form),內部實現方法
* @param url url
* @param params key value
*/
public void inner_GetFormAsync(String url, Map<String, String> params, final DataCallBack callBack) {
if (params == null) {
params = new HashMap<>();
}
final String doUrl = urlJoint(url, params);
final Request request = new Request.Builder().url(doUrl).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
deliverDataFailure(request, e, callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response != null && response.isSuccessful()) {
String result = response.body().string();
deliverDataSuccess(result, callBack);
} else {
throw new IOException(response + "");
}
}
});
}
/**
* get請求 沒有請求體
*
* @param url
* @param callBack
*/
private void getMethod(String url, final DataCallBack callBack) {
final Request req = new Request.Builder().url(url).build();
okHttpClient.newCall(req).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
deliverDataFailure(req, e, callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response != null && response.isSuccessful()) {
String result = response.body().string();
deliverDataSuccess(result, callBack);
} else {
deliverDataSuccess("請求異常", callBack);
}
}
});
}
/**
* 非同步post請求(Form),內部實現方法
* @param url url
* @param params params
* @param callBack callBack
*/
private void inner_PostFormAsync(String url, Map<String, String> params, final DataCallBack callBack) {
RequestBody requestBody;
if (params == null) {
params = new HashMap<>();
}
FormBody.Builder builder = new FormBody.Builder();
/**
* 在這對新增的引數進行遍歷
*/
for (Map.Entry<String, String> map : params.entrySet()) {
String key = map.getKey();
String value;
/**
* 判斷值是否是空的
*/
if (map.getValue() == null) {
value = "";
} else {
value = map.getValue();
}
/**
* 把key和value新增到formbody中
*/
builder.add(key, value);
}
requestBody = builder.build();
final Request request = new Request.Builder().url(url).post(requestBody).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
deliverDataFailure(request, e, callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) { // 請求成功
Headers newHead = response.networkResponse().request().headers();
Log.i(TAG, "new headers :: " + newHead);
//執行請求成功的操作
String result = response.body().string();
deliverDataSuccess(result, callBack);
} else {
throw new IOException(response + "");
}
}
});
}
private void inner_PostJsonAsync(String url, Map<String, String> params, final DataCallBack callBack) {
// 將map轉換成json,需要引入Gson包
String mapToJson = new Gson().toJson(params);
final Request request = buildJsonPostRequest(url, mapToJson);
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
deliverDataFailure(request, e, callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) { // 請求成功
//執行請求成功的操作
String result = response.body().string();
deliverDataSuccess(result, callBack);
} else {
throw new IOException(response + "");
}
}
});
}
private Request buildJsonPostRequest(String url, String json) {
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
return new Request.Builder().url(url).post(requestBody).build();
}
/**
* 分發失敗的時候呼叫
* 客戶端沒有網路 還是 伺服器異常
* @param request request
* @param e e
* @param callBack callBack
*/
private void deliverDataFailure(final Request request, final IOException e, final DataCallBack callBack) {
/**
* 在這裡使用非同步處理
*/
checkNet = CheckNetUtil.checkNet(NRApplication.context());
mHandler.post(new Runnable() {
@Override
public void run() {
if (callBack != null) {
try {
if (checkNet) {
callBack.requestFailure(request, e);
} else {
callBack.requestNoNet(ProjectDataDescribe.NET_NO_LINKING,
ProjectDataDescribe.NET_NO_LINKING);
}
} catch (Exception e) {
}
}
}
});
}
/**
* 分發成功的時候呼叫
*
* @param result result
* @param callBack callBack
*/
private void deliverDataSuccess(final String result, final DataCallBack callBack) {
/**
* 在這裡使用非同步執行緒處理
*/
mHandler.post(new Runnable() {
@Override
public void run() {
if (callBack != null) {
try {
callBack.requestSuccess(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
/**
* 資料回撥介面
*/
public interface DataCallBack {
// 請求成功 響應成功
void requestSuccess(String result) throws Exception;
// 請求失敗 響應失敗
void requestFailure(Request request, IOException e);
// 客戶端沒有網路連線
void requestNoNet(String msg, String data);
}
/**
* 拼接url和請求引數
*
* @param url url
* @param params key value
* @return String url
*/
private static String urlJoint(String url, Map<String, String> params) {
StringBuilder endUrl = new StringBuilder(url);
boolean isFirst = true;
Set<Map.Entry<String, String>> entrySet = params.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
if (isFirst && !url.contains("?")) {
isFirst = false;
endUrl.append("?");
} else {
endUrl.append("&");
}
endUrl.append(entry.getKey());
endUrl.append("=");
endUrl.append(entry.getValue());
}
return endUrl.toString();
}
//-------------對外提供的方法Start--------------------------------
/**
* 建立網路框架,獲取網路資料,非同步get請求(Form)
*
* @param url url
* @param params key value
* @param callBack data
*/
public static void okGetFormRequest(String url, Map<String, String> params, DataCallBack callBack) {
getInstance().inner_GetFormAsync(url, params, callBack);
}
/**
* 建立網路框架,獲取網路資料,非同步post請求(Form)
* @param url url
* @param params key value
* @param callBack data
*/
public static void okPostFormRequest(String url, Map<String, String> params, DataCallBack callBack) {
getInstance().inner_PostFormAsync(url, params, callBack);
}
/**
* get 請求
* 沒有請求體
*/
public static void okGetRequest(String url, DataCallBack callBack) {
getInstance().getMethod(url, callBack);
}
}
public class SaCookieManger implements CookieJar {
private static final String TAG = LogTAG.cookie;
private static Context mContext;
public SaCookieManger(Context context) {
mContext = context;
}
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
SaasCookieManager.loadCookie(cookies,url.host());
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
return new ArrayList<>();
}
}
public class AddCookiesInterceptor implements Interceptor {
private static final String COOKIE_PREF = "cookies_prefs";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder builder = request.newBuilder();
String cookie = getCookie(request.url().toString(), request.url().host());
if (!TextUtils.isEmpty(cookie)) {
builder.addHeader("Cookie", cookie);
Log.i(LogTAG.cookie, "interceptor addHeader Cookie: "+cookie);
}
return chain.proceed(builder.build());
}
private String getCookie(String url, String domain) {
SharedPreferences sp = MyApplication.context().getSharedPreferences(COOKIE_PREF,
Context.MODE_PRIVATE);
String cookie = sp.getString(domain, "");
Log.i(LogTAG.cookie, "interceptor getCookie: "+cookie);
if (!TextUtils.isEmpty(domain) && sp.contains(domain) && !
TextUtils.isEmpty(sp.getString(domain, ""))) {
return sp.getString(domain, "");
}
return null;
}
}
/**
* 儲存cookie攔截器
*/
public class SaveCookiesInterceptor implements Interceptor {
private static final String COOKIE_PREF = "cookies_prefs";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (!response.headers("set-cookie").isEmpty()) {
List<String> cookies = response.headers("set-cookie");
String cookie = encodeCookie(cookies);
saveCookie(request.url().toString(), request.url().host(), cookie);
}
return response;
}
/**
* 整合cookie為唯一字串
* @param cookies
* @return
*/
private String encodeCookie(List<String> cookies) {
StringBuilder sb = new StringBuilder();
List<String> set = new ArrayList<>();
for (String cookie : cookies) {
String[] arr = cookie.split(";");
for (String s : arr) {
if (set.contains(s)) {
continue;
}
set.add(s);
}
}
Iterator<String> ite = set.iterator();
while (ite.hasNext()) {
String cookie = ite.next();
sb.append(cookie).append(";");
}
int last = sb.lastIndexOf(";");
if (sb.length() - 1 == last) {
sb.deleteCharAt(last);
}
return sb.toString();
}
/**
* 持久化cookie
*/
private void saveCookie(String url, String domain, String cookies) {
SharedPreferences sp = MyApplication.context().getSharedPreferences(COOKIE_PREF,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if (TextUtils.isEmpty(url)) {
throw new NullPointerException("url is null.");
}else{
editor.putString(url, cookies);
}
if (!TextUtils.isEmpty(domain)) {
editor.putString(domain, cookies);
}
editor.apply();
}
}
//登陸請求
public class SimpleLogin {
//TODO 賬號密碼
String userName = bean.getUserName();
String passWord = bean.getPassWord();
HashMap<String,String> map = new HashMap<>();
map.put("username",userName);
map.put("password",passWord);
//TODO 網路請求
OkHttpRequestUtil.okPostFormRequest(HttpUrlConstance.APP_LOGIN, map, new OkHttpRequestUtil.DataCallBack() {
@Override
public void requestSuccess(String result) throws Exception {//TODO 成功
JSONObject j = new JSONObject(result);
//TODO 具體業務處理
}
@Override
public void requestFailure(Request request, IOException e) {//TODO 失敗
}
@Override
public void requestNoNet(String msg, String data) {//TODO 網路問題
}
});
}
3、X5WebView呼叫Android攝像頭、相機進行錄影、拍照
/**
* WebView渲染Activity
*
* @author wenhua.qin
*/
public class BrowserActivity extends BaseActivity {
private ValueCallback<Uri> uploadFile;
private ValueCallback<Uri[]> uploadFiles;
public String TAG = "AABBCC";
private int mResultCode = Activity.RESULT_CANCELED;
private X5WebView mWebView;
private String mHomeUrl = "";
@BindView(R.id.webView1)
ViewGroup mViewParent;
@Override
protected int getContentView() {
return R.layout.activity_browser;
}
@Override
protected void initData() {
super.initData();
Intent intent = getIntent();
if (intent != null) {
mHomeUrl = (String) intent.getExtras().get("url"); //傳入的網頁
}
try {
if (Integer.parseInt(android.os.Build.VERSION.SDK) >= 11) {
getWindow()
.setFlags(
android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
} catch (Exception e) {
}
init();
}
@Override
protected boolean getSwipeBack() {
return true;
}
private void init() {
mWebView = new X5WebView(this, null);
WebSettings webSetting = mWebView.getSettings();
webSetting.setAppCachePath(this.getDir("appcache", 0).getPath()); //設定應用快取目錄
webSetting.setDatabasePath(this.getDir("databases", 0).getPath()); //設定資料庫快取路徑
webSetting.setGeolocationDatabasePath(this.getDir("geolocation", 0).getPath());//設定定位的資料庫路徑
mViewParent.addView(mWebView, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.FILL_PARENT,
FrameLayout.LayoutParams.FILL_PARENT));
mWebView.addJavascriptInterface(new WebContrl(this, mWebView), "webCtrl");//與js進行互動
mWebView.setWebChromeClient(chromeClient);
mWebView.loadUrl(mHomeUrl);
}
@Override
public void onDestroy() { //銷燬時候需要處理Webview移除
if (mWebView != null && mWebView.getParent() != null) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
ViewGroup view = (ViewGroup) getWindow().getDecorView();
view.removeAllViews();
}
super.onDestroy();
}
//處於onPause、onStop狀態需要重寫onNewIntent方法
@Override
protected void onNewIntent(Intent intent) {
if (intent == null || mWebView == null || intent.getData() == null)
return;
mWebView.loadUrl(intent.getExtras().getString("url"));
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {//返回鍵監聽 回滾H5頁面
if (mWebView != null && mWebView.canGoBack()) {
mWebView.goBack();
if (Integer.parseInt(android.os.Build.VERSION.SDK) >= 16)
changGoForwardButton(mWebView);
return true;
} else
return super.onKeyDown(keyCode, event);
}
return super.onKeyDown(keyCode, event);
}
private void changGoForwardButton(com.tencent.smtt.sdk.WebView view) {
}
//當前涉及呼叫拍照、攝像功能,需要重新設定WebChromeClient
private WebChromeClient chromeClient = new WebChromeClient() {
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
}
public void openFileChooser(ValueCallback<Uri> uploadMsgs) {
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
}
// For Android >= 5.0 該專案需求是在5.0之上開發、所以5.0以下不作處理
public boolean onShowFileChooser(com.tencent.smtt.sdk.WebView webView,
ValueCallback<Uri[]> filePathCallback,
final WebChromeClient.FileChooserParams fileChooserParams) {
uploadFiles = filePathCallback;
new ActionSheetDialog(BrowserActivity.this)
.builder(uploadFile,uploadFiles)//這裡是重點!!!,需要傳入uploadFile,uploadFiles進行判斷處理
.setCancelable(true) //取消鍵
.setCanceledOnTouchOutside(true)//空白地方取消dialog
.addSheetItem("上傳照片",
ActionSheetDialog.SheetItemColor.Blue,
new ActionSheetDialog.OnSheetItemClickListener() {
@Override
public void onClick(int which) {
take();
}
})
.addSheetItem("上傳視訊",
ActionSheetDialog.SheetItemColor.Blue,
new ActionSheetDialog.OnSheetItemClickListener() {
@Override
public void onClick(int which) {
Toast.makeText(BrowserActivity.this, "呼叫視訊", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
//限制時長
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
//開啟攝像機
startActivityForResult(intent, 101);
}
})
// .addSheetItem("呼叫相簿",
// ActionSheetDialog.SheetItemColor.Blue,
// new ActionSheetDialog.OnSheetItemClickListener() {
// @Override
// public void onClick(int which) {
// Toast.makeText(BrowserActivity.this, "呼叫相簿", Toast.LENGTH_SHORT).show();
// Intent i = new Intent(Intent.ACTION_GET_CONTENT);
// i.addCategory(Intent.CATEGORY_OPENABLE);
// i.setType("image/*");
// startActivityForResult(Intent.createChooser(i, "選擇相簿"), 102);
// }
// })
.show();
return true;
}
};
public boolean flag = true;
@SuppressWarnings("null")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
if (requestCode != 100
|| uploadFiles == null) {
return;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
results = new Uri[]{imageUri};
} else {
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null)
results = new Uri[]{Uri.parse(dataString)};
}
}
if(results!=null){
uploadFiles.onReceiveValue(results);
uploadFiles = null;
}else{
results = new Uri[]{imageUri};
uploadFiles.onReceiveValue(results);
uploadFiles = null;
}
return;
}
private void take(){
File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");
// Create the storage directory if it does not exist
if (! imageStorageDir.exists()){
imageStorageDir.mkdirs();
}
File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
imageUri = Uri.fromFile(file);
final List<Intent> cameraIntents = new ArrayList<Intent>();
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
final PackageManager packageManager = getPackageManager();
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for(ResolveInfo res : listCam) {
final String packageName = res.activityInfo.packageName;
final Intent i = new Intent(captureIntent);
i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
i.setPackage(packageName);
i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
cameraIntents.add(i);
}
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
Intent chooserIntent = Intent.createChooser(i,"請選擇相簿或者拍照");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
BrowserActivity.this.startActivityForResult(chooserIntent, 100);
}
Uri result;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case 100://相片 拍照片
if (null == uploadFile && null == uploadFiles) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (uploadFiles != null) {
onActivityResultAboveL(requestCode, resultCode, data);
}
else if (uploadFile != null) {
Log.e("result",result+"");
if(result==null){
uploadFile.onReceiveValue(imageUri);
uploadFile = null;
Log.e("imageUri",imageUri+"");
}else {
uploadFile.onReceiveValue(result);
uploadFile = null;
}
}
flag = true;
break;
case 101://相機 拍攝視訊
if (null == uploadFile && null == uploadFiles) {
Log.d(TAG, "onActivityResult null");
return;
}
result = data == null || resultCode != RESULT_OK ? null : data.getData();
Log.d(TAG, "onActivityResult path=" + result.getPath());
if (uploadFiles != null) {
if (resultCode == RESULT_OK) {
Log.d(TAG, "onActivityResult 1");
uploadFiles.onReceiveValue(new Uri[]{result});
} else {
Log.d(TAG, "onActivityResult 2");
uploadFiles.onReceiveValue(new Uri[]{});
uploadFiles = null;
}
} else if (uploadFile != null) {
if (resultCode == RESULT_OK) {
uploadFile.onReceiveValue(result);
uploadFile = null;
} else {
Log.d(TAG, "onActivityResult 4");
uploadFile.onReceiveValue(Uri.EMPTY);
uploadFile = null;
}
}
break;
case 102:
if (null != uploadFile) {
result = data == null || resultCode != RESULT_OK ? null
: data.getData();
uploadFile.onReceiveValue(result);
uploadFile = null;
}
if (null != uploadFiles) {
result = data == null || resultCode != RESULT_OK ? null
: data.getData();
uploadFiles.onReceiveValue(new Uri[]{result});
uploadFiles = null;
}
break;
default:
break;
}
} else if (resultCode == RESULT_CANCELED) {
if (null != uploadFile) {
uploadFile.onReceiveValue(null);
uploadFile = null;
}
}
}
private Uri imageUri;
@Override
protected void onResume() {
super.onResume();
// 取消選擇時需要回調onReceiveValue,否則網頁會掛住,不會再響應點選事件
if (mResultCode == Activity.RESULT_CANCELED) {
try {
if (uploadFiles != null) {
uploadFiles.onReceiveValue(null);
}
if (uploadFile != null) {
uploadFile.onReceiveValue(null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//採用ios風格彈出框,進行多項選擇(呼叫相機、呼叫攝像)
public class ActionSheetDialog {
private Context context;
private Dialog dialog;
private TextView txt_title;
private TextView txt_cancel;
private LinearLayout lLayout_content;
private ScrollView sLayout_content;
private boolean showTitle = false;
private List<SheetItem> sheetItemList;
private Display display;
public ActionSheetDialog(Context context) {
this.context = context;
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
display = windowManager.getDefaultDisplay();
}
public ActionSheetDialog builder(final ValueCallback<Uri> uploadFile,final ValueCallback<Uri[]> uploadFiles) {
// 獲取Dialog佈局
View view = LayoutInflater.from(context).inflate(
R.layout.view_actionsheet, null);
// 設定Dialog最小寬度為螢幕寬度
view.setMinimumWidth(display.getWidth());
// 獲取自定義Dialog佈局中的控制元件
sLayout_content = (ScrollView) view.findViewById(R.id.sLayout_content);
lLayout_content = (LinearLayout) view
.findViewById(R.id.lLayout_content);
txt_title = (TextView) view.findViewById(R.id.txt_title);
txt_cancel = (TextView) view.findViewById(R.id.txt_cancel);
txt_cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) { //這裡如果不做處理,onShowFileChooser只會執行一次
// Toast.makeText(context,"按鈕取消",100).show();
if (uploadFiles != null) {
uploadFiles.onReceiveValue(null);
}
if (uploadFile != null) {
uploadFile.onReceiveValue(null);
}
dialog.dismiss();
}
});
// 定義Dialog佈局和引數
dialog = new Dialog(context, R.style.ActionSheetDialogStyle);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {//這裡如果不做處理,onShowFileChooser只會執行一次
// Toast.makeText(context,"點選螢幕取消",100).show();
if (uploadFiles != null) {
uploadFiles.onReceiveValue(null);
}
if (uploadFile != null) {
uploadFile.onReceiveValue(null);
}
}
});
dialog.setContentView(view);
Window dialogWindow = dialog.getWindow();
dialogWindow.setGravity(Gravity.LEFT | Gravity.BOTTOM);
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
lp.x = 0;
lp.y = 0;
dialogWindow.setAttributes(lp);
return this;
}
public ActionSheetDialog setTitle(String title) {
showTitle = true;
txt_title.setVisibility(View.VISIBLE);
txt_title.setText(title);
return this;
}
public ActionSheetDialog setCancelable(boolean cancel) {
dialog.setCancelable(cancel);
return this;
}
public ActionSheetDialog setCanceledOnTouchOutside(boolean cancel) {
dialog.setCanceledOnTouchOutside(cancel);
return this;
}
/**
*
* @param strItem
* 條目名稱
* @param color
* 條目字型顏色,設定null則預設藍色
* @param listener
* @return
*/
public ActionSheetDialog addSheetItem(String strItem, SheetItemColor color,
OnSheetItemClickListener listener) {
if (sheetItemList == null) {
sheetItemList = new ArrayList<SheetItem>();
}
sheetItemList.add(new SheetItem(strItem, color, listener));
return this;
}
/** 設定條目佈局 */
private void setSheetItems() {
if (sheetItemList == null || sheetItemList.size() <= 0) {
return;
}
int size = sheetItemList.size();
// TODO 高度控制,非最佳解決辦法
// 新增條目過多的時候控制高度
if (size >= 7) {
LinearLayout.LayoutParams params = (LayoutParams) sLayout_content
.getLayoutParams();
params.height = display.getHeight() / 2;
sLayout_content.setLayoutParams(params);
}
// 迴圈新增條目
for (int i = 1; i <= size; i++) {
final int index = i;
SheetItem sheetItem = sheetItemList.get(i - 1);
String strItem = sheetItem.name;
SheetItemColor color = sheetItem.color;
final OnSheetItemClickListener listener = (OnSheetItemClickListener) sheetItem.itemClickListener;
TextView textView = new TextView(context);
textView.setText(strItem);
textView.setTextSize(18);
textView.setGravity(Gravity.CENTER);
// 背景圖片
if (size == 1) {
if (showTitle) {
textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector);
} else {
textView.setBackgroundResource(R.drawable.actionsheet_single_selector);
}
} else {
if (showTitle) {
if (i >= 1 && i < size) {
textView.setBackgroundResource(R.drawable.actionsheet_middle_selector);
} else {
textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector);
}
} else {
if (i == 1) {
textView.setBackgroundResource(R.drawable.actionsheet_top_selector);
} else if (i < size) {
textView.setBackgroundResource(R.drawable.actionsheet_middle_selector);
} else {
textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector);
}
}
}
// 字型顏色
if (color == null) {
textView.setTextColor(Color.parseColor(SheetItemColor.Blue
.getName()));
} else {
textView.setTextColor(Color.parseColor(color.getName()));
}
// 高度
float scale = context.getResources().getDisplayMetrics().density;
int height = (int) (45 * scale + 0.5f);
textView.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, height));
// 點選事件
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onClick(index);
dialog.dismiss();
}
});
lLayout_content.addView(textView);
}
}
public void show() {
setSheetItems();
dialog.show();
}
public interface OnSheetItemClickListener {
void onClick(int which);
}
public class SheetItem {
String name;
OnSheetItemClickListener itemClickListener;
SheetItemColor color;
public SheetItem(String name, SheetItemColor color,
OnSheetItemClickListener itemClickListener) {
this.name = name;
this.color = color;
this.itemClickListener = itemClickListener;
}
}
public enum SheetItemColor {
Blue("#037BFF"), Red("#FD4A2E");
private String name;
private SheetItemColor(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
//與js互動
public class WebContrl {
private Context context;
X5WebView mWebView;
public WebContrl(Context context, X5WebView webView) {
this.context=context;
this.mWebView = webView;
}
@JavascriptInterface
public void finish(){
((Activity)context).finish();
}
@JavascriptInterface
public void toastMessage(String message) {
Log.i("toastMessage" , "傳遞過來的值是: "+message);
}
@JavascriptInterface
public String getMessage(String s){
return s+"world !";
}
}
//js部分程式碼
<script>
function showMsgInAndroid(){
webCtrl.finish();
}
</script>
<div class="header_left" onclick="showMsgInAndroid()"></>
<input type="file" text="點選拍照">
需要引入的庫
implementation 'com.zhy.base:fileprovider:1.0.0'
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.squareup.okio:okio:1.13.0'
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
implementation 'pub.devrel:easypermissions:1.2.0'
//許可權部分程式碼
checkWritePermission();
public static String[] PERMISSION = {Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE};
* 檢查讀寫許可權許可權
*/
private void checkWritePermission() {
boolean result = PermissionManager.checkPermission(this,PERMISSION);
if (!result) {
PermissionManager.requestPermission(this, Permission.WRITE_PERMISSION_TIP, Permission.WRITE_PERMISSION_CODE, PERMISSION);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//將請求結果傳遞EasyPermission庫處理
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
Toast.makeText(this, "使用者授權成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
Toast.makeText(this, "使用者授權失敗", Toast.LENGTH_SHORT).show();
/**
* 若是在許可權彈窗中,使用者勾選了'NEVER ASK AGAIN.'或者'不在提示',且拒絕許可權。
* 這時候,需要跳轉到設定介面去,讓使用者手動開啟。
*/
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this).build().show();
}
}
最後說明:專案剛結束、匆匆忙忙附上程式碼、註釋很多未註明,最後附上原始碼https://download.csdn.net/download/jsniitqwh/10710585