Android用webView包装WebAPP方法

前言 Android webView 兼容体验真的差到了极点!!

前一阵子,老板要将 WebAPP 放到 Android 和 iOS 里面,而我因为以前做过安卓,所以这方面就由我来打包,原理是很简单的,就是打开 APP 的时候用 webView 加载网站的网址,这样服务器一次更新,就能更新微信版, iOS 版和 Android 版;

首先我要说一句,如果你的 WebAPP 里面有文件上传,并且想要完全兼容,那么就别用原生的 WebAPP, 后面我会写一个关于 crossWalk 的博客,不过在此之前,我先记录下我所经历的一些坑,我的工具使用的是 Android studio;

创建一个项目,这个我就不说了,网上很多教程;

首先在 app/src/main/AndroidManifest.xml 里添加权限:

注意本文代码中的"..."都代表省略的代码

<manifest ...>
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  <application
      ...
  </application>
</manifest>
  • 第一个是允许访问网络连接;
  • 第二个是允许程序写入外部存储,如SD卡上写文件;
  • 第三个是允许应用程序从外部存储读取;

再是 app/src/main/res/layout/activity_main.xml 添加:

<WebView
    android:id="@+id/local_webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone" />

MainActivety.java:

private WebView webview;
//...
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    WebView.setWebContentsDebuggingEnabled(true);
  }

  webview = findViewById(R.id.local_webview);

  WebSettings settings = webview.getSettings();

  loading = findViewById(R.id.loadView);

  settings.setJavaScriptEnabled(true);//必须

  settings.setCacheMode(WebSettings.LOAD_DEFAULT);//关闭webview中缓存
  settings.setRenderPriority(WebSettings.RenderPriority.HIGH);//提高渲染的优先级
  settings.setUseWideViewPort(true);//WebView是否支持HTML的“viewport”标签或者使用wide viewport。
  settings.setAllowContentAccess(true);//是否允许在WebView中访问内容URL
  settings.setBuiltInZoomControls(true);//是否使用其内置的变焦机制
  settings.setJavaScriptCanOpenWindowsAutomatically(true);//是否允许自动打开弹窗
  settings.setDomStorageEnabled(true);//是否开启DOM存储API权限

  webview.loadUrl("http://www.baidu.com");

  webview.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
      Log.d("加载", "on page progress changed and progress is " + newProgress);
      //...
    }

  }

  webview.setWebViewClient(new WebViewClient() {
    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
      super.onReceivedError(view, errorCode, description, failingUrl);
          // 加载网页失败时处理 如:
      view.loadDataWithBaseURL(null,
        "<span>页面加载失败,请确认网络是否连接</span>",
        "text/html",
        "utf-8",
        null);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
      if (!webview.getSettings().getLoadsImagesAutomatically()) {
        webview.getSettings().setLoadsImagesAutomatically(true);
      }
      Log.d("加载", "end ");
    }

  });
}

这是一个比较简单的 webView 例子,这里有几点需要说下:

关于WebSettings:

1.1 需要运行 js 的网页都需要此设置:setJavaScriptEnabled

1.2 关于setCacheMode,尽量不要设置 LOAD_CACHE_ONLY 该值,设置这个值会在 webkit 类型浏览器对短时间内的 ajax 访问产生Provisional headers are shown问题;

1.3 关于 AllowFileAccess 一般默认值就好,都开了会有安全上的问题;

1.4 WebSettings 的设置内容很多,如果想看更多的话可以进行搜索;

1.5 暂未发现其他问题,待定;

setWebChromeClient 和 setWebViewClient:

2.1 这2个都是 webView 的配置属性,不过在功能上有所区分:

WebViewClient帮助WebView处理各种通知、请求事件的

WebChromeClient是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等;

js 里面使用 alert 和 confirm 需要在WebChromeClient里面进行修改,提供对话框;

2.2 关于onPageFinished:

如果你的路由里面是异步加载的,如resolve => require(['./routers/XXX'], resolve),那么就要注意,在每进入异步加载的页面后,都会触发此函数,所以如果你需要在页面加载后只执行一次的代码的话,就放在 setWebChromeClient 的 onProgressChanged 里进行判断进度是否为100时再执行;

webview.loadUrl():

3.1 这里的加载地址可以有2种,1是 webview.loadUrl("file:///android_asset/index.html"); 访问本地文件,2是webview.loadUrl("http://www.baidu.com");访问网络文件;

各有其优点:若访问网络文件,更新服务器内容即可使用最新的功能;而访问本地资源的话,加载的速度会快一点,而且即使断网也可以看到默认的东西;

刚刚有说到,进入 APP 的快慢问题,这里我是调用了一个加载的动画来完成的:

我这边选择的动画时这个:点击查看

而在 Android studio 里调用插件的方式十分简单:

打开根目录下的 build.gradle,在 allprojects 的 repositories 里添加:

maven {
 url "https://jitpack.io"
}

然后打开 app/build.gradle,在 dependencies 里添加:

compile 'com.github.zzz40500:android-shapeLoadingView:1.0.3.2'

这时候先 build 项目,再在 src/main/res/layout/activity_main.xml 里添加代码:

<android.support.constraint.ConstraintLayout >

  <com.mingle.widget.LoadingView
    android:id="@+id/loadView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

  ...

</android.support.constraint.ConstraintLayout>

这时候可以,这样 loading 动画就添加好了,后面只需要在 Java 代码里显示和隐藏就行了;

最关键的html:input[type="file"]问题,这个问题才是最大的问题,先说好
如果你的webApp不需要上传文件或者不在意Android 4.2-4.4 版本的话,可以用该方法
MainActivity.java:
先创建变量

public static final int INPUT_FILE_REQUEST_CODE = 1;
  private ValueCallback<Uri> mUploadMessage;
  private final static int FILECHOOSER_RESULTCODE = 2;
  private ValueCallback<Uri[]> mFilePathCallback;
  private String mCameraPhotoPath;

在setWebChromeClient里添加代码:

public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        Log.d("选择", "3.0+");
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        MainActivity.this.startActivityForResult(
            Intent.createChooser(i, "Image Chooser"),
            FILECHOOSER_RESULTCODE);
      }

      //Android 5.0
      public boolean onShowFileChooser(
          WebView webView, ValueCallback<Uri[]> filePathCallback,
          WebChromeClient.FileChooserParams fileChooserParams) {
        Log.d("选择", "5.0+");
        if (mFilePathCallback != null) {
          mFilePathCallback.onReceiveValue(null);
        }

        mFilePathCallback = filePathCallback;

        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
          // Create the File where the photo should go
          File photoFile = null;
          try {
            //设置MediaStore.EXTRA_OUTPUT路径,相机拍照写入的全路径
            photoFile = createImageFile();
            takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
          } catch (Exception ex) {
            // Error occurred while creating the File
            Log.e("WebViewSetting", "Unable to create Image File", ex);
          }

          // Continue only if the File was successfully created
          if (photoFile != null) {
            mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                Uri.fromFile(photoFile));
            System.out.println(mCameraPhotoPath);
          } else {
            takePictureIntent = null;
          }
        }

        Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
        contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
        contentSelectionIntent.setType("image/*");
        Intent[] intentArray;
        if (takePictureIntent != null) {
          intentArray = new Intent[]{takePictureIntent};
          System.out.println(takePictureIntent);
        } else {
          intentArray = new Intent[0];
        }

        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
        chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
        chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

        startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);

        return true;
      }
      // For Android 3.0+
      public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        Log.d("选择", "3.0+");

        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        MainActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE);

      }

      //For Android 4.1
      public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        Log.d("选择", "4+");
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        MainActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), MainActivity.FILECHOOSER_RESULTCODE);

      }

在主类中添加:

@SuppressLint("SdCardPath")
  private File createImageFile() {
    File file=new File(Environment.getExternalStorageDirectory()+"/","tmp.png");
    mCameraPhotoPath=file.getAbsolutePath();
    if(!file.exists())
    {
      try {
        file.createNewFile();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return file;
  }
   @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      Log.d("result", "show");
      if (requestCode == FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage) return;
        Uri result = data == null || resultCode != RESULT_OK ? null
            : data.getData();
        if (result != null) {
          String imagePath = ImageFilePath.getPath(this, result);
          if (!TextUtils.isEmpty(imagePath)) {
            result = Uri.parse("file:///" + imagePath);
          }
        }
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
      } else if (requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
        // 5.0的回调
        Uri[] results = null;

        // Check that the response is a good one
        if (resultCode == Activity.RESULT_OK) {
          if (data == null && !TextUtils.isEmpty(data.getDataString())) {
            // If there is not data, then we may have taken a photo
            if (mCameraPhotoPath != null) {
              results = new Uri[]{Uri.parse(mCameraPhotoPath)};
            }
          } else {
            String dataString = data.getDataString();
            if (dataString != null) {
              results = new Uri[]{Uri.parse(dataString)};
            }
          }
        }

        mFilePathCallback.onReceiveValue(results);
        mFilePathCallback = null;
      } else {
        super.onActivityResult(requestCode, resultCode, data);
        return;
      }
    }

这里还需要一个 ImageFilePath 类文件,我将他放在 GitHub 里面了,后面我会附上链接:

至于 Android 4.2-4.4 会有问题是因为这个:点击查看

ps:需要FQ

而如果你是 native 开发者的话也比较容易解决,就是在点击时直接用 js 调用 Java 就行了,如果不是的话,一般都需要其他框架或者插件的支持;

我所碰到的问题基本就是这些,如果有错误和疏漏之处还请指出,谢谢;

GitHub:https://github.com/Grewer/android-casing-webapp

您可能感兴趣的文章:

  • Android webview转PDF的方法示例
  • Android WebView实现长按保存图片及长按识别二维码功能
  • Android 保存WebView中的图片示例
  • Android 中WebView 截图的实现方式
  • Android点击WebView实现图片缩放及滑动浏览效果
  • Android WebView中图片浏览及缩放效果
  • android中webview定位问题示例详解
  • 实例详解Android Webview拦截ajax请求
  • Android自定义webView头部进度加载效果
  • Android中WebView的基本配置与填坑记录大全
(0)

相关推荐

  • Android webview转PDF的方法示例

    1.网上找了好多没有显示出来效果不错,后来看到调用手机打印预览,看了效果还不错,就打算使用系统打印服务预览下载 2.'webView.createPrintDocumentAdapter()'得到打印的PrintDocumentAdapter有了该类就可以使用onWrite方法写入制定的文件,但是这个方法需要传入回调这个悲剧的是这个回调方法是hiden的我们没办法调用 3,字怎么解决呢,有连个方法 3.1 使用此开源库替换自己的sdk 中的android.jar文件,就可以使用了 https:/

  • Android点击WebView实现图片缩放及滑动浏览效果

    最近做的项目有一个要求,就是在WebView中显示的html,需要在点击其中的图片时进行放大,并进行缩放和滑动 浏览,我第一想到的是这是和js进行交互的事情,但是怎么获取html中图片的url,并保存起来进行显示,我就不知道 了,所以去查了下资料,最后找到了解决的办法: 博客地址:Android WebView中图片浏览及缩放效果 首先说一下处理这个要求的思路,首先我们要获取到html中的所有图片的url,并保存到集合中,当点击图片时,跳转 一个Activity用ViewPager进行显示,这样

  • android中webview定位问题示例详解

    前言 现在很多App里都内置了Web网页(Hyprid App),比如说很多电商平台,淘宝.京东.聚划算等等 京东首页 那么这种该如何实现呢?其实这是Android里一个叫WebView的组件实现的. 最近在做安卓的网页开发.有一个页面需要用到定位,但是一直定位获取失败.很难过.网上教程也很多,但是无一例外全部失败.最后老夫花了3天时间,呕心沥血,终于研制出了解决方案. 三步走战略: 一.获取权限 android 6.0 以后,需要动态的获取位置或者存储权限,按照各自的爱好放置位置.我是应用开启

  • Android WebView中图片浏览及缩放效果

    本文实例为大家分享了Android WebView图片浏览及缩放效果展示的具体代码,供大家参考,具体内容如下 此工程用到了两个开源库: PhotoView支持图片的缩放 Android-Universal-Image-Loader图片的异步加载 (android studio)将两个源工程中的library文件夹导入到Demo Module所在的Project中,修改各自的build.gradle文件,让里面的版本号.所用的android包等与Demo Module相同即可.大致如图: 源代码:

  • Android 保存WebView中的图片示例

    前言 项目中有需求在APP的Webview中长按图片可以保存.后来就去研究一下该怎么实现,顺便整理了一下. WebView基本配置 mWvContent.getSettings().setJavaScriptEnabled(true); mWvContent.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); mWvContent.getSettings().setDomStorageEnabled(false); mWvC

  • Android中WebView的基本配置与填坑记录大全

    前言 在应用程序开发过程中,经常会采用webview来展现某些界面,这样就可以不受发布版本控制,实时更新,遇到问题可以快速修复. 但是在Android开发中,由于Android版本分化严重,每一个版本针对webview都有部分更改,因此在开发过程中会遇到各种各样的坑,下面这篇就来给大家介绍关于Android中WebView的基本配置与填坑记录,话不多说了,来一起看看详细的介绍吧. 基本配置 // 硬件加速 getActivity().getWindow().setFlags( WindowMan

  • Android 中WebView 截图的实现方式

    Hybrid App 中网页部分的分享方式越来越趋向于多元化,比较常见的用户操作方式有:复制网页链接式,直接选择目标应用自动分享式等.其中,截图行为,越来越成为丰富用户操作.备受用户喜爱的互动方式之一,我们在很多内容社区类应用中都能看到这种功能.这篇文章总结一下 Android 应用中 WebView 截图的实现方式. WebView 作为一种特殊的控件,自然不能像其他系统 View 或者截屏的方式来获取截图(多为截取长图).如: public static Bitmap getScreenSh

  • Android WebView实现长按保存图片及长按识别二维码功能

    先来简单说一下本文所要实现的功能:用户在浏览网页的时候,长按某一区域,识别如果是图片,则弹出弹框,出现保存图片的功能.同时识别图片是否是二维码,如果是则在弹框中追加识别二维码功能. 细节上:保存图片的弹框要显示在手指长按的位置:选择图片保存后,可以让用户直接去相册查看:选择识别二维码,判断是是不是网址,是的话可以让用户选择复制或访问,否则可以让用户选择复制或搜索. 然后再来看一下效果图: 保存图片 save.gif 识别包含普通文字的二维码: text.gif 识别包含网址的二维码: code.

  • 实例详解Android Webview拦截ajax请求

    Android Webview虽然提供了页面加载及资源请求的钩子,但是对于h5的ajax请求并没有提供干涉的接口,这意味着我们不能在webview中干涉javascript发起的http请求,而有时候我们确实需要能够截获ajax请求并实现一些功能如:统一的网络请求管理.cookie同步.证书校验.访问控制等. 思路 虽然在 Webview中无法直接拦截 ajax请求(其实在shouldInterceptRequest 中是可以收到ajax请求的,但是遗憾的是取不到请求参数,这样也是没有意义的),

  • Android自定义webView头部进度加载效果

    不多说先来看下效果图: 1. 颜色渐变加载进度条(夜神模拟器) 绿色加载进度条(魅蓝note2) 看图说话: 上图是不是加载网页的时候会有一个进度条在横向加载,比以前网速不好的时候是一片空白给人的感觉友好多了是不,然后效果还不错. 实现思路 就是自己画一条进度线(大家应该都会吧)然后加载到WebView的上面,开始进度条是隐藏的,进度线初始值为1,然后为了效果好一点,初始少于10的进度都让它加载到10的位置,等进度到100的时候0.2秒后隐藏. 请记得添加网络权限: <uses-permissi

随机推荐