平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Android给图片添加水印的实现方法代码”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
在这个场景下,在当前的互联网时代,图片已经成为信息传递和社交传播的重要媒介。随着社交媒体、短视频平台、电商平台、新闻门户等应用的发展,图片的采用越来越广泛。然而,图片在传播过程中极易被他人下载、二次采用甚至篡改。为了防止图片被非法盗用、保护版权、突出品牌标识,在图片中添加水印 成为一种常用且有效的解决方案。
水印的作用主要包括:
在这个场景下,所以,图片加水印在 Android 应用开发中具有极为广泛的应用场景,比如:
本项目的主要需求是 在 Android 应用中实现图片加水印功能,具体需求包括:
兼容水印类型
水印位置灵活
兼容透明度
操作简便
性能要求
保存与分享
要在 Android 中实现图片加水印,常用技术方案有以下几种:
采用 Canvas 绘制
Canvas 类,能够对 Bitmap 进行绘制。采用 OpenGL 处理
采用第三方库
Glide、Picasso 主要做图片加载,本身不提供水印功能。在本项目中,我们选用 Canvas 绘制方案落到代码里,,因为它轻松易懂,完全依赖 Android 原生 API,不需额外引入大型库。
我们采用以下思路来实现:
选择图片
加载图片
新建新 Bitmap
绘制原图
绘制水印
Paint.setTextSize、setColor、setAlpha 设置样式后绘制文字。canvas.drawBitmap() 绘制在指定位置。保存新图片
展示结果
// ===================== 文件:MainActivity.java =====================
package com.example.imagewatermark;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileOutputStream;
/**
* 主页面:选择图片并添加水印
*/
public class MainActivity extends AppCompatActivity {
private Button btnSelectImage;
private Button btnAddTextWatermark;
private Button btnAddImageWatermark;
private ImageView imageView;
private Bitmap selectedBitmap;
// 文件选择器
private ActivityResultLauncher<String> imagePickerLauncher =
registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
if (uri != null) {
try {
selectedBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
imageView.setImageBitmap(selectedBitmap);
Toast.makeText(this, "选择图片成功", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnSelectImage = findViewById(R.id.btn_select_image);
btnAddTextWatermark = findViewById(R.id.btn_add_text_watermark);
btnAddImageWatermark = findViewById(R.id.btn_add_image_watermark);
imageView = findViewById(R.id.image_view);
// 选择图片
btnSelectImage.setOnClickListener(v -> {
imagePickerLauncher.launch("image/*");
});
// 添加文字水印
btnAddTextWatermark.setOnClickListener(v -> {
if (selectedBitmap == null) {
Toast.makeText(this, "请先选择图片", Toast.LENGTH_SHORT).show();
return;
}
Bitmap watermarked = WatermarkUtils.addTextWatermark(selectedBitmap, "MyWatermark", 50, 50);
imageView.setImageBitmap(watermarked);
saveBitmap(watermarked, "text_watermark.png");
});
// 添加图片水印
btnAddImageWatermark.setOnClickListener(v -> {
if (selectedBitmap == null) {
Toast.makeText(this, "请先选择图片", Toast.LENGTH_SHORT).show();
return;
}
Bitmap watermarkLogo = WatermarkUtils.decodeResource(this, R.drawable.logo);
Bitmap watermarked = WatermarkUtils.addImageWatermark(selectedBitmap, watermarkLogo, 50, 50);
imageView.setImageBitmap(watermarked);
saveBitmap(watermarked, "image_watermark.png");
});
}
/**
* 保存 Bitmap 到本地文件
*/
private void saveBitmap(Bitmap bitmap, String fileName) {
try {
File output = new File(getExternalFilesDir(null), fileName);
FileOutputStream out = new FileOutputStream(output);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
Toast.makeText(this, "保存成功:" + output.getAbsolutePath(), Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// ===================== 文件:WatermarkUtils.java =====================
package com.example.imagewatermark;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
public class WatermarkUtils {
/**
* 添加文字水印
* @param src 原始图片
* @param text 水印文字
* @param x X坐标
* @param y Y坐标
* @return 新的带水印图片
*/
public static Bitmap addTextWatermark(Bitmap src, String text, int x, int y) {
Bitmap result = src.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setColor(Color.RED); // 水印颜色
paint.setTextSize(60); // 字体大小
paint.setAlpha(180); // 透明度
paint.setAntiAlias(true); // 抗锯齿
canvas.drawText(text, x, y, paint);
return result;
}
/**
* 添加图片水印
* @param src 原始图片
* @param watermark 水印图片
* @param x X坐标
* @param y Y坐标
* @return 新的带水印图片
*/
public static Bitmap addImageWatermark(Bitmap src, Bitmap watermark, int x, int y) {
Bitmap result = src.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setAlpha(180); // 透明度
canvas.drawBitmap(watermark, x, y, paint);
return result;
}
/**
* 将资源文件转换为 Bitmap
*/
public static Bitmap decodeResource(Context context, int resId) {
Drawable drawable = context.getResources().getDrawable(resId);
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
return null;
}
}
// ===================== 文件:res/layout/activity_main.xml =====================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp">
<Button
android:id="@+id/btn_select_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择图片" />
<Button
android:id="@+id/btn_add_text_watermark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加文字水印"
android:layout_marginTop="20dp"/>
<Button
android:id="@+id/btn_add_image_watermark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加图片水印"
android:layout_marginTop="20dp"/>
<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_marginTop="20dp"
android:scaleType="fitCenter"/>
</LinearLayout>
MainActivity
WatermarkUtils
addTextWatermark():在原图上绘制文字水印,兼容位置、字体大小、透明度。addImageWatermark():在原图上绘制一张 PNG 图片作为水印。decodeResource():将资源文件的图片转换为 Bitmap,用作水印素材。activity_main.xml
借助本项目,我们实现了一个完整的 Android 图片加水印功能,主要特点:
Q:如何设置水印在右下角?
A:只需根据原图宽高计算水印绘制的起点坐标即可,比如 x = src.getWidth() - watermark.getWidth() - 20。
Q:如何设置文字旋转角度?
A:在 canvas.drawText() 前调用 canvas.rotate(angle, x, y) 即可。
Q:如何提高绘制效率?
A:避免每次都解码大图,尽量压缩后再绘制。
Q:能否实现平铺式水印?
A:能够借助循环 drawText 或 drawBitmap 实现平铺效果。
Q:能否兼容用户自定义字体?
A:能够采用 Typeface.createFromAsset() 设置自定义字体。
兼容平铺水印
将文字或 Logo 平铺整个图片,增加防盗效果。
兼容动态参数设置
允许用户在界面上自定义水印文字、颜色、透明度、位置。
兼容批量处理
一次性对多个图片批量加水印,适用来电商平台。
兼容 GPU 加速
采用 OpenGL 处理大图,提高性能。
与相机结合
在拍照完成后自动加水印,而不是事后处理。
结合项目来看,以上就是Android实现图片添加水印的示例的详细内容,更多关于Android图片添加水印的资料请关注脚本之家其它相关文章!