欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android给图片添加水印

程序员文章站 2022-06-16 09:41:10
1. 前言ps:最近在项目执行过程中有这样一个需求,要求拍完照的图片必须达到以上的效果。需求分析: 使用用预览布局surfaceview,在不局上方使用控件的方式来进行设计,最后通过截图的方式将...

1. 前言

Android给图片添加水印

ps:最近在项目执行过程中有这样一个需求,要求拍完照的图片必须达到以上的效果。需求分析:

  1. 使用用预览布局surfaceview,在不局上方使用控件的方式来进行设计,最后通过截图的方式将画面进行保存。
  2. 使用图片添加水印的方式来完成。

2. 方法1 使用surfaceview

我心想这不简单吗?于是开始一顿balabala的操作,结果到最后一步时发现,surfaceview居然不能进行截图,截图下来的图片居然是一张黑色的。简单地说这是因为surfaceview的特性决定的,我们知道安卓中唯一可以在子线程中进行绘制的view就只有surfaceview了。他可以独立于子线程中绘制,不会导致主线程的卡顿,至于造成surfaceview黑屏的原因,可以移步这里 android视图surfaceview的实现原理分析。如果非要使用此方式时还是有三种思路来进行解决: 采用三种思路:

1. 获取源头视频的截图作为surfaceview的截图
2. 获取surfaceview的画布canvas,将canvas保存成bitmap
3. 直接截取整个屏幕,然后在截图surfaceview位置的图

但是我觉得这种方式太过繁琐,所以选择用添加水印的式来完成。

3. 方法2 给拍照下来的图片添加水印

第一步:获取拍照权限

<!--相机权限-->
<uses-permission android:name="android.permission.camera" />
<!--访问外部权限-->
<uses-permission android:name="android.permission.read_external_storage" />

这里使用到郭霖大佬的开源库permissionx获取权限:

permissionx.init(this)
    .permissions(manifest.permission.camera,  manifest.permission.record_audio)
    .onexplainrequestreason { scope, deniedlist ->
        val message = "需要您同意以下权限才能正常使用"
        scope.showrequestreasondialog(deniedlist, message, "确定", "取消")
    }
    .request { allgranted, grantedlist, deniedlist ->
        if (allgranted) {
            opencamera()
        } else {
            toast.maketext(activity, "您拒绝了如下权限:$deniedlist", toast.length_short).show()
        }
    }

第二步:拍照

android 6.0以后,相机权限需要动态申请。

 // 申请相机权限的requestcode
   private static final int permission_camera_request_code = 0x00000012;

   /**
    * 检查权限并拍照。
    * 调用相机前先检查权限。
    */
   private void checkpermissionandcamera() {
       int hascamerapermission = contextcompat.checkselfpermission(getapplication(),
               manifest.permission.camera);
       if (hascamerapermission == packagemanager.permission_granted) {
           //有调起相机拍照。
           opencamera();
       } else {
           //没有权限,申请权限。
           activitycompat.requestpermissions(this,new string[]{manifest.permission.camera},
                   permission_camera_request_code);
       }
   }

   /**
    * 处理权限申请的回调。
    */
   @override
   public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) {
       if (requestcode == permission_camera_request_code) {
           if (grantresults.length > 0
                   && grantresults[0] == packagemanager.permission_granted) {
               //允许权限,有调起相机拍照。
               opencamera();
           } else {
               //拒绝权限,弹出提示框。
               toast.maketext(this,"拍照权限被拒绝",toast.length_long).show();
           }
       }
   }

调用相机进行拍照

申请权限后,就可以调起相机拍照了。调用相机只需要调用startactivityforresult传一个intent就可以了,但是这个intent需要传递一个uri,用于保存拍出来的图片,创建这个uri时,各个android版本有所不同,需要进行版本兼容。

  //用于保存拍照图片的uri
    private uri mcamerauri;

    // 用于保存图片的文件路径,android 10以下使用图片路径访问图片
    private string mcameraimagepath;

    // 是否是android 10以上手机
    private boolean isandroidq = build.version.sdk_int >= android.os.build.version_codes.q;

    /**
     * 调起相机拍照
     */
    private void opencamera() {
        intent captureintent = new intent(mediastore.action_image_capture);
        // 判断是否有相机
        if (captureintent.resolveactivity(getpackagemanager()) != null) {
            file photofile = null;
            uri photouri = null;

            if (isandroidq) {
                // 适配android 10
                photouri = createimageuri();
            } else {
                try {
                    photofile = createimagefile();
                } catch (ioexception e) {
                    e.printstacktrace();
                }

                if (photofile != null) {
                    mcameraimagepath = photofile.getabsolutepath();
                    if (build.version.sdk_int >= build.version_codes.n) {
                        //适配android 7.0文件权限,通过fileprovider创建一个content类型的uri
                        photouri = fileprovider.geturiforfile(this, getpackagename() + ".fileprovider", photofile);
                    } else {
                        photouri = uri.fromfile(photofile);
                    }
                }
            }

            mcamerauri = photouri;
            if (photouri != null) {
                captureintent.putextra(mediastore.extra_output, photouri);
                captureintent.addflags(intent.flag_grant_write_uri_permission);
                startactivityforresult(captureintent, camera_request_code);
            }
        }
    }

    /**
     * 创建图片地址uri,用于保存拍照后的照片 android 10以后使用这种方法
     */
    private uri createimageuri() {
        string status = environment.getexternalstoragestate();
        // 判断是否有sd卡,优先使用sd卡存储,当没有sd卡时使用手机存储
        if (status.equals(environment.media_mounted)) {
           return getcontentresolver().insert(mediastore.images.media.external_content_uri, new contentvalues());
        } else {
            return getcontentresolver().insert(mediastore.images.media.internal_content_uri, new contentvalues());
        }
    }

    /**
     * 创建保存图片的文件
     */
    private file createimagefile() throws ioexception {
        string imagename = new simpledateformat("yyyymmdd_hhmmss", locale.getdefault()).format(new date());
        file storagedir = getexternalfilesdir(environment.directory_pictures);
        if (!storagedir.exists()) {
            storagedir.mkdir();
        }
        file tempfile = new file(storagedir, imagename);
        if (!environment.media_mounted.equals(environmentcompat.getstoragestate(tempfile))) {
            return null;
        }
        return tempfile;
    }

接收拍照结果

  @override
    protected void onactivityresult(int requestcode, int resultcode, @nullable intent data) {
        super.onactivityresult(requestcode, resultcode, data);
        if (requestcode == camera_request_code) {
            if (resultcode == result_ok) {
                if (isandroidq) {
                    // android 10 使用图片uri加载
                    ivphoto.setimageuri(mcamerauri);
                } else {
                    // 使用图片路径加载
                    ivphoto.setimagebitmap(bitmapfactory.decodefile(mcameraimagepath));
                }
            } else {
                toast.maketext(this,"取消",toast.length_long).show();
            }
        }
    }

注意:

这两需要说明一下,android 10由于文件权限的关系,显示手机储存卡里的图片不能直接使用图片路径,需要使用图片uri加载。

另外虽然我在这里对android 10和10以下的手机使用了不同的方式创建uri 和加载图片,但其实android 10创建uri的方式和使用uri加载图片的方式在10以下的手机是同样适用的。 android 7.0需要配置文件共享。

<provider
    android:name="androidx.core.content.fileprovider"
    android:authorities="${applicationid}.fileprovider"
    android:exported="false"
    android:granturipermissions="true">
    <meta-data
        android:name="android.support.file_provider_paths"
        android:resource="@xml/file_paths" />
</provider>

在res目录下创建文件夹xml ,放置一个文件file_paths.xml(文件名可以随便取),配置需要共享的文件目录,也就是拍照图片保存的目录。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <!-- 这个是保存拍照图片的路径,必须配置。 -->
        <external-files-path
            name="images"
            path="pictures" />
    </paths>
</resources>

第三步:给拍照后得到的图片添加水印

  @override
    protected void onactivityresult(int requestcode, int resultcode, @nullable intent data) {
        super.onactivityresult(requestcode, resultcode, data);
        if (requestcode == camera_request_code) {
            if (resultcode == result_ok) {
                  bitmap mp;           
                   if (isandroidq) {
                        // android 10 使用图片uri加载
                        mp = mediastore.images.media.getbitmap(this.contentresolver, t.uri);
                    } else {
                        // android 10 以下使用图片路径加载
                        mp = bitmapfactory.decodefile(uri);
                    }
                    //对图片添加水印 这里添加一张图片为示例:
                    imageutil.drawtexttolefttop(this,mp,"示例文字",30,r.color.black,20,30)
                    } else {
                toast.maketext(this,"取消",toast.length_long).show();
            }
        }
    }

这里使用到一个imageutil工具类,我在这里贴上。如果需要使用可以直接拿走~

public class imageutil {
    /**
     * 设置水印图片在左上角
     *
     * @param context     上下文
     * @param src
     * @param watermark
     * @param paddingleft
     * @param paddingtop
     * @return
     */
    public static bitmap createwatermasklefttop(context context, bitmap src, bitmap watermark, int paddingleft, int paddingtop) {
        return createwatermaskbitmap(src, watermark,
                dp2px(context, paddingleft), dp2px(context, paddingtop));
    }

    private static bitmap createwatermaskbitmap(bitmap src, bitmap watermark, int paddingleft, int paddingtop) {
        if (src == null) {
            return null;
        }
        int width = src.getwidth();
        int height = src.getheight();
        //创建一个bitmap
        bitmap newb = bitmap.createbitmap(width, height, bitmap.config.argb_8888);// 创建一个新的和src长度宽度一样的位图
        //将该图片作为画布
        canvas canvas = new canvas(newb);
        //在画布 0,0坐标上开始绘制原始图片
        canvas.drawbitmap(src, 0, 0, null);
        //在画布上绘制水印图片
        canvas.drawbitmap(watermark, paddingleft, paddingtop, null);
        // 保存
        canvas.save(canvas.all_save_flag);
        // 存储
        canvas.restore();
        return newb;
    }

    /**
     * 设置水印图片在右下角
     *
     * @param context       上下文
     * @param src
     * @param watermark
     * @param paddingright
     * @param paddingbottom
     * @return
     */
    public static bitmap createwatermaskrightbottom(context context, bitmap src, bitmap watermark, int paddingright, int paddingbottom) {
        return createwatermaskbitmap(src, watermark,
                src.getwidth() - watermark.getwidth() - dp2px(context, paddingright),
                src.getheight() - watermark.getheight() - dp2px(context, paddingbottom));
    }

    /**
     * 设置水印图片到右上角
     *
     * @param context
     * @param src
     * @param watermark
     * @param paddingright
     * @param paddingtop
     * @return
     */
    public static bitmap createwatermaskrighttop(context context, bitmap src, bitmap watermark, int paddingright, int paddingtop) {
        return createwatermaskbitmap(src, watermark,
                src.getwidth() - watermark.getwidth() - dp2px(context, paddingright),
                dp2px(context, paddingtop));
    }

    /**
     * 设置水印图片到左下角
     *
     * @param context
     * @param src
     * @param watermark
     * @param paddingleft
     * @param paddingbottom
     * @return
     */
    public static bitmap createwatermaskleftbottom(context context, bitmap src, bitmap watermark, int paddingleft, int paddingbottom) {
        return createwatermaskbitmap(src, watermark, dp2px(context, paddingleft),
                src.getheight() - watermark.getheight() - dp2px(context, paddingbottom));
    }

    /**
     * 设置水印图片到中间
     *
     * @param src
     * @param watermark
     * @return
     */
    public static bitmap createwatermaskcenter(bitmap src, bitmap watermark) {
        return createwatermaskbitmap(src, watermark,
                (src.getwidth() - watermark.getwidth()) / 2,
                (src.getheight() - watermark.getheight()) / 2);
    }

    /**
     * 给图片添加文字到左上角
     *
     * @param context
     * @param bitmap
     * @param text
     * @return
     */
    public static bitmap drawtexttolefttop(context context, bitmap bitmap, string text, int size, int color, int paddingleft, int paddingtop) {
        paint paint = new paint(paint.anti_alias_flag);
        paint.setcolor(color);
        paint.settextsize(dp2px(context, size));
        rect bounds = new rect();
        paint.gettextbounds(text, 0, text.length(), bounds);
        return drawtexttobitmap(context, bitmap, text, paint, bounds,
                dp2px(context, paddingleft),
                dp2px(context, paddingtop) + bounds.height());
    }

    /**
     * 绘制文字到右下角
     *
     * @param context
     * @param bitmap
     * @param text
     * @param size
     * @param color
     * @return
     */
    public static bitmap drawtexttorightbottom(context context, bitmap bitmap, string text, int size, int color, int paddingright, int paddingbottom) {
        paint paint = new paint(paint.anti_alias_flag);
        paint.setcolor(color);
        paint.settextsize(dp2px(context, size));
        rect bounds = new rect();
        paint.gettextbounds(text, 0, text.length(), bounds);
        return drawtexttobitmap(context, bitmap, text, paint, bounds,
                bitmap.getwidth() - bounds.width() - dp2px(context, paddingright),
                bitmap.getheight() - dp2px(context, paddingbottom));
    }

    /**
     * 绘制文字到右上方
     *
     * @param context
     * @param bitmap
     * @param text
     * @param size
     * @param color
     * @param paddingright
     * @param paddingtop
     * @return
     */
    public static bitmap drawtexttorighttop(context context, bitmap bitmap, string text, int size, int color, int paddingright, int paddingtop) {
        paint paint = new paint(paint.anti_alias_flag);
        paint.setcolor(color);
        paint.settextsize(dp2px(context, size));
        rect bounds = new rect();
        paint.gettextbounds(text, 0, text.length(), bounds);
        return drawtexttobitmap(context, bitmap, text, paint, bounds,
                bitmap.getwidth() - bounds.width() - dp2px(context, paddingright),
                dp2px(context, paddingtop) + bounds.height());
    }

    /**
     * 绘制文字到左下方
     *
     * @param context
     * @param bitmap
     * @param text
     * @param size
     * @param color
     * @param paddingleft
     * @param paddingbottom
     * @return
     */
    public static bitmap drawtexttoleftbottom(context context, bitmap bitmap, string text, int size, int color, int paddingleft, int paddingbottom) {
        paint paint = new paint(paint.anti_alias_flag);
        paint.setcolor(color);
        paint.settextsize(dp2px(context, size));
        rect bounds = new rect();
        paint.gettextbounds(text, 0, text.length(), bounds);
        return drawtexttobitmap(context, bitmap, text, paint, bounds,
                dp2px(context, paddingleft),
                bitmap.getheight() - dp2px(context, paddingbottom));
    }

    /**
     * 绘制文字到中间
     *
     * @param context
     * @param bitmap
     * @param text
     * @param size
     * @param color
     * @return
     */
    public static bitmap drawtexttocenter(context context, bitmap bitmap, string text, int size, int color) {
        paint paint = new paint(paint.anti_alias_flag);
        paint.setcolor(color);
        paint.settextsize(dp2px(context, size));
        rect bounds = new rect();
        paint.gettextbounds(text, 0, text.length(), bounds);
        return drawtexttobitmap(context, bitmap, text, paint, bounds,
                (bitmap.getwidth() - bounds.width()) / 2,
                (bitmap.getheight() + bounds.height()) / 2);
    }

    //图片上绘制文字
    private static bitmap drawtexttobitmap(context context, bitmap bitmap, string text, paint paint, rect bounds, int paddingleft, int paddingtop) {
        android.graphics.bitmap.config bitmapconfig = bitmap.getconfig();

        paint.setdither(true); // 获取跟清晰的图像采样
        paint.setfilterbitmap(true);// 过滤一些
        if (bitmapconfig == null) {
            bitmapconfig = android.graphics.bitmap.config.argb_8888;
        }
        bitmap = bitmap.copy(bitmapconfig, true);
        canvas canvas = new canvas(bitmap);

        canvas.drawtext(text, paddingleft, paddingtop, paint);
        return bitmap;
    }

    /**
     * 缩放图片
     *
     * @param src
     * @param w
     * @param h
     * @return
     */
    public static bitmap scalewithwh(bitmap src, double w, double h) {
        if (w == 0 || h == 0 || src == null) {
            return src;
        } else {
            // 记录src的宽高
            int width = src.getwidth();
            int height = src.getheight();
            // 创建一个matrix容器
            matrix matrix = new matrix();
            // 计算缩放比例
            float scalewidth = (float) (w / width);
            float scaleheight = (float) (h / height);
            // 开始缩放
            matrix.postscale(scalewidth, scaleheight);
            // 创建缩放后的图片
            return bitmap.createbitmap(src, 0, 0, width, height, matrix, true);
        }
    }

    /**
     * dip转pix
     *
     * @param context
     * @param dp
     * @return
     */
    public static int dp2px(context context, float dp) {
        final float scale = context.getresources().getdisplaymetrics().density;
        return (int) (dp * scale + 0.5f);
    }
}

4. 最终实现的效果如下

Android给图片添加水印

5.总结

整体来说没有什么太大的问题,添加水印的原理就是通过canvas绘制的方式将文字/图片添加到图片上。最后再将修改之后的图片呈现给用户。同时也记录下surfaceview截图黑屏的问题。

以上就是android实现添加水印功能的详细内容,更多关于android 添加水印的资料请关注其它相关文章!

相关标签: Android 水印