项目需求讨论 - WebView下拍照及图片选择功能

前言:

现在很多app里面,都会有这么一个需求,就是上传图片的按钮,当然按了这个按钮之后,就会出现二种选择: 1. 直接拍照,2. 相册选择现有图片。

因为现在的app这块功能会有二个大的情况:

  1. 全部原生的 app 来实现。
  2. HyBrid 的 app 来实现。

本文先讨论HyBrid的app的实现情况,下次再讨论原生,不过其实大部分实现都是相似的。

其实这种在WebView配合下实现这类功能的文章很多很多,但是大多数都是上传一大段代码,然后让大家自己看,千篇一律,所以本文主要是写的完整的思路。

正文:

我们知道用户会在网页上点击了某个按钮,然后调用起安卓方面的相关操作。然后实现完整的功能。

1. 网页端:

其实网页端很简单,只需要实现一个简单的<input>标签即可。

总体思路是一个<input>标签和一个<img>标签重叠在一起(<input>在上,<img>在下,类似可以理解<img>作为背景),当选完照片后,最后把图片赋值给<img>标签。

但是在给<img>赋值的时候我遇到过不同的情况:

  1. 当在Android这边拍照或者进入图库选完照片后,把图片信息给了网页端后,<input>标签的onchange监听到了图片选择好了,网页端直接把图片上传到服务器并传回来一个地址,显示时把地址拼接成可以找到路径的地址放在<img>标签中就可以了。

  2. 配合FileReader,FileReader是作为文件API的重要成员用于读取文件。可以参考: h5 实现调用系统拍照或者选择照片并预览

2. Android端:

2.1 WebChromeClient

因为Android端访问网页大部分使用的是WebView,所以我们这里还是用WebView来说明。

既然用户在网页上点击了<input>,我们肯定需要WebView能监听到,好比原生的Button点击我们要监听也要写一个OnclickListener来实现监听。我们这里使用的是WebChromeClient

public class ImgWebChromeClient extends WebChromeClient {
        
      //.......
      //.......
        
     public ImgWebChromeClient(Activity activity) {
         this.activity = activity;
     }

        
        
}

我们实现我们的类,继承WebChromeClient。然后我们就可以把这个我们自己定义的WebChromeClient设置给我们的WebView。

webView.setWebChromeClient(new ImgWebChromeClient(this));

我们可以看到我们在WebChromeClient在监听<input>点击事件的时候,还要根据不同的版本来区分,主要是以Android 5.0版本来进行大的划分。

  1. Android 5.0及以上版本:


  2. Android 5.0以下版本:


都是openFileChooser方法,不同版本的里面参数不同。

所以我们可以看到主要是openFileChooseronShowFileChooser方法。

// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback) {
            ***
}

// For Android  >= 3.0
public void openFileChooser(ValueCallback valueCallback, String acceptType) {
            ***
}

//For Android  >= 4.1
public void openFileChooser(ValueCallback<Uri> valueCallback, 
                String acceptType, String capture) {
            ***
}

// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView, 
                ValueCallback<Uri[]> filePathCallback, 
                WebChromeClient.FileChooserParams fileChooserParams) {
            ***
     return true;
}

不管是什么版本,我们看到这几个方法的参数里面都有ValueCallback参数。

/**
 * A callback interface used to provide values asynchronously.
 */
public interface ValueCallback<T> {
    /**
     * Invoked when the value is available.
     * @param value The value.
     */
    public void onReceiveValue(T value);
};

所以我们知道了,我们最后是调用openFileChooseronShowFileChooser方法里面的ValueCallback参数,调用它的onReceiveValue方法把我们的选择的图片的Uri传入,网页端那边就会收到信息了,就知道用户到底选择了什么图片。

所以我们最终目标就是获取选择的图片的Uri,然后给ValueCallback.onReceiveValue方法,这就是我们整个文章的最主要的流程。

2.2 获取相关图片的Uri

上面我们提到了,我们最终目标是获取用户选取的图片Uri,然后传给ValueCallback就可以。
所以我们这里就要讲二大块:

  1. 用户怎么跳到自己想要的界面(相机 or 图库)
  2. 用户在自己想要的界面选择好了图片后 (拍好了照片 or 在图库选择好了图片),如何获取相关图片的Uri

根据这二点,我们一步步来分析。

2.2.1 相机 or 图库

我们肯定想到是用户点击了某个按钮后,我们需要跳出一个弹框,然后上面有拍照和图库按钮:
比如我使用系统自带的选择框(不同手机显示的弹框不同):


所以我们这里知道了这个又要细分任务:

  1. 获取相关权限
  2. 如何点击按钮后可以跳到相应界面(拍照 or 图库)。
  3. 如何创建弹框,把上面的按钮显示在上面
2.2.1.1 获取相关权限

emmm......这块我觉得应该不需要花更多的时间来说明了吧,主要就是:

  1. 检查权限 (checkSelfPermission)
  2. 请求权限(requestPermissions)
  3. 回调事件处理(onRequestPermissionsResult)

而我们要申请的权限无非就是 Camera的权限,还有读写外部存储的权限。

2.2.1.2 如何点击按钮后可以跳到相应界面(拍照 or 图库):

我们先来看拍照:

2.2.1.2.1 设置打开相机Intent的Action

我们知道打开某个界面,就是startActivity(Intent);平常比如跳到拨号界面等。只要对Intent设定相应的Action即可。
具体我们可以看谷歌的Android官方教程网页即可:

Android指南 - 通用 Intent

我们可以看到有这些:


我们可以这个目录中看到了相机,我们具体看相机的介绍:

:当您使用 ACTION_IMAGE_CAPTURE拍摄照片时,相机可能还会在结果 Intent 中返回缩小尺寸的照片副本(缩略图),这个副本以 Bitmap 形式保存在名为 dataextra 字段中。

所以我们这里跳到拍照界面也是一样,只要建立跳到相机界面的Intent即可:

Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
2.2.1.2.2 设置相机拍摄的照片的存储位置

因为有些人需要在自己的APP中调用拍照的功能,存在自己指定的目录下面,所以需要在startActivity启动相机界面时候同时传递过去信息,告诉拍照了之后照片存的位置。

1.我们先指定我们的要存储的照片的路径Uri:

其实很简单,设定我们接下去要拍的照片的完整存储路径,然后得到File对象,再通过Uri.fromFile方法再通过刚才我们的File对象来获得Uri

(当然如果这里你只需要打开系统相机,以下第二部分可以忽略)

2.获取所有相机的Intent集合:

因为我们手机上面可能有很多个相机软件,所以我们需要先找到能打开各自相机软件的Intent,我们通过PackageManager.queryIntentActivities的方式来进行符合拍照Action的Intent的软件,然后得到它们具体的详细信息,比如包名及对应的activity名字等,然后把相应的Intent变得更加详细即可。

3.把uri赋值给Intent:

在上面贴出的Android 官方网页上面的相机部分其实也提到过了如何设置存储位置:

所以这里我们只需要找到相应的Intent,然后把我们的Uri位置赋值给Intent即可:

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

最后只需要startActivity去启动我们这个指定了打开相机的特定Intent即可。


拍照说完了,我们再来看在图库界面选择图片:
其实总体思路和拍照是一模一样,无非就是指定Intent是打开了图库的Intent。

还是在刚才的Android 官网我们可以看到:

因为我们是查看本地的图片,所以我们要使用 ACTION_GET_CONTENT,同时指定MIME类型是图片类型,如果要进行图片多选,就再指定EXTRA_ALLOW_MULTIPLEtrue

同时也给出了实例代码:

这里我要提一下,我们在设置IntentAction的时候不只是可以使用ACTION_GET_CONTENT,还可以使用ACTION_PICK

我们可以看到上面写着可以用来选择数据,然后返回被选中的选项。

但是在具体手机操作上有点不同(不知道不同的手机系统会不会结果不同,我只测了模拟器):


ps:最坑的是用ACTION_GET_CONTEN时候多选图片要长按操作,一直以为没成功,以为多选图片功能没实现,后来在 Android: Intent.EXTRA_ALLOW_MULTIPLE allows only single picking 上面找到别人的答案,说需要长按

2.2.2 出现选择框让用户选择

我们可以看到可以自定义弹框,比如我们设定固定的按钮,然后再点击特定按钮后启动我们的上面提过的特定的Intent即可。

这里我们讲如果只是给定我们想要启动的多个Intent的选项,让系统帮我们弹出弹框及相关按钮,关键字就是Intent.createChooser方法

直接看图片即可,写的很详细了,或者大家搜相关的关键字也是有很多文章的。比如:Android createChooser方法源码简析等。

2.2.3 获取用户在相机或者图库选择的图片Uri


因为我们不是单纯的跳到了相机界面或者是图库界面就可以了,我们还需要获取用户在那些应用外的界面到底选了什么图片,所以单纯的startActivity肯定不够,所以大家肯定想到了使用startActivityForResult来启动,这样才能根据用户不同的操作来进行相应的处理。

我们知道需要复写onActivityResult来处理,主要也就三个参数(int requestCode, int resultCode, Intent data)。具体的内容图片里面也写的很清楚。


2.3 Uri 和 ValueCallback

所以我们ValueCallback实例在 WebChromeClient的方法里面拿到了,Uri也通过相机或者图库的选择下获取到了。最终调用把获取的Uri 赋值给ValueCallback.onReceiveValue()即可。

PS: 取消这次网页点击选取图片的请求,只需要调用onReceiveValue(null)即可。

结语:

emm.......大家轻喷即可。。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容