前言
本文是基于在fragment中执行保存H5图片到本地相册的,相对于在acvitity中执行,削微有一点点复杂,但差别不大,代码中有明显区别

Step1:webview添加长按事件private WebView.HitTestResult hitTestResult;

webView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                 hitTestResult=webView.getHitTestResult();
                // 如果是图片类型或者是带有图片链接的类型
                if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE ||
                    hitTestResult.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE){
                    // 弹出保存图片的对话框
                    showBottomDialog();
                    return true;
                }
                return false;
            }
        });

Step2:弹出保存图片的底部对话框

private void showBottomDialog(){
        //1、使用Dialog、设置style
        final Dialog dialog = new Dialog(context);
        //2、设置布局
        View view = View.inflate(context,R.layout.dialog_custom_layout,null);
        dialog.setContentView(view);

        Window window = dialog.getWindow();
        //设置弹出位置
        window.setGravity(Gravity.BOTTOM);
        //设置弹出动画
//        window.setWindowAnimations(R.style.main_menu_animStyle);
        //设置对话框大小
        window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        dialog.show();

        dialog.findViewById(R.id.tv_take_photo).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.dismiss();
                picUrl = hitTestResult.getExtra();//获取图片链接
//                            保存图片到相册
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        urlToBitMap(picUrl);
                    }
                }).start();
            }
        });

        dialog.findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.dismiss();
            }
        });

    }

自定义弹窗布局R.layout.dialog_custom_layout就省略了,写了不利于装逼,勿怪Step3:将图片转为Bitmap

/**
     * 将图片转为Bitmap
     * @param picUrl
     */
    private void urlToBitMap(String picUrl) {
        Bitmap bitmap=null;
        try {
            URL iconUrl=new URL(picUrl);
            URLConnection connection=iconUrl.openConnection();
            HttpURLConnection httpURLConnection= (HttpURLConnection) connection;
            int length = httpURLConnection.getContentLength();
            connection.connect();
            inputStream=connection.getInputStream();
            bufferedInputStream=new BufferedInputStream(inputStream,length);
            bitmap=BitmapFactory.decodeStream(bufferedInputStream);
            bufferedInputStream.close();
            inputStream.close();
            if (bitmap != null){
                saveToAlbum(bitmap);
            }
        } catch (Exception e) {
            LogUtils.e("保存失败:",e.toString());
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
//                    Toast.makeText(context, "保存失败", Toast.LENGTH_SHORT).show();
                }
            });
            e.printStackTrace();
        }
    }

Step4:保存到相册(动态权限申请)

/**
     * 保存到相册
     * @param bitmap
     */
    private void saveToAlbum(Bitmap bitmap) {
        permissions= new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
//            手机系统6.0(23)以上动态申请权限
          int i=ContextCompat.checkSelfPermission(context,permissions[0]);
            if (i!=PackageManager.PERMISSION_GRANTED){
//                用户未授权,提醒授权
                ActivityCompat.requestPermissions((Activity) context,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},111);
            }else {
                appDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "kuaizai");
                if (!appDir.exists()) {
                    appDir.mkdirs();
                }
            }
        }else {
//            系统23以下不需要动态授权
             appDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "kuaizai");
             if (!appDir.exists()) {
                 appDir.mkdirs();
             }
        }
//        savePicture();
        String[] str = picUrl.split("/");
        String fileName = str[str.length - 1];
        if (appDir != null){
            File file = new File(appDir, fileName);
            try {
                fos = new FileOutputStream(file);
                //解决7.0系统打开sd卡找不到文件的问题
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
                    StrictMode.setVmPolicy(builder.build());
                }
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                fos.flush();
                fos.close();
                onSaveSuccess(file);
            } catch (final IOException e) {
                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "保存失败"+e.toString(), Toast.LENGTH_SHORT).show();
                        LogUtils.e("保存失败:",e.toString());
                    }
                });
                e.printStackTrace();
            }
        }else {
            Toast.makeText(context, "授权失败!", Toast.LENGTH_SHORT).show();
        }


    }

Step5 保存成功,通知系统更新相册

/**
     * 保存图片成功
     * @param file
     */
    private void onSaveSuccess(final File file) {
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ~~try {
                    MediaStore.Images.Media.insertImage(context.getContentResolver(),
                            file.getAbsolutePath(), file.getName(), null);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }~~ 
                //用以下方式替换上边try- catch内容,避免保存时在本地生成两次图片
                  ContentValues values = new ContentValues();
                values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
                Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
                // 最后通知系统更新相册
                context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
                Toast.makeText(context, "保存成功", Toast.LENGTH_SHORT).show();
            }
        });
    }

Step6,Fragment中动态权限申请回调处理

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 111:
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                urlToBitMap(picUrl);
                            }
                        }).start();
                    }else {
                        ToastUtils.showToast2(context,"用户拒绝!");
                        return;
                    }
                }
                break;
                default:
                    ToastUtils.showToast2(context,"保存失败!");
        }
    }

补充
上边步骤就是在webview中长按识别H5中的图片,并保存到手机系统相册,然后通知系统更新相册,整个过程只在Android端完成即可,并不需要像网上很多同僚说的需要和前端联调然后走交互方法才能完成,此方案仅在原生端即可完美解决,并且在各个版本包括pad上都是可以的。
但是开始也说了,如果你是在acvitity中进行上述操作,最后只需要让你的acvitity实现
ActivityCompat.OnRequestPermissionsResultCallback 接口,然后重写onRequestPermissionsResult方法就行了,记得一定要实现接口,不然重写的方法是不会走的,
但是如果在fragment中,执行完上述操作,你会惊喜的发现,fragment中的onRequestPermissionsResult回调方法依然没走,这个有两个原因,一个就是你的fragment也要和acvitity一样实现ActivityCompat.OnRequestPermissionsResultCallback 接口,还有就是你的fragment中onRequestPermissionsResult回调方法被你的载体acvitity给拦截了,所以要在acvitity的onRequestPermissionsResult回调方法中做一点手脚,让acvitity中的onRequestPermissionsResult回调方法传递给依赖于当前acvitity的Fragment,这样就nice了。也就是如下操作:

 

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        List<Fragment> fragments =getSupportFragmentManager().getFragments();
        if (fragments == null){
            return;
        }
        for (Fragment fragment
                : fragments) {
            if (fragment != null){
//                调用Fragment中的onRequestPermissionsResult
                fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
            }
        }
    }
}