• 图片剪裁控件——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的朋友,请留个言吧。

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




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

  • 相关阅读:
    GitLab CI/CD的官译【原】
    Gearman介绍、原理分析、实践改进
    Golang逃逸分析
    Go 程序是怎样跑起来的
    分布式系统的常见玩法
    开发更高可用、高质量的服务的一些建议
    理解 Kubernetes 的亲和性调度
    服务发现对比:Zookeeper vs etcd vs Consul
    探索etcd,Zookeeper和Consul一致键值数据存储的性能
    CentOS 7 安装无线驱动
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7262403.html
Copyright © 2020-2023  润新知