图片剪裁控件——ClipImageView

这段时间在做自己的项目时,须要使用到图片剪裁功能,当时大概的思考了一些需求。想到了比較简单的实现方法。因此就抽了点时间做了这个图片剪裁控件——ClipImageView

这里先贴上ClipImageView的代码:

package com.example.clipimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;

/**
 * 图片剪裁控件; 注意事项: 1.在为ClipImageView设置图片时(调用setImageResource(),
 * setImageDrawable(), setImageBitmap()),要注意图片的大小,即注意Bitmap可能导致 程序出现oom的问题。
 * 2.如对剪裁图片质量无过高要求,建议调用setImageResourceSecure(), setImageDrawableSecure(),
 * setImageBitmapSecure()设置图片。
 *
 * @author freeman.wu
 *
 */
public class ClipImageView extends ImageView {
	private float currX;
	private float currY;
	private float dX;
	private float dY;
	private float oldX;
	private float oldY;
	private int maxX;
	private int maxY;

	private final float density = getResources().getDisplayMetrics().density; // 密度
	private float mClipFrameBorderWidth = 1 * density; // 剪裁框的边框宽度

	private int mClipFrameWidth = 350; // 默认的剪裁框的宽度
	private int mClipFrameHeight = 350; // 默认的剪裁框的高度

	private int imWidth; // ClipImageView的宽度
	private int imHeight; // ClipImageView的高度

	private boolean showClipFrame = true; // 是否显示剪裁框

	private String mClipFrameColor = "#FFFFFFFF"; // 剪裁框的边框颜色
	private String mShadowColor = "#99000000"; // 阴影颜色

	private Paint mShadowPaint;
	private Paint mClipFramePaint;
	/**
	 * 剪裁框外的阴影
	 */
	private Rect mRectLeftShadow;
	private Rect mRectRightShadow;
	private Rect mRectTopShadow;
	private Rect mRectBottomShadow;
	/**
	 * 剪裁框
	 */
	private Rect mClipFrame;

	/**
	 * 设置在ImageView中的Bitmap
	 */
	private Bitmap source;

	public ClipImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		setAdjustViewBounds(true);

		initPaint();
		initRect();

		post(new Runnable() {
			@Override
			public void run() {
				imWidth = getWidth();
				imHeight = getHeight();

				resolveClipFrameSize(); // 必要步骤,校正剪裁框大小。且必须在计算maxX和maxY之前
				maxX = imWidth - mClipFrameWidth;
				maxY = imHeight - mClipFrameHeight;
				currX = (float) maxX / 2;
				currY = (float) maxY / 2;

				// 设置剪裁框显示在图片正中间
				setShadowRegion(currX, currY);
				setClipFramePosition(currX, currY);
			}
		});
	}

	private void initPaint() {
		mShadowPaint = new Paint();
		mShadowPaint.setColor(Color.parseColor(mShadowColor));

		mClipFramePaint = new Paint();
		mClipFramePaint.setStyle(Paint.Style.STROKE); // 设置为空心
		mClipFramePaint.setStrokeWidth(mClipFrameBorderWidth); // 设置边框宽度
		setClipFrameColor(mClipFrameColor); // 设置颜色
	}

	private void initRect() {
		/**
		 * 阴影区域
		 */
		mRectLeftShadow = new Rect();
		mRectTopShadow = new Rect();
		mRectRightShadow = new Rect();
		mRectBottomShadow = new Rect();
		// 剪裁框
		mClipFrame = new Rect();
	}

	/**
	 * 设置剪裁框的位置
	 *
	 * @param x
	 * @param y
	 */
	private void setClipFramePosition(float x, float y) {
		int dx = (int) (mClipFrameBorderWidth / 2);
		mClipFrame.set((int) x + dx, (int) y + dx, (int) x + mClipFrameWidth
				- dx, (int) y + mClipFrameHeight - dx);
	}

	/**
	 * 设置剪裁框外的阴影
	 *
	 * @param x
	 *            剪裁框当前的左上角X坐标
	 * @param y
	 *            剪裁框当前的左上角Y坐标
	 */
	private void setShadowRegion(float x, float y) {
		mRectLeftShadow.set(0, 0, (int) x, imHeight);
		mRectTopShadow.set((int) x, 0, (int) x + mClipFrameWidth, (int) y);
		mRectRightShadow.set((int) x + mClipFrameWidth, 0, imWidth, imHeight);
		mRectBottomShadow.set((int) x, (int) y + mClipFrameHeight, (int) x
				+ mClipFrameWidth, imHeight);
	}

	/**
	 * 方法已对resId指向的图片进行压缩处理, 用此方法设置图片,剪裁后的相片质量相对 较差,但可简单避免Bitmap的OOM;如需
	 * 对原图进行裁剪。请直接调用setImageResource()
	 *
	 * @param resId
	 */
	public void setImageResourceSecure(int resId) {
		Bitmap bm = BitmapFactory.decodeResource(getResources(), resId);
		setImageBitmap(processBitmap(bm));
	}

	/**
	 * 方法已对drawable指向的图片进行压缩处理。 用此方法设置图片。剪裁后的相片质量相对 较差。但可简单避免Bitmap的OOM;如需
	 * 对原图进行裁剪。请直接调用setImageDrawable()
	 *
	 * @param drawable
	 */
	public void setImageDrawableSecure(Drawable drawable) {
		if (drawable == null)
			throw new IllegalArgumentException("drawable 不能为null");
		BitmapDrawable bd = (BitmapDrawable) drawable;
		setImageBitmap(processBitmap(bd.getBitmap()));
	}

	/**
	 * 方法已对bm指向的图片进行压缩处理, 用此方法设置图片。剪裁后的相片质量相对 较差。但可简单避免Bitmap的OOM;如需
	 * 对原图进行裁剪,请直接调用setImageBitmap()
	 *
	 * @param bm
	 */
	public void setImageBitmapSecure(Bitmap bm) {
		setImageBitmap(processBitmap(bm));
	}

	/**
	 * 对Bitmap进行简单的处理。适当地压缩图片大小
	 *
	 * @param bm
	 * @return
	 */
	private Bitmap processBitmap(Bitmap bm) {
		if (bm == null)
			throw new IllegalArgumentException("bitmap 不能为null");

		int screenWidth = getResources().getDisplayMetrics().widthPixels;
		int screenHeight = getResources().getDisplayMetrics().heightPixels;
		int bmWidth = bm.getWidth();
		int bmHeight = bm.getHeight();
		if (bmWidth < screenWidth || bmHeight < screenHeight)
			return bm;

		float scale = (float) screenWidth / bmWidth;
		Bitmap bitmap = Bitmap.createScaledBitmap(bm, screenWidth,
				(int) (bmHeight * scale), true);
		bm.recycle();
		return bitmap;
	}

	/**
	 * 获取设置在ClipImageView中的Bitmap
	 *
	 * @return
	 */
	public Bitmap getSourceBitmap() {
		if (source != null)
			return source;

		Drawable d = getDrawable();
		if (d == null) {
			return null;
		}

		BitmapDrawable bd = (BitmapDrawable) d;
		source = bd.getBitmap();
		return source;
	}

	/**
	 * 获取ImageView对原图的缩放比例
	 *
	 * @return
	 */
	public float getScale() {
		if (getSourceBitmap() == null)
			return 0f;

		int bmWidth = source.getWidth();
		int bmHeight = source.getHeight();
		float scale = Math.min((float) bmWidth / imWidth, (float) bmHeight
				/ imHeight);
		return scale;
	}

	/**
	 * 获取剪裁好的bitmap
	 *
	 * @return
	 */
	public Bitmap getClippedBitmap() {
		float scale = getScale();
		if (scale > 0 && source != null)
			return ClipImageUtils.clipImage(source, (int) currX, (int) currY, // 剪裁图片
					(int) mClipFrameWidth, (int) mClipFrameHeight, scale);
		return null;
	}

	/**
	 * 设置剪裁框边框的颜色,支持#RRGGBB #AARRGGBB 'red', 'blue', 'green', 'black', 'white',
	 * 'gray', 'cyan', 'magenta', 'yellow', 'lightgray', 'darkgray', 'grey',
	 * 'lightgrey', 'darkgrey', 'aqua', 'fuschia', 'lime', 'maroon', 'navy',
	 * 'olive', 'purple', 'silver', 'teal'
	 *
	 * @param color
	 */
	public void setClipFrameColor(String color) {
		mClipFramePaint.setColor(Color.parseColor(color));
	}

	/**
	 * 设置剪裁框的宽度和高度
	 *
	 * @param width
	 *            宽度
	 * @param height
	 *            高度
	 */
	public void setClipFrameSize(int width, int height) {
		mClipFrameWidth = width;
		mClipFrameHeight = height;

		maxX = imWidth - mClipFrameWidth;
		maxY = imHeight - mClipFrameHeight;
	}

	/**
	 * 校正裁剪框的宽高。使其不能超过View的宽高
	 */
	private void resolveClipFrameSize() {
		mClipFrameWidth = mClipFrameWidth >= imWidth ? imWidth
				: mClipFrameWidth;
		mClipFrameHeight = mClipFrameHeight >= imHeight ? imHeight
				: mClipFrameHeight;
	}

	/**
	 * 设置剪裁框的边框宽度
	 *
	 * @param w
	 */
	public void setClipFrameBorderWidth(float w) {
		w = w < 0 ? 0 : w;
		mClipFrameBorderWidth = w;
		mClipFramePaint.setStrokeWidth(mClipFrameBorderWidth);
	}

	/**
	 * 剪裁内容的左上角X坐标
	 *
	 * @return
	 */
	public float getContentX() {
		return currX;
	}

	/**
	 * 剪裁内容的左上角Y坐标
	 *
	 * @return
	 */
	public float getContentY() {
		return currY;
	}

	/**
	 * 获取剪裁内容的宽度
	 *
	 * @return
	 */
	public int getContentWidth() {
		return mClipFrameWidth;
	}

	/**
	 * 获取剪裁内容的高度
	 *
	 * @return
	 */
	public int getContentHeight() {
		return mClipFrameHeight;
	}

	public int getImWidth() {
		return imWidth;
	}

	public int getImHeight() {
		return imHeight;
	}

	/**
	 * 设置是否显示剪裁框
	 *
	 * @param f
	 */
	public void setShowClipFrame(boolean f) {
		showClipFrame = f;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		if (showClipFrame) {
			drawShadowRegion(canvas);
			drawClipFrame(canvas);
		}
	}

	/**
	 * 绘制剪裁框外的阴影
	 *
	 * @param canvas
	 */
	private void drawShadowRegion(Canvas canvas) {
		canvas.drawRect(mRectLeftShadow, mShadowPaint);
		canvas.drawRect(mRectTopShadow, mShadowPaint);
		canvas.drawRect(mRectRightShadow, mShadowPaint);
		canvas.drawRect(mRectBottomShadow, mShadowPaint);
	}

	/**
	 * 绘制剪裁框
	 *
	 * @param canvas
	 */
	private void drawClipFrame(Canvas canvas) {
		canvas.drawRect(mClipFrame, mClipFramePaint);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			oldX = event.getX();
			oldY = event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			if (mClipFrame.contains((int) oldX, (int) oldY)) {
				dX = event.getX() - oldX;
				dY = event.getY() - oldY;
				oldX = event.getX();
				oldY = event.getY();
				currX += dX;
				currY += dY;
				// 确保剪裁框不会超出ImageView的范围
				currX = currX > maxX ? maxX : currX;
				currX = currX < 0 ? 0 : currX;
				currY = currY > maxY ? maxY : currY;
				currY = currY < 0 ?

0 : currY;

				setShadowRegion(currX, currY); // 设置阴影区域
				setClipFramePosition(currX, currY); // 设置剪裁框位置
				invalidate();
			}
			break;
		}
		return true;
	}
}

接着以下是剪裁工具类ClipImageUtils的代码:

package com.example.clipimage;

import android.graphics.Bitmap;

/**
 * 裁剪工具类
 *
 * @author freeman.wu
 *
 */
public class ClipImageUtils {
	/**
	 * 对源位图进行剪裁
	 *
	 * @param source
	 * @param x
	 * @param y
	 * @param width
	 *            剪裁内容的宽度
	 * @param height
	 *            剪裁内容的高度
	 * @param imWidth
	 * @param imHeight
	 * @return
	 */
	public static Bitmap clipImage(Bitmap source, int x, int y, int width,
			int height, int imWidth, int imHeight) {

		int bmWidth = source.getWidth();
		int bmHeight = source.getHeight();
		float scale = Math.min((float) bmWidth / imWidth, (float) bmHeight
				/ imHeight);

		return clipImage(source, x, y, width, height, scale);
	}

	/**
	 * 对源位图进行剪裁
	 *
	 * @param source
	 * @param x
	 * @param y
	 * @param width
	 *            剪裁内容的宽度
	 * @param height
	 *            剪裁内容的高度
	 * @param scale
	 *            剪裁比例
	 * @return
	 */
	public static Bitmap clipImage(Bitmap source, int x, int y, int width,
			int height, float scale) {

		int bmWidth = source.getWidth();
		int bmHeight = source.getHeight();

		x *= scale;
		y *= scale;
		width *= scale;
		height *= scale;

		/**
		 * 校正x,y的值
		 */
		x = (x + width > bmWidth) ?

bmWidth - width : x;
		x = x < 0 ? 0 : x;
		y = (y + height > bmHeight) ? bmHeight - height : y;
		y = y < 0 ? 0 : y;

		return Bitmap.createBitmap(source, x, y, width, height);
	}
}

因为实现方法简单。所以。我就不费太多口舌,在这里就简单地解释下控件的实现思路:

1. 控件继承自ImageView。为了在ImageView上面可以绘制到剪裁控,所以必需要重写onDraw()方法。然后对其进行绘制

2.在onDraw()中。主要须要绘制的对象有两个。第一是剪裁框,第二是剪裁框外的阴影。

3.使用剪裁工具类对ClipImageView中剪裁框内的内容从原图片剪裁出来。

基本内容就是这些了,控件使用起来也是十分方便,所以我就不放demo了。

假设有须要demo的朋友,请留个言吧。

应大家要求, 补上效果图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3V6aGlwZW5nMTk5MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

欢迎大家进行交流,转载请标明 http://blog.csdn.net/wuzhipeng1991/article/details/41120583,谢谢!

时间: 2024-11-11 01:49:49

图片剪裁控件——ClipImageView的相关文章

Android开发技巧——定制仿微信图片裁剪控件

拍照--裁剪,或者是选择图片--裁剪,是我们设置头像或上传图片时经常需要的一组操作.上篇讲了Camera的使用,这篇讲一下我对图片裁剪的实现. 背景 下面的需求都来自产品. 裁剪图片要像微信那样,拖动和放大的是图片,裁剪框不动. 裁剪框外的内容要有半透明黑色遮罩. 裁剪框下面要显示一行提示文字(这点我至今还是持保留意见的). 在Android中,裁剪图片的控件库还是挺多的,特别是github上比较流行的几个,都已经进化到比较稳定的阶段,但比较遗憾的是它们的裁剪过程是拖动或缩放裁剪框,于是只好自己

Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38140505 自从Gallery被谷歌废弃以后,Google推荐使用ViewPager和HorizontalScrollView来实现Gallery的效果.的确HorizontalScrollView可以实现Gallery的效果,但是HorizontalScrollView存在一个很大的问题,如果你仅是用来展示少量的图片,应该是没问题的,但是如果我希望HorizontalScr

我写的一个 Qt 显示图片的控件

Qt 中没有专门显示图片的控件,通常我们会使用QLabel来显示图片.但是QLabel 显示图片的能力还是有点弱.比如不支持图像的缩放一类的功能,使用起来不是很方便.因此我就自己写了个简单的类. 我这个类支持三种图像显示模式,我分别称之为:FIXED_SIZE, CENTRED,AUTO_ZOOM, AUTO_SIZE. FIXED_SIZE 模式下,显示的图像大小等于图像尺寸乘以缩放因子,如果控件的尺寸小于这个大小则多出的部分被裁切掉. FIX_SIZE_CENTRED模式与FIXED_SIZ

【WP8】图片缓存控件

在做图片相关的应用的时候,经常需要用大图片的缓存,默认的Image控件不支持缓存的支持,本文自定义一个支持图片缓存的控件 当图片的地址是网络图片时候 根据Url判断该图片是否存在本地,如果存在,则直接从本地读取,如果不存在,则通过Http请求下载该图片,保存到本地,然后读取到Image控件中 当图片为本地地址的时候,直接从本地读取,设置到Image控件中 1.在定义可缓存图片控件之前,先封装一下文件存储的帮助类 using System; using System.IO; using Syste

从数码图片读取条码或者生成数码图片条码控件VintaSoftBarcode.NET Library

VintaSoftBarcode.NET Library 条形码控件是一个完美的条码读写.NET 库,支持从数码图片读取条码或者生成数码图片条码. 具体功能: 支持多线程 支持创建条码图片 支持为生成的条码指定大小和分辨率 拥有许多选项控制条码创建 支持从图片对象.图片文件.和PDF文档读取条码 编程环境:.NET 框架. 这是一个可完全操纵的.NET 库,保证在.NET 框架中实现快速工作. 能够识别图片中的所有条码. 确认已识别的条码类型. 返回条码的字符串值. 自动检测条码的方位. 返回条

WP8__实现ListBox横向滑动及子项绑定图片等控件

<!--实现绑定的图片等信息 ListBox水平滚动--> <Grid> <Grid.Resources> <Style x:Key="horizontalListBoxStyle" TargetType="ListBox"> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> &l

Android实现图片滚动控件

怎样实现图片滚动器的效果. 今天就总结下.这里我也不多说什么.直接源代码.不懂的直接提问我. 第一种实现方式:利用Gallery,但是这个现在已经过时了.我这里不多介绍了.只贴一个核心类. package com.drocode.swithcer; import java.util.TimerTask; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import

在MAC应用里显示多个图片(IKImageBrowserView控件使用)(二)

接着说上一片文章: 还需要在库里面增加: QuartzCore.framework Quartz.framework 这样才能编译通过. 以上是文件结构: 以上是界面结构 一个视图,图片类的,然后还要2个委托时间,分别和类绑定, 然后选中图片视图控件里面的视图,一般这类控件有2层,要选择里面那类. 然后分别绑定这两个委托.一个是数据源,一个是委托 然后还要将这个控件定义,在这两个类里面. 然后具体操作就是在数据源里面进行. 以下是运行效果. 可以全选,还可以改名称,这个需要你自己再修改一下代码.

【WPF】【UWP】借鉴 asp.net core 管道处理模型打造图片缓存控件 ImageEx

原文:[WPF][UWP]借鉴 asp.net core 管道处理模型打造图片缓存控件 ImageEx 在 Web 开发中,img 标签用来呈现图片,而且一般来说,浏览器是会对这些图片进行缓存的. 比如访问百度,我们可以发现,图片.脚本这种都是从缓存(内存缓存/磁盘缓存)中加载的,而不是再去访问一次百度的服务器,这样一方面改善了响应速度,另一方面也减轻了服务端的压力. 但是,对于 WPF 和 UWP 开发来说,原生的 Image 控件是只有内存缓存的,并没有磁盘缓存的,所以一旦程序退出了,下次再