Android图片添加水印

在项目开发进行中,要求给商品图片添加水印。有什么好办法呢?搜索网上的大致有两种方案

  • ImageView视图加载出现之后在进行加载
  • 在布局中写布局文件实现

以上两种方案都不能很好的满足项目的需要,所以就考虑到重写ImageView,在onDraw方法中做事情

  • WatermarkImageView继承ImageView
public class WatermarkImageView extends AppCompatImageView {

   private Bitmap mWatemarkBitmap;
   private RectF srcRect;
   private PorterDuffXfermode mXfermode;

   public WatermarkImageView(Context context) {
        this(context, null);
    }

    public WatermarkImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WatermarkImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        init();
    }

    /**
     * 默认资源初始化
     */
    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
        mWatemarkBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_prologo);
    }
}
  • 重写onDraw()方法
   @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 创建水印图层
        int i = canvas.saveLayer(srcRect, mPaint, Canvas.ALL_SAVE_FLAG);
        // 设置模式,为直接覆盖到原图
        mPaint.setXfermode(mXfermode);
        // 绘制水印到当前图层
        canvas.drawBitmap(mWatemarkBitmap, null, srcRect, mPaint);
        // 清除模式
        mPaint.setXfermode(null);
        // 将如上画的层覆盖到原有层级上
        canvas.restoreToCount(i);
    }
  • 设置属性
    1.arrays.xml文件内容

     <declare-styleable name="WatermarkImageView">
        <attr name="watermark_width" format="dimension" />
        <attr name="watermark_height" format="dimension" />
        <attr name="watermark_visible" format="boolean" />
        <attr name="watermark_padding_top" format="dimension" />
        <attr name="watermark_padding_right" format="dimension" />
        <attr name="watermark_padding_left" format="dimension" />
        <attr name="watermark_padding_bottom" format="dimension" />
        <attr name="watermark_src" format="reference" />
        <attr name="watermark_location" format="enum">
            <enum name="locate_left_to_top" value="0" />
            <enum name="locate_right_to_top" value="1" />
            <enum name="locate_left_to_bottom" value="2" />
            <enum name="locate_right_to_bottom" value="3" />
            <enum name="locate_center" value="4" />
        </attr>
    </declare-styleable>
    

    2.代码获取属性

      /**
     * 从xml文件中获取属性
     *
     * @param attrs attrs
     */
    private void initAttribute(AttributeSet attrs) {
        TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.WatermarkImageView);
        int N = typedArray.getIndexCount();
        for (int i = 0; i < N; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.WatermarkImageView_watermark_src:
                    // Bitmap bitmap = ((BitmapDrawable) typedArray.getDrawable(attr)).getBitmap();
                    int resourceId = typedArray.getResourceId(attr, R.drawable.ic_prologo);
                    mWatemarkBitmap = BitmapFactory.decodeResource(getResources(), resourceId);
                    break;
                case R.styleable.WatermarkImageView_watermark_visible:
                    mIsWatermark = typedArray.getBoolean(R.styleable.WatermarkImageView_watermark_visible, false);
                    break;
                case R.styleable.WatermarkImageView_watermark_width:
                    mWatermarkWidth = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_width, -1);
                    Log.d("mWatermarkWidth" + mWatermarkWidth);
                    break;
                case R.styleable.WatermarkImageView_watermark_height:
                    mWatermarkHeight = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_height, -1);
                    Log.d("mWatermarkHeight" + mWatermarkHeight);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_top:
                    mWatermarkPaddingTop = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_top, -1);
                    Log.d("mWatermarkPaddingTop" + mWatermarkPaddingTop);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_right:
                    mWatermarkPaddingRight = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_right, -1);
                    Log.d("mWatermarkPaddingRight" + mWatermarkPaddingRight);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_left:
                    mWatermarkPaddingLeft = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_left, -1);
                    Log.d("mWatermarkPaddingLeft" + mWatermarkPaddingLeft);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_bottom:
                    mWatermarkPaddingBottom = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_bottom, -1);
                    Log.d("mWatermarkPaddingBottom" + mWatermarkPaddingBottom);
                    break;
                case R.styleable.WatermarkImageView_watermark_location:
                    mCurrentLocate = typedArray.getInteger(R.styleable.WatermarkImageView_watermark_location, mCurrentLocate);
                    Log.d("mCurrentLocate" + mCurrentLocate);
                    break;
                default:
                    break;
            }
        }
        typedArray.recycle();
    }
    
  • 完整代码

package com.pbs.xpjx.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.annotation.IntDef;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;

import com.bsh.sdk.utils.DisplayUtil;
import com.pbs.xpjx.R;
import com.pbs.xpjx.utils.Log;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * 图片添加水印(目前该view只支持添加图片,后续如需要添加文字可以继续添加)
 * Created by timmy.tan on 2017/11/23.
 */

public class WatermarkImageView extends AppCompatImageView {

    public static final int LOCATE_LEFT_TO_TOP = 0; // 左上
    public static final int LOCATE_RIGHT_TO_TOP = 1; // 右上
    public static final int LOCATE_LEFT_TO_BOTTOM = 2; // 左下
    public static final int LOCATE_RIGHT_TO_BOTTOM = 3; // 右下
    public static final int LOCATE_CENTER = 4; // 居中


    @IntDef({LOCATE_LEFT_TO_TOP, LOCATE_RIGHT_TO_TOP, LOCATE_LEFT_TO_BOTTOM, LOCATE_RIGHT_TO_BOTTOM, LOCATE_CENTER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Locate {
    }

    @Locate
    int mCurrentLocate = LOCATE_RIGHT_TO_TOP;

    private Context mContext;
    private Paint mPaint;
    private int mScale = 5; // 设置水印图与原图的比例
    private Bitmap mWatemarkBitmap;
    private RectF srcRect;
    private PorterDuffXfermode mXfermode;
    private boolean mIsWatermark = false;
    private int mWatermarkWidth = -1; // 水印宽度
    private int mWatermarkHeight = -1; // 水印高度
    private int mWatermarkPaddingTop;
    private int mWatermarkPaddingRight;
    private int mWatermarkPaddingLeft;
    private int mWatermarkPaddingBottom;


    public WatermarkImageView(Context context) {
        this(context, null);
    }

    public WatermarkImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WatermarkImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        init();
        initAttribute(attrs);
    }

    /**
     * 默认资源初始化
     */
    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
        mWatemarkBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_prologo);
    }

    /**
     * 从xml文件中获取属性
     *
     * @param attrs attrs
     */
    private void initAttribute(AttributeSet attrs) {
        TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.WatermarkImageView);
        int N = typedArray.getIndexCount();
        for (int i = 0; i < N; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.WatermarkImageView_watermark_src:
                    // Bitmap bitmap = ((BitmapDrawable) typedArray.getDrawable(attr)).getBitmap();
                    int resourceId = typedArray.getResourceId(attr, R.drawable.ic_prologo);
                    mWatemarkBitmap = BitmapFactory.decodeResource(getResources(), resourceId);
                    break;
                case R.styleable.WatermarkImageView_watermark_visible:
                    mIsWatermark = typedArray.getBoolean(R.styleable.WatermarkImageView_watermark_visible, false);
                    break;
                case R.styleable.WatermarkImageView_watermark_width:
                    mWatermarkWidth = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_width, -1);
                    Log.d("mWatermarkWidth" + mWatermarkWidth);
                    break;
                case R.styleable.WatermarkImageView_watermark_height:
                    mWatermarkHeight = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_height, -1);
                    Log.d("mWatermarkHeight" + mWatermarkHeight);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_top:
                    mWatermarkPaddingTop = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_top, -1);
                    Log.d("mWatermarkPaddingTop" + mWatermarkPaddingTop);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_right:
                    mWatermarkPaddingRight = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_right, -1);
                    Log.d("mWatermarkPaddingRight" + mWatermarkPaddingRight);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_left:
                    mWatermarkPaddingLeft = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_left, -1);
                    Log.d("mWatermarkPaddingLeft" + mWatermarkPaddingLeft);
                    break;
                case R.styleable.WatermarkImageView_watermark_padding_bottom:
                    mWatermarkPaddingBottom = typedArray.getLayoutDimension(R.styleable.WatermarkImageView_watermark_padding_bottom, -1);
                    Log.d("mWatermarkPaddingBottom" + mWatermarkPaddingBottom);
                    break;
                case R.styleable.WatermarkImageView_watermark_location:
                    mCurrentLocate = typedArray.getInteger(R.styleable.WatermarkImageView_watermark_location, mCurrentLocate);
                    Log.d("mCurrentLocate" + mCurrentLocate);
                    break;
                default:
                    break;
            }
        }
        typedArray.recycle();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 不设置宽高默认按比例计算
        if (mWatermarkWidth < 0) {
            mWatermarkWidth = w / mScale;
        }
        if (mWatermarkHeight < 0) {
            mWatermarkHeight = h / mScale;
        }
        int left = 0;
        int top = 0;
        int right = 0;
        int bottom = 0;

        switch (mCurrentLocate) {
            case LOCATE_LEFT_TO_TOP:
                // 左上
                left = mWatermarkPaddingLeft;
                top = mWatermarkPaddingTop;
                right = mWatermarkWidth + mWatermarkPaddingLeft;
                bottom = mWatermarkHeight + mWatermarkPaddingTop;
                break;

            case LOCATE_RIGHT_TO_TOP:
                // 右上
                left = w - mWatermarkWidth - mWatermarkPaddingRight;
                top = mWatermarkPaddingTop;
                right = left + mWatermarkWidth;
                bottom = top + mWatermarkHeight;
                break;

            case LOCATE_LEFT_TO_BOTTOM:
                // 左下
                left = mWatermarkPaddingLeft;
                top = h - mWatermarkPaddingBottom - mWatermarkHeight;
                right = left + mWatermarkWidth;
                bottom = h - mWatermarkPaddingBottom;
                break;

            case LOCATE_RIGHT_TO_BOTTOM:
                // 右下
                left = w - mWatermarkPaddingRight - mWatermarkWidth;
                top = h - mWatermarkPaddingBottom - mWatermarkHeight;
                right = w - mWatermarkPaddingRight;
                bottom = h - mWatermarkPaddingBottom;
                break;

            case LOCATE_CENTER:
                // 居中
                int parentHalfW = w >> 1;
                int watermarkHalfW = mWatermarkWidth >> 1;
                int parentHalfH = h >> 1;
                int watermarkHalfH = mWatermarkHeight >> 1;

                left = parentHalfW - watermarkHalfW;
                top = parentHalfH - watermarkHalfH;
                right = parentHalfW + watermarkHalfW;
                bottom = parentHalfH + watermarkHalfH;
                break;
            default:
                break;
        }
        srcRect = new RectF(left, top, right, bottom);


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (false) {
            drawWatermark(canvas);
        }
    }

    /**
     * 画水印
     *
     * @param canvas canvas
     */
    private void drawWatermark(Canvas canvas) {
        // 创建水印图层
        int i = canvas.saveLayer(srcRect, mPaint, Canvas.ALL_SAVE_FLAG);
        // 设置模式,为直接覆盖到原图
        mPaint.setXfermode(mXfermode);
        // 绘制水印到当前图层
        canvas.drawBitmap(mWatemarkBitmap, null, srcRect, mPaint);
        // 清除模式
        mPaint.setXfermode(null);
        // 将如上画的层覆盖到原有层级上
        canvas.restoreToCount(i);
    }


    /**
     * 是否显示水印
     *
     * @param b true 显示 false 不显示
     */
    public void showWatermark(boolean b) {
        this.mIsWatermark = b;
        // 视图重绘,执行onDraw()方法
        invalidate();
    }

    public Bitmap getWatemarkBitmap() {
        return mWatemarkBitmap;
    }

    public void setWatemarkBitmap(Bitmap mWatemarkBitmap) {
        this.mWatemarkBitmap = mWatemarkBitmap;
        invalidate();
    }

    public int getWatermarkWidth() {
        return mWatermarkWidth;
    }

    public void setWatermarkWidth(int mWatermarkWidth) {
        this.mWatermarkWidth = mWatermarkWidth;
        invalidate();
    }

    public int getWatermarkHeight() {
        return mWatermarkHeight;
    }

    public void setWatermarkHeight(int mWatermarkHeight) {
        this.mWatermarkHeight = mWatermarkHeight;
        invalidate();
    }

    public int getCurrentLocate() {
        return mCurrentLocate;
    }

    public void setCurrentLocate(@Locate int mCurrentLocate) {
        this.mCurrentLocate = mCurrentLocate;
        invalidate();
    }
}

  • 使用指南
  <xxx.WatermarkImageView
            android:id="@+id/iv_photo"
            android:layout_width="90dp"
            android:layout_height="90dp"
            android:background="@drawable/divider_color_round_rect"
            android:scaleType="centerCrop"
            android:src="@mipmap/normal_product"
            app:watermark_padding_right="10dp"
            app:watermark_padding_top="10dp" />
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352