图片添加水印相关记录

最近一直在搞水印相关的业务,整体添加水印,网上代码一大堆,但是如果接入自身业务逻辑还是必须对添加水印有一个基本了解。特别是琐碎的技能点工具类等,挺耽误时间, 所以在这里总结记录下。

  • 获取屏幕View大小,测量摆放等。
  • 根据屏幕View控件生成Bitmap并返回
  • 选择图片 - 一个好用的选择图片,裁剪的轮子
  • 一个完整的图片添加水印,美颜,磨皮等Demo

屏幕相关,View宽高度测量,摆放。

1、工具类 ScreenUtils 提供功能:获取屏幕高度, 宽度, 状态栏高度, 当前屏幕截屏

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
 
//获得屏幕相关的辅助类
public class ScreenUtils
{
	private ScreenUtils()
	{
		/* cannot be instantiated */
		throw new UnsupportedOperationException("cannot be instantiated");
	}
 
	/**
	 * 获得屏幕高度
	 * 
	 * @param context
	 * @return
	 */
	public static int getScreenWidth(Context context)
	{
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics = new DisplayMetrics();
		wm.getDefaultDisplay().getMetrics(outMetrics);
		return outMetrics.widthPixels;
	}
 
	/**
	 * 获得屏幕宽度
	 * 
	 * @param context
	 * @return
	 */
	public static int getScreenHeight(Context context)
	{
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics = new DisplayMetrics();
		wm.getDefaultDisplay().getMetrics(outMetrics);
		return outMetrics.heightPixels;
	}
 
	/**
	 * 获得状态栏的高度
	 * 
	 * @param context
	 * @return
	 */
	public static int getStatusHeight(Context context)
	{
 
		int statusHeight = -1;
		try
		{
			Class<?> clazz = Class.forName("com.android.internal.R$dimen");
			Object object = clazz.newInstance();
			int height = Integer.parseInt(clazz.getField("status_bar_height")
					.get(object).toString());
			statusHeight = context.getResources().getDimensionPixelSize(height);
		} catch (Exception e)
		{
			e.printStackTrace();
		}
		return statusHeight;
	}
 
	/**
	 * 获取当前屏幕截图,包含状态栏
	 * 
	 * @param activity
	 * @return
	 */
	public static Bitmap snapShotWithStatusBar(Activity activity)
	{
		View view = activity.getWindow().getDecorView();
		view.setDrawingCacheEnabled(true);
		view.buildDrawingCache();
		Bitmap bmp = view.getDrawingCache();
		int width = getScreenWidth(activity);
		int height = getScreenHeight(activity);
		Bitmap bp = null;
		bp = Bitmap.createBitmap(bmp, 0, 0, width, height);
		view.destroyDrawingCache();
		return bp;
 
	}
 
	/**
	 * 获取当前屏幕截图,不包含状态栏
	 * 
	 * @param activity
	 * @return
	 */
	public static Bitmap snapShotWithoutStatusBar(Activity activity)
	{
		View view = activity.getWindow().getDecorView();
		view.setDrawingCacheEnabled(true);
		view.buildDrawingCache();
		Bitmap bmp = view.getDrawingCache();
		Rect frame = new Rect();
		activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
		int statusBarHeight = frame.top;
 
		int width = getScreenWidth(activity);
		int height = getScreenHeight(activity);
		Bitmap bp = null;
		bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height
				- statusBarHeight);
		view.destroyDrawingCache();
		return bp;
 
	}
 
}

2、获取控件的高度,宽度

方法一:

        int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        mTextView.measure(w, h);
        int height = mTextView.getMeasuredHeight();
        int width = mTextView.getMeasuredWidth();

这个方法限制挺大,我使用的结果是并没有返回正确的高度,和宽度。猜测应该是我直接在XML 中写死了控件的宽高,所以并没有正确返回。并且网上代码都有说明需要在oncreat中书写。那么一些动态布局就无法使用。

方法二:

ViewTreeObserver vto = _root.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                _root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                int _view2_w = _view2.getWidth();
                int _view2_h = _view2.getHeight();
            }
        });

测试有效,ViewTreeObserver 是监听屏幕视图发生改变时候的监听。在这里获取 你所要view 的高度宽度是可以去到具体值,不过个监听既然是监听屏幕视图发生变化,所以肯定会发挥多次结果。猜测我是使用_root xml最外成父布局获取监听才导致获取多个结果。(前几次都是 0),可以使用直接测量的控件去获取 屏幕的宽度。

方法三:

    @Override
    public void onWindowFocusChanged(boolean hasFocus)
    {
        // TODO Auto-generated method stub
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus)
        {
            System.out.println("onWindowFocusChanged width=" + mTextView.getWidth() + " height=" + mTextView.getHeight());
        }
    }

方法未测试,先记录下来,以免特殊场景下使用。

3、计算控件宽高,摆放位置,view随手指一动滑动 仿Keep 水印功能。

我使用获取屏幕高度,动态设置控件高度,把水印添加至图片相对位置。然后重写onTouch ,控制手指控制水印。

  _view2.setOnTouchListener(this);


   @Override
    public boolean onTouch(View v, MotionEvent event) {
        final int X = (int) event.getRawX();
        final int Y = (int) event.getRawY();
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getRawX();
                lastY = (int) event.getRawY();
                RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) _view2.getLayoutParams();
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                break;
            case MotionEvent.ACTION_POINTER_UP:
                break;
            case MotionEvent.ACTION_MOVE:
                int dx = (int) event.getRawX() - lastX;
                int dy = (int) event.getRawY() - lastY;
                int left = _view2.getLeft() + dx;
                int top = _view2.getTop() + dy;
                RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) _view2.getLayoutParams();
                layoutParams.leftMargin = left;
                layoutParams.topMargin = top;
                // _view2 就是屏幕上添加一个控件,当成水印。这样水印就可以根据手指一动而滑动。
                _view2.setLayoutParams(layoutParams);
                lastX = (int) event.getRawX();
                lastY = (int) event.getRawY();
                Log.e("lipeng", "lastX:" + lastX + "lastY:" + lastY);
                break;
        }
        _root.invalidate();
        return true;

动态摆放控件位置,其实看到上面代码,已经写的很清楚了,直接利用 layoutParams API 达到控件动态摆放的功能

 RelativeLayout.LayoutParams lParams;
 
lParams = (RelativeLayout.LayoutParams) _view2.getLayoutParams();

        lParams.leftMargin = width;
        lParams.topMargin = height;
        _view2.setLayoutParams(lParams);

根据屏幕View控件生成Bitmap并返回

刚刚知道要做一个添加水印的功能的时候,我就打算使用这个方法。

其实我们看看一般添加水印的做法,

// 创建一个新的和SRC长度宽度一样的位图
Bitmap newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//将该图片作为画布
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();

但是使用这样的方法添加水印,无法达到keep那种手指控制
所以就想使用RelativeLayout 布局包裹,直接每次添加水印就 new imageview 最后使用RelativeLayout生成一个bitmap对象。
————————————————————————————————————————————————————————————————
整个屏幕截图

public static Bitmap getNormalViewScreenshot(View view) {
    view.setDrawingCacheEnabled(true);
    view.buildDrawingCache();
    return view.getDrawingCache();
  }

scrollview的整体截屏

public static Bitmap getWholeScrollViewToBitmap(View view) {
    view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    view.buildDrawingCache();
    Bitmap bitmap = view.getDrawingCache();
    return bitmap;
  }

webview的整体截图

public static Bitmap getWholeWebViewToBitmap(WebView webView) {
    Picture snapShot = webView.capturePicture();
    Bitmap bmp = Bitmap.createBitmap(snapShot.getWidth(), snapShot.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bmp);
    snapShot.draw(canvas);
    return bmp;
  }

listview的整体截图

public static Bitmap getWholeListViewItemsToBitmap(ListView listview) {
 
    ListAdapter adapter = listview.getAdapter();
    int itemscount = adapter.getCount();
    int allitemsheight = 0;
    List<Bitmap> bmps = new ArrayList<Bitmap>();
 
    for (int i = 0; i < itemscount; i++) {
 
      View childView = adapter.getView(i, null, listview);
      childView.measure(MeasureSpec.makeMeasureSpec(listview.getWidth(), MeasureSpec.EXACTLY),
          MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
 
      childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
      childView.setDrawingCacheEnabled(true);
      childView.buildDrawingCache();
      bmps.add(childView.getDrawingCache());
      allitemsheight += childView.getMeasuredHeight();
    }
 
    Bitmap bigbitmap = Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);
    Canvas bigcanvas = new Canvas(bigbitmap);
 
    Paint paint = new Paint();
    int iHeight = 0;
 
    for (int i = 0; i < bmps.size(); i++) {
      Bitmap bmp = bmps.get(i);
      bigcanvas.drawBitmap(bmp, 0, iHeight, paint);
      iHeight += bmp.getHeight();
 
      bmp.recycle();
      bmp = null;
    }
    return bigbitmap;
  }

需要多次截图的话,需要用到 view.destroyDrawingCache();

Bitmap normalViewScreenshot = ScreenShotUtils.getNormalViewScreenshot(mFrameContent);
        if (normalViewScreenshot != null) {
          Bitmap b = Bitmap.createBitmap(normalViewScreenshot);
          mImageResult.setImageBitmap(b);
          mFrameContent.destroyDrawingCache();
        }

引用 Android屏幕及view的截图实例详解

当然这样的方式也可以使用到屏幕截图的相关业务上,如果产品需要加一个截图成功的效果,可以参考
Android屏幕截图,View截图(干货)
android 获取界面部分view,view截图,生成bitmap图片
——————————————————————————————————————————————————————————————

选择图片 - 一个好用的选择图片,裁剪的轮子

Github 项目 支持图片视频

详细使用流程可以去项目地址看下,目前项目中使用的就是这个轮子,目前没有发现致命bug

——————————————————————————————————————————————————————————————

一个完整的图片添加水印,美颜,磨皮等Demo

这个项目发现的有点晚了,自己琢磨了三四天,上周五完成业务demo,交差的时候,产品告诉我如果可以的话,添加美颜,磨皮等功能。然后我github上开始找相关轮子。然后发现了这个demo,原版demo地址已经找不到了,自己传是csdn一份

在这里插入图片描述

我拍了一个视频,但是做gif图片太。
CSDN 资源地址,github上的链接没有找到所以就打包上传到csdn上了,没有积分的可以github上翻一翻。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章