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

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

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

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

    拍照--裁剪,或者是选择图片--裁剪,是我们设置头像或上传图片时经常需要的一组操作.上篇讲了Camera的使用,这篇讲一下我对图片裁剪的实现. 背景 下面的需求都来自产品. 裁剪图片要像微信那样,拖动 ...

  2. UI-UIImageView的图片填充方式(contentMode)_图片作为控件背景图的拉伸方式(stretch)介绍

    常用图片填充方式 这里只介绍三个最常用的图片填充方式 UIViewContentModeScaleToFill模式会导致图片变形.例如: UIViewContentModeScaleAspectFit ...

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

    Qt 中没有专门显示图片的控件.通常我们会使用QLabel来显示图片.可是QLabel 显示图片的能力还是有点弱.比方不支持图像的缩放一类的功能.使用起来不是非常方便. 因此我就自己写了个简单的类. ...

  4. MFC入门(三)-- MFC图片/文字控件(循环显示文字和图片的小程序)

    惯例附上前几个博客的链接: MFC入门(一)简单配置:http://blog.csdn.net/zmdsjtu/article/details/52311107 MFC入门(二)读取输入字符:http ...

  5. 关于IOS某图片添加控件,图片从相册或拍照保存后,再次进入时点击放大图无法显示的问题

    某图片添加控件: https://github.com/XZTLLQ/LQPhotoPickerDemo 问题: 标题已说明 代码块: NSArray *alAssetUrl =(NSMutableA ...

  6. Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来

    首先题外话,今天早上起床的时候,手滑一下把我的手机甩了出去,结果陪伴我两年半的摩托罗拉里程碑一代就这么安息了,于是我今天决定怒更一记,纪念我死去的爱机. 如果你是网购达人,你的手机上一定少不了淘宝客户 ...

  7. 用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机

    原文:用MVVM模式开发中遇到的零散问题总结(5)--将动态加载的可视元素保存为图片的控件,Binding刷新的时机 在项目开发中经常会遇到这样一种情况,就是需要将用户填写的信息排版到一张表单中,供打 ...

  8. 一个 Qt 显示图片的控件(继承QWidget,使用QPixmap记录图像,最后在paintEvent进行绘制,可缩放)

    Qt 中没有专门显示图片的控件,通常我们会使用QLabel来显示图片.但是QLabel 显示图片的能力还是有点弱.比如不支持图像的缩放一类的功能,使用起来不是很方便.因此我就自己写了个简单的类. 我这 ...

  9. Android微信九宫格图片展示控件

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/214 Android微信九宫格图片展示控件 半年前,公司产 ...

随机推荐

  1. Overview of iOS Crash Reporting Tools: Part 2/2

    Thanks for joining me for the second part of this two-part series on crash reporting services! The f ...

  2. 【Git】windows上git命令中文乱码的问题

    windows上git命令中文乱码的问题解决 1.打开git bash快捷方式启动 2.右键 options 3.进入text选项卡,选中中文 和UTF-8 4.应用 测试[中文正常显示] 尝试打开文 ...

  3. JAVA常见算法题(十)

    package com.xiaowu.demo; /** * 一球从100米高度自由落下,每次落地后反跳回原高度的一半:再落下……求它在第10次落地时,共经过多少米?第10次反弹多高? * * @au ...

  4. github 丢失的本地提交

    open git bash git reflog git reset xxxxxxx

  5. 新人补钙系列教程之:AS3 位运算符

    ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数).在 ECMAScript 中,所有整数字面量默认都是有符号整数,这意味着什么呢? 有符号整数使用 3 ...

  6. Linux C存取效率对照——堆、栈、常量区

    本文主要探讨堆和栈在使用中的存取效率.利用宏汇编指令分析訪存情况来进行简单推断. 实验环境及使用工具:i686,32位Ubuntu Linux.gcc (Ubuntu/Linaro 4.6.3-1ub ...

  7. OC第五课

    主要内容:字典.集合.数组排序 一.字典 演示样例: name : @" 张三 " .sex:@" 男 " ; age :@" 21 " ; ...

  8. Weblogic OutOfMemory exception的误解 -- thread limitation

    不是全部的OutofMemory exception都是内存问题... 前几天有个客户的site报了下面错误: [ERROR][thread ] Could not start thread Time ...

  9. 人工智能真NB?何不去炒股?

    自从AlphaGo/Master战胜人类棋手,人工智能替代人工的呼声就甚嚣尘上.人工智能还有个小伙伴名为大数据也来势汹汹,貌似未来二者联手要取代人类似的. 人工智能真的锐不可当?AlphaGo/Mas ...

  10. hibernate学习系列-----(3)Session 缓存和持久化生命周期以及Session 基本操作

    Session缓存原理 为了能够在控制台更好的看到我们的hibernate干了些什么,可以在hibernate.cfg.xml文件中写入如下配置: <!-- print all generate ...