好多办公软件需要添加水印,添加水印一般有两种,一种是在布局中设置显示在底层,不会遮挡上层内容,实现比较繁琐;另一种是覆盖在最上层,会有遮挡的问题,实现较简单
方法一:先实现简单覆盖在上面的
/**
* 水印
* 大概思路是,首先获取当前页面的底层布局,然后在布局最上层添加一个全屏的带有水印的FrameLayout布局。
* 自定义一个Drawable,把它当作水印,然后设置到FrameLayout布局的背景。
*/
public class WatermarkUtil {
/**
* 水印文本
*/
private String mText;
/**
* 字体颜色,十六进制形式,例如:0xAEAEAEAE
*/
private int mTextColor;
/**
* 字体大小,单位为sp
*/
private float mTextSize;
/**
* 旋转角度
*/
private float mRotation;
private static WatermarkUtil sInstance;
private WatermarkUtil() {
mText = "";
mTextColor = 0xAEAEAEAE;
mTextSize = 18;
mRotation = -25;
}
public static WatermarkUtil getInstance() {
if (sInstance == null) {
synchronized (WatermarkUtil.class) {
sInstance = new WatermarkUtil();
}
}
return sInstance;
}
/**
* 设置水印文本
*
* @param text 文本
* @return Watermark实例
*/
public WatermarkUtil setText(String text) {
mText = text;
return sInstance;
}
/**
* 设置字体颜色
*
* @param color 颜色,十六进制形式,例如:0xAEAEAEAE
* @return Watermark实例
*/
public WatermarkUtil setTextColor(int color) {
mTextColor = color;
return sInstance;
}
/**
* 设置字体大小
*
* @param size 大小,单位为sp
* @return Watermark实例
*/
public WatermarkUtil setTextSize(float size) {
mTextSize = size;
return sInstance;
}
/**
* 设置旋转角度
*
* @param degrees 度数
* @return Watermark实例
*/
public WatermarkUtil setRotation(float degrees) {
mRotation = degrees;
return sInstance;
}
/**
* 显示水印,铺满整个页面
*
* @param activity 活动
*/
public void show(Activity activity) {
show(activity, mText);
}
/**
* 显示水印,铺满整个页面
*
* @param activity 活动
* @param text 水印
*/
public void show(Activity activity, String text) {
WatermarkDrawable drawable = new WatermarkDrawable();
drawable.mText = text;
drawable.mTextColor = mTextColor;
drawable.mTextSize = mTextSize;
drawable.mRotation = mRotation;
ViewGroup rootView = activity.findViewById(android.R.id.content);
FrameLayout layout = new FrameLayout(activity);
layout.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
layout.setBackground(drawable);
rootView.addView(layout);
}
/**
* 显示水印,铺满整个页面
*
* @param viewGroup 活动
*/
public void show(ViewGroup viewGroup) {
show(viewGroup, mText);
}
/**
* 显示水印,铺满整个页面
*
* @param viewGroup 活动
* @param text 水印
*/
public void show(ViewGroup viewGroup, String text) {
try{
WatermarkDrawable drawable = new WatermarkDrawable();
drawable.mText = text;
drawable.mTextColor = mTextColor;
drawable.mTextSize = mTextSize;
drawable.mRotation = mRotation;
// ViewGroup rootView = activity.findViewById(android.R.id.content);
FrameLayout layout = new FrameLayout(viewGroup.getContext());
layout.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
layout.setBackground(drawable);
viewGroup.addView(layout);
}catch (Exception e){
e.printStackTrace();
}
}
private class WatermarkDrawable extends Drawable {
private Paint mPaint;
/**
* 水印文本
*/
private String mText;
/**
* 字体颜色,十六进制形式,例如:0xAEAEAEAE
*/
private int mTextColor;
/**
* 字体大小,单位为sp
*/
private float mTextSize;
/**
* 旋转角度
*/
private float mRotation;
private WatermarkDrawable() {
mPaint = new Paint();
}
@Override
public void draw(Canvas canvas) {
int width = getBounds().right;
int height = getBounds().bottom;
int diagonal = (int) Math.sqrt(width * width + height * height); // 对角线的长度
mPaint.setColor(mTextColor);
mPaint.setTextSize(DrawUtil.sp2px(mTextSize)); // DrawUtil.sp2px这个方法是将sp转换成px
mPaint.setAntiAlias(true);
float textWidth = mPaint.measureText(mText);
//设置不从原点开始绘制,以免遮挡导航栏
canvas.translate(0, 150);
canvas.drawColor(0x00000000);
canvas.rotate(mRotation);
int index = 0;
float fromX;
// 以对角线的长度来做高度,这样可以保证竖屏和横屏整个屏幕都能布满水印
for (int positionY = diagonal / 8; positionY <= diagonal; positionY += diagonal / 8) {
fromX = -width + (index++ % 2) * textWidth; // 上下两行的X轴起始点不一样,错开显示
for (float positionX = fromX; positionX < width; positionX += textWidth * 2) {
canvas.drawText(mText, positionX, positionY, mPaint);
}
}
canvas.save();
canvas.restore();
}
@Override
public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
}
使用方法:
WatermarkUtil.getInstance()
.setText("需要显示的内容")
.setTextColor(Color.parseColor("#cceeeeee"))
.setTextSize(40)
.setRotation(-15f)
.show(viewGroup);
方法二:布局中实现,水印在内容下面
下面是自定义控件,可以显示在任何布局中,是ViewGroup
**
* Des: 水印ViewGroup
* Created by ww on 2020/11/14.
*/
public class WaterMarkViewGroup extends FrameLayout {
private int mDrawWidth = 300;//画的宽度 为实际高度的mScaleSize倍(作用是在旋转后防止有些地方没有水印)
private int mDrawHeight = 300;//画的高度 同上
private String str = "信随行";//水印文字
private float hSpace = 50f;//水平间距
private float vSpace = 20f;//垂直间距
private int mScaleSize = 2;//缩放大小 固定:2
private int mDegrees = -30;//旋转角度
private int bgColor = Color.TRANSPARENT;//背景颜色
private int textColor = Color.BLACK;//字体颜色
private float textSize = 13f;//字体大小
private int mWidth;//布局实际宽度
private int mHeight;//布局实际高度
public WaterMarkViewGroup(@NonNull Context context) {
this(context, null);
}
public WaterMarkViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
/**
* 初始化
*
* @param context
* @param attrs
*/
public void init(Context context, AttributeSet attrs) {
if (null != attrs) {
//获取XML中的参数
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WaterMarkView);
str = ta.getString(R.styleable.WaterMarkView_text);
hSpace = ta.getDimension(R.styleable.WaterMarkView_hSpace, 50f);
vSpace = ta.getDimension(R.styleable.WaterMarkView_vSpace, 20f);
mDegrees = ta.getInteger(R.styleable.WaterMarkView_degrees, -30);
bgColor = ta.getColor(R.styleable.WaterMarkView_bgColor, Color.TRANSPARENT);
textColor = ta.getColor(R.styleable.WaterMarkView_textColor, Color.BLACK);
textSize = ta.getDimension(R.styleable.WaterMarkView_textSize, 13f);
ta.recycle();
}
}
/**
* 设置参数
*
* @param text 水印文字
*/
public void setParams(String text) {
setParams(text, hSpace, vSpace);
}
/**
* 设置参数
*
* @param text 水印文字
* @param hSize 水平方向水印文字个数
* @param vSize 垂直方向水印文字个数
*/
public void setParams(String text, float hSize, float vSize) {
setParams(text, hSize, vSize, mDegrees);
}
/**
* 设置参数
*
* @param text 水印文字
* @param hSize 水平方向水印文字个数
* @param vSize 垂直方向水印文字个数
* @param degrees 旋转角度
*/
public void setParams(String text, float hSize, float vSize, int degrees) {
setParams(text, hSize, vSize, degrees, bgColor, textColor, textSize);
}
/**
* 参数设置
*
* @param text
* @param hSize
* @param vSize
* @param degrees
* @param bgColor 背景颜色
* @param textColor 字体颜色
* @param textSize 字体大小
*/
public void setParams(String text, float hSize, float vSize, int degrees, int bgColor, int textColor, float textSize) {
this.str = text;
this.hSpace = hSize;
this.vSpace = vSize;
this.mDegrees = degrees;
this.bgColor = bgColor;
this.textColor = textColor;
this.textSize = textSize;
invalidate();
}
/**
* 在View的源码当中并没有对AT_MOST和EXACTLY两个模式做出区分,
* 也就是说View在wrap_content和match_parent两个模式下是完全相同的,
* 都会是match_parent,
* 显然这与我们平时用的View不同,
* 所以我们要重写onMeasure方法。
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mDrawWidth, mDrawHeight);
} else if (widthMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mDrawWidth, heightSize);
} else if (heightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSize, mDrawHeight);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//获取实际宽高
mWidth = getWidth();
mHeight = getHeight();
//设置需要画的宽高
mDrawWidth = mWidth * mScaleSize;
mDrawHeight = mHeight * mScaleSize;
}
@Override
protected void dispatchDraw(Canvas canvas) {
drawWaterMark(canvas);
super.dispatchDraw(canvas);
}
/**
* 绘制水印
*
* @param canvas
*/
private void drawWaterMark(Canvas canvas) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Rect rect = new Rect();
paint.setTextSize(textSize);
//获取文字长度和宽度
paint.getTextBounds(str, 0, str.length(), rect);
int textWidth = rect.width();
int textHeight = rect.height();
//获取每个单独的item的宽高
float itemWidth = textWidth + hSpace;
float itemHeight = textHeight + vSpace;
//获取水平、垂直方向需要绘制的个数
int hSize = (int) ((mDrawWidth / itemWidth) + 0.5);
int vSize = (int) ((mDrawHeight / itemHeight) + 0.5);
//X轴开始坐标
float xStart = hSpace / 2;
//Y轴开始坐标
float yStart = vSpace / 2 + textHeight;
//创建透明画布
canvas.drawColor(bgColor);
paint.setColor(textColor);
//paint.setAlpha((int) (0.1 * 255));
// 获取跟清晰的图像采样
paint.setDither(true);
paint.setFilterBitmap(true);
canvas.save();
//平移
canvas.translate(-(mDrawWidth / 4), -(mDrawHeight / 4));
//旋转对应角度
canvas.rotate(mDegrees, mDrawWidth / 2, mDrawHeight / 2);
//画X轴方向
for (int i = 0; i < hSize; i++) {
float xDraw = xStart + itemWidth * i;
//画Y轴方向
for (int j = 0; j < vSize; j++) {
float yDraw = yStart + itemHeight * j;
canvas.drawText(str, xDraw, yDraw, paint);
}
}
canvas.restore();
}
}
涉及到的自定义参数:
<!-- 水印参数-->
<declare-styleable name="WaterMarkView">
<!-- 水印文字-->
<attr name="text" format="string" />
<!-- 水平间距-->
<attr name="hSpace" format="dimension" />
<!-- 垂直间距-->
<attr name="vSpace" format="dimension" />
<!-- 角度-->
<attr name="degrees" format="integer" />
<!-- 背景颜色-->
<attr name="bgColor" format="color" />
<!-- 文字颜色-->
<attr name="textColor" format="color" />
<!-- 文字大小-->
<attr name="textSize" format="dimension" />
</declare-styleable>
将以上在布局底层引用,即可实现底层水印,但是需要在每个添加水印的布局中添加,比较繁琐!
|