最近项目中存在图片加水印效果的需求,具体效果如下:

然后做出来的效果如下:

原图 水印图

点击可以查看大图:大图

那么针对这种比较复杂的水印图片,应该如何去做呢?下面我分享一下自己的思路。
如果没有使用到NDK,单纯的使用Android提供的Canvas画布,那么就有一下几个步骤:

  1. 获取原始的图片地址,转化成为 sourceBitmap;
  2. 获取水印图片的Bitmap;
  3. 使用Canvas,将sourceBitmap作为底片,然后将水印Bitmap画上去;
  4. 然后将二者合并的Bitmap,保存成文件即可。

那么按照这个步骤来:

1. 原始图片赚Bitmap

这个一般很简单,用代码表示为:

Bitmap sourceBitmap = BitmapFactory.decodeFile(sourcePath);

2. 获取水印图片的Bitmap

对于复杂的水印图片,我现在的做法是,把水印图片转化成一个View,然后把View转成Bitmap。
比如上图的复杂水印图片,我们可以先画一个XML:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="@dimen/dp_18"><TextViewandroid:id="@+id/id_tv_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="40sp"style="@style/phone_water_mark_text_style"android:textStyle="bold"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Viewandroid:layout_width="@dimen/dp_2"app:layout_constraintTop_toTopOf="@id/id_tv_time"app:layout_constraintBottom_toBottomOf="@id/id_tv_time"app:layout_constraintStart_toEndOf="@id/id_tv_time"android:layout_marginStart="@dimen/dp_8"android:background="@color/app_theme_color"android:id="@+id/id_center_view"android:layout_marginTop="@dimen/dp_10"android:layout_marginBottom="@dimen/dp_4"android:layout_height="0dp"/><TextViewandroid:layout_width="wrap_content"app:layout_constraintTop_toTopOf="@id/id_tv_time"app:layout_constraintStart_toEndOf="@id/id_center_view"android:layout_marginStart="@dimen/dp_8"android:textSize="14sp"style="@style/phone_water_mark_text_style"android:layout_marginTop="@dimen/dp_6"android:id="@+id/id_tv_date"android:layout_height="wrap_content"/><TextViewandroid:layout_width="wrap_content"app:layout_constraintBottom_toBottomOf="@id/id_tv_time"app:layout_constraintStart_toStartOf="@id/id_tv_date"style="@style/phone_water_mark_text_style"android:textSize="14sp"android:id="@+id/id_tv_week"android:layout_marginBottom="@dimen/dp_4"android:layout_height="wrap_content"/><LinearLayoutandroid:layout_width="0dp"android:orientation="vertical"app:layout_constraintTop_toBottomOf="@id/id_tv_time"android:layout_marginTop="@dimen/dp_8"app:layout_constraintStart_toStartOf="@id/id_tv_time"app:layout_constraintEnd_toEndOf="parent"android:id="@+id/id_external_info_layout"android:layout_height="wrap_content"><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/id_tv_user_name"/><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/id_tv_user_location"      /></LinearLayout></androidx.constraintlayout.widget.ConstraintLayout>

XML解析之后,大致的轮廓我们可以看到:

我们可以看到,XML渲染的大致轮廓就出来了,那么应该如果将View转成Bitmap呢?其实Android早就提供了类似的方法:

    fun getCurrentBitmap(): Bitmap? {invalidate()val density = resources.displayMetrics.densityBaseLog.d("density = $density")val bitmapWidth = (width * density).toInt()val bitmapHeight = (height * density).toInt()val resultBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)val canvas = Canvas(resultBitmap)canvas.scale(density, density)draw(canvas)return  resultBitmap}

代码中我新增了 density,这是为了防止不同屏幕密度下UI效果不同做的一个缩放。

3.4. Bitmap上面画水印Bitmap, 然后保存

还是先上代码吧,注释也比较清晰:

/*** 添加水印** @param sourcePath 原始图片地址* @param watermarkBitmap 图片水印* @return 添加水印后的图片地址*/public String addWaterMarkLayout(String sourcePath, Bitmap watermarkBitmap) {// 检查文件是否存在if (!BaseFileUtils.isFileExist(sourcePath)) {return sourcePath;}// 检查水印图片是否存在if (null == watermarkBitmap){BaseLog.e("watermark bitmap is null");return sourcePath ;}int[] bitmapWidthHeight = BaseImageUtils.getBitmapWidthHeight(sourcePath);if (bitmapWidthHeight[0] <= 0 || bitmapWidthHeight[1] <= 0) {return sourcePath;}Bitmap sourceBitmap = BitmapFactory.decodeFile(sourcePath);if (null == sourceBitmap){BaseLog.e("source bitmap is null : " + sourcePath);return sourcePath;}//创建一个底仓BitmapBitmap copiedBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight(), Bitmap.Config.ARGB_8888);Canvas targetCanvas = new Canvas(copiedBitmap);targetCanvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));targetCanvas.drawBitmap(sourceBitmap, 0,0,null);// 获取watermarkBitmap 和 sourceBitmap之间的比例,好进行缩放int[] getSample          = checkSample(sourceBitmap, watermarkBitmap, BaseImageUtils.getOrientation(sourcePath));float scaleWidth =  getSample[0] * 1.0f  / watermarkBitmap.getWidth();float scaleHeight = getSample[1] * 1.0f / watermarkBitmap.getHeight();// CREATE A MATRIX FOR THE MANIPULATIONMatrix matrix = new Matrix();// RESIZE THE BIT MAPmatrix.postScale(scaleWidth, scaleHeight);//true : 以启用双线性过滤 为了就是不产生锯齿Bitmap scaledBitmap  = Bitmap.createBitmap(watermarkBitmap,0,0,watermarkBitmap.getWidth(),watermarkBitmap.getHeight(),matrix,true);Paint canvasPaint = new Paint();//抗锯齿canvasPaint.setAntiAlias(true);//双线性过滤canvasPaint.setFilterBitmap(true);// 画目标水印Bitmap// 因为是画的位置是 左下角,因此x为0,y为底座Bitmap的高度 - 缩放之后的水印Bitmap的高度 targetCanvas.drawBitmap(scaledBitmap, 0, copiedBitmap.getHeight() - getSample[1], canvasPaint);//创建一个新的临时地址,请来保存合并之后的BitmapString newWaterMarkPath = BaseIOUtils.getTempImageFilePath(context);//保存合并之后的Bitmap到临时文件LzMarkBitmapUtils.saveBitmapToFile(copiedBitmap, newWaterMarkPath,85);bitmapWidthHeight = BaseImageUtils.getBitmapWidthHeight(newWaterMarkPath);if (bitmapWidthHeight[0] > 0 && bitmapWidthHeight[1] > 0) {//不能删除原始图片,因为Android系统会拦截BaseIOUtils.renameUnusedFileData(sourcePath);return newWaterMarkPath;}BaseLog.e("bitmapWidthHeight error");return sourcePath;}private int[] checkSample(Bitmap sourceBitmap, Bitmap waterMarkBitmap, int getOrientation) {int sourceBitmapWidth  = sourceBitmap.getWidth();int sourceBitmapHeight = sourceBitmap.getHeight();int waterMarkWidth     = waterMarkBitmap.getWidth();int waterMarkHeight    = waterMarkBitmap.getHeight();BaseLog.i("sample:" + sourceBitmapWidth + "," + sourceBitmapHeight+ "," + waterMarkWidth + "," + waterMarkHeight + "," + getOrientation);if (getOrientation == 90 || getOrientation == 270){ //横向sourceBitmapWidth = sourceBitmap.getHeight();if (waterMarkWidth > sourceBitmapWidth * 0.5f){waterMarkWidth = (int) (sourceBitmapWidth * 0.65f);waterMarkHeight = waterMarkBitmap.getHeight() * waterMarkWidth / waterMarkBitmap.getWidth();}} else  {if (waterMarkWidth > sourceBitmapWidth * 0.8f) {waterMarkWidth = (int) (sourceBitmapWidth * 0.8f);waterMarkHeight = waterMarkBitmap.getHeight() * waterMarkWidth / waterMarkBitmap.getWidth();}}BaseLog.i("show the water mark width :" + waterMarkWidth + "," + waterMarkHeight);return new int[]{(int) (waterMarkWidth * 1.3f), (int) (waterMarkHeight * 1.3f)};}

使用过程中出现的问题

1. 压缩问题

生成的水印图片需要上传到服务器的,为了节省带宽和流量,因此压缩是必不可少少的。我在很长一段时间内,采取的步骤为:

原始Bitmap + 水印Bitmap -> 生成带有水印的图片 -> 压缩 -> 上传

发现效果比较差,水印比较模糊,尝试了很多种方法,比如使用Paint防锯齿,先放大水印图片等等,但是效果不是很好,后来采取了另外一种思路:

原始Bitmap -> 压缩 -> 保存成文件 -> 转化成新的Bitmap + 水印Bitmap -> 合成新的Bitmap -> 保存成JPG文件 -> 上传

和上面的思路换了之后,我只合成压缩之后的Bitmap,对水印的Bitmap不进行压缩,发现效果很好。

2. 字体比较小的TextView比较模糊

对于View上的TextView如果设置字体比较小,生成的Bitmap合并之后会发现比较模糊,这可能是因为View的分辨率和最终生成的Bitmap分辨率不一致所导致的。为了解决这个问题,可以从以下几个点考虑:

  1. 调整View的分辨率
    当创建水印Bitmap时,确保View的尺寸与水印Bitmap的尺寸一致。你可以通过设置View的LayoutParams来调整View的尺寸
  1. 使用高质量的缩放方法

在使用Bitmap.createBitmap()时,确保使用高质量的缩放方法来生成带有水印的图片。使用Bitmap.createScaledBitmap()方法可以实现这一目的。

Bitmap originalBitmap = ...; // 原始图片
Bitmap watermarkBitmap = ...; // 水印图片
int watermarkWidth = watermarkBitmap.getWidth();
int watermarkHeight = watermarkBitmap.getHeight();// 缩放水印图片
int newWatermarkWidth = ...; // 新的水印宽度
int newWatermarkHeight = ...; // 新的水印高度
Bitmap scaledWatermarkBitmap = Bitmap.createScaledBitmap(watermarkBitmap, newWatermarkWidth, newWatermarkHeight, true);
  1. 使用适当的质量参数进行Bitmap保存

在将Bitmap保存为JPEG或PNG格式的图片时,确保使用适当的质量参数。一般情况下,JPEG的质量参数范围是0-100,PNG是无损压缩,质量参数对其没有影响。

Bitmap finalBitmap = ...; // 最终生成的带有水印的图片
FileOutputStream outputStream = ...; // 输出流
int quality = 100; // 质量参数finalBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);

基本上一个稍微负责的水印图片,我们基本上就成功了,如果有什么疑问或者有什么错误,请及时指出,或者可以联系我wx:javainstalling,备注:水印 即可。

Android如何做出带有复杂水印的图片相关推荐

  1. 新生研讨课:利用OpenCV处理带有水印的图片的调研报告

    电子科技大学 格拉斯哥学院 2017级 2017200602038 席文骥 目录 1.引言 2.技术背景 3.具体问题 4.实现方法 5.结语 6.参考 1.引言 在我们学院去年开设的新生研讨课上,曾 ...

  2. android 涂鸦之图片叠加,android图像处理系列之七--图片涂鸦,水印-图片叠加...

    图片涂鸦和水印其实是一个功能,实现的方式是一样的,就是一张大图片和一张小点图片叠加即可.前面在android图像处理系列之六--给图片添加边框(下)-图片叠加中也讲到了图片叠加,里面实现的原理是直接操 ...

  3. android水印控件,Android图片添加文字水印并保存水印文字图片到指定文件

    Android图片添加文字水印并保存水印文字图片到指定文件package zhangphil.test;import android.graphics.Bitmap;import android.gr ...

  4. 不会ps可不可以html5开发,H5周边丨不用ps如何做出一张好看的图片

    1. 创客贴 网址: https://www.chuangkit.com/dc.html 功能:如果你只是想制作一张由背景和文字构成的图片,放在H5里面,堂妹儿比较推荐"创客贴"这 ...

  5. Android 使用ViewPager 做的半吊子的图片轮播

    Android 使用ViewPager 做的半吊子的图片轮播 效果图 虽然不咋样,但是最起码的功能是实现了,下面我们来一步步的实现它. 界面 下面我们来分析一下界面的构成 整体的布局: 因为我们要做出 ...

  6. css3 实现盒子四周光晕_使用CSS3做出带有光晕流星旋转光环的效果 -

    ...章给大家带来的内容是关于CSS3属性:text-shadow文本阴影的使用方法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助.text-shadow还没有出现时,大家在网页设计中 ...

  7. Android:加载网图时精确获取图片格式

    一.开始挖坑 项目中有一个点击查看大图的需求,并且在大图模式下支持手势缩放,所以,我们必然会用到 chrisbanes 大神的 PhotoView,主要使用的是其中的PhotoView 和 Photo ...

  8. Graphics2D 在一张图片上添加一个带有透明背景的图片或绘制透明图片

    目录 代码实例 代码实例 // 读取原图片信息 底图//得到文件File file = new File("d:\\1.png");//文件转化为图片Image srcImg = ...

  9. 怎么消除图片中的水印?图片去水印工具

    在工作中当我们在平台上找一些图片素材发现带有水印,那么如何获得一张无水印的图片呢? 第一步,打开浏览器搜索工具名称,进入官方网站上点击获取它,即可获取.然后我们点击启动,进入首页,我们可以看到它各种强 ...

最新文章

  1. 当前订单不支持只花呗支付是什么意思_1、(跑腿介绍篇)支付宝花呗分期线下推广...
  2. 机器学习实战读书笔记--决策树
  3. shiro表单认证(系统默认的form认证器)
  4. 【技术综述】深度学习中的数据增强(下)
  5. java collection api_Java Stream和Collection比较:何时以及如何从Java API返回?
  6. gson-2.2.api简单
  7. 你为什么喜欢VIM?
  8. Jmeter中JDBC Connection Configuration实现MySQL JDBC Request数据库处理
  9. STM32_ADC初始化参数说明以及常用的固件库
  10. Android 进行单元測试难在哪-part3
  11. 我国三大常用坐标系区别(北京54、西安80和WGS-84)
  12. 一个月通过软考中级软件设计师
  13. 使用Python的pandas库操作Excel
  14. Mybatis_select、insert、update、delete常用属性
  15. opencv warp(扭曲)球面投影的原理
  16. 论文阅读笔记 | Enemy At the Gateways:Censorship-Resilient Proxy Distribution Using Game Theory(NDSS 2019)
  17. 小知识--Windows10许可证即将过期
  18. 毕业论文查重软件如何论文查重?
  19. 一个有意思的echarts3D树状图
  20. druid的后台监控

热门文章

  1. 编程之美---数字之魅
  2. python刷题:哥德巴赫猜想
  3. RN + Flutter
  4. 【Android之SmartImageView图片控件】
  5. 2018富途证券前端实习面试总结
  6. 工厂模式总结——三个工厂
  7. 【科研】浅学Cross-attention?
  8. 曼哈顿算法公式_距离计算方法总结
  9. 使用canvas 绘制象棋棋盘
  10. 科目二:倒车入库考试技巧详细图解