你好,游客 登录 注册 搜索
背景:
阅读新闻

Android 自定义控件——图片剪裁

[日期:2014-12-23] 来源:Linux社区  作者:Mingwei [字体: ]

本文介绍一个Android自定义的图片剪裁控件

该控件由另一篇文章:Android 图片拖拽、放大缩小的自定义控件 扩展而来 http://www.linuxidc.com/Linux/2014-12/110763.htm

如图:

Android 自定义控件——图片剪裁

思路:在一个自定义View上绘制一张图片(参照前面提到的另一篇文章),在该自定义View上绘制一个自定义的FloatDrawable,也就是图中的浮层。绘制图片和FloatDrawable的交集的补集部分灰色阴影(这个其实很简单,就一句话)。在自定义View的touch中去处理具体的拖动事件和FloatDrawable的变换。图片的绘制和FloatDrawable的绘制以及变换最终其实就是在操作各自的Rect而已,Rect就是一个有矩形,有四个坐标,图片和FloatDrawable就是按照坐标去绘制的。

CropImageView.java

该类继承View

功能:在onDraw方法中画图片、浮层,处理touch事件,最后根据坐标对图片进行剪裁。

public class CropImageView extends View {
 // 在touch重要用到的点,
 private float mX_1 = 0;
 private float mY_1 = 0;
 // 触摸事件判断
 private final int STATUS_SINGLE = 1;
 private final int STATUS_MULTI_START = 2;
 private final int STATUS_MULTI_TOUCHING = 3;
 // 当前状态
 private int mStatus = STATUS_SINGLE;
 // 默认裁剪的宽高
 private int cropWidth;
 private int cropHeight;
 // 浮层Drawable的四个点
 private final int EDGE_LT = 1;
 private final int EDGE_RT = 2;
 private final int EDGE_LB = 3;
 private final int EDGE_RB = 4;
 private final int EDGE_MOVE_IN = 5;
 private final int EDGE_MOVE_OUT = 6;
 private final int EDGE_NONE = 7;

 public int currentEdge = EDGE_NONE;

 protected float oriRationWH = 0;
 protected final float maxZoomOut = 5.0f;
 protected final float minZoomIn = 0.333333f;

 protected Drawable mDrawable;
 protected FloatDrawable mFloatDrawable;

 protected Rect mDrawableSrc = new Rect();// 图片Rect变换时的Rect
 protected Rect mDrawableDst = new Rect();// 图片Rect
 protected Rect mDrawableFloat = new Rect();// 浮层的Rect
 protected boolean isFrist = true;
 private boolean isTouchInSquare = true;

 protected Context mContext;

 public CropImageView(Context context) {
  super(context);
  init(context);
 }

 public CropImageView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init(context);
 }

 public CropImageView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init(context);

 }

 @SuppressLint("NewApi")
 private void init(Context context) {
  this.mContext = context;
  try {
   if (android.os.Build.VERSION.SDK_INT >= 11) {
    this.setLayerType(LAYER_TYPE_SOFTWARE, null);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  mFloatDrawable = new FloatDrawable(context);
 }

 public void setDrawable(Drawable mDrawable, int cropWidth, int cropHeight) {
  this.mDrawable = mDrawable;
  this.cropWidth = cropWidth;
  this.cropHeight = cropHeight;
  this.isFrist = true;
  invalidate();
 }

 @SuppressLint("ClickableViewAccessibility")
 @Override
 public boolean onTouchEvent(MotionEvent event) {

  if (event.getPointerCount() > 1) {
   if (mStatus == STATUS_SINGLE) {
    mStatus = STATUS_MULTI_START;
   } else if (mStatus == STATUS_MULTI_START) {
    mStatus = STATUS_MULTI_TOUCHING;
   }
  } else {
   if (mStatus == STATUS_MULTI_START
     || mStatus == STATUS_MULTI_TOUCHING) {
    mX_1 = event.getX();
    mY_1 = event.getY();
   }

   mStatus = STATUS_SINGLE;
  }

  switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:
   mX_1 = event.getX();
   mY_1 = event.getY();
   currentEdge = getTouch((int) mX_1, (int) mY_1);
   isTouchInSquare = mDrawableFloat.contains((int) event.getX(),
     (int) event.getY());

   break;

  case MotionEvent.ACTION_UP:
   checkBounds();
   break;

  case MotionEvent.ACTION_POINTER_UP:
   currentEdge = EDGE_NONE;
   break;

  case MotionEvent.ACTION_MOVE:
   if (mStatus == STATUS_MULTI_TOUCHING) {

   } else if (mStatus == STATUS_SINGLE) {
    int dx = (int) (event.getX() - mX_1);
    int dy = (int) (event.getY() - mY_1);

    mX_1 = event.getX();
    mY_1 = event.getY();
    // 根據得到的那一个角,并且变换Rect
    if (!(dx == 0 && dy == 0)) {
     switch (currentEdge) {
     case EDGE_LT:
      mDrawableFloat.set(mDrawableFloat.left + dx,
        mDrawableFloat.top + dy, mDrawableFloat.right,
        mDrawableFloat.bottom);
      break;

     case EDGE_RT:
      mDrawableFloat.set(mDrawableFloat.left,
        mDrawableFloat.top + dy, mDrawableFloat.right
          + dx, mDrawableFloat.bottom);
      break;

     case EDGE_LB:
      mDrawableFloat.set(mDrawableFloat.left + dx,
        mDrawableFloat.top, mDrawableFloat.right,
        mDrawableFloat.bottom + dy);
      break;

     case EDGE_RB:
      mDrawableFloat.set(mDrawableFloat.left,
        mDrawableFloat.top, mDrawableFloat.right + dx,
        mDrawableFloat.bottom + dy);
      break;

     case EDGE_MOVE_IN:
      if (isTouchInSquare) {
       mDrawableFloat.offset((int) dx, (int) dy);
      }
      break;

     case EDGE_MOVE_OUT:
      break;
     }
     mDrawableFloat.sort();
     invalidate();
    }
   }
   break;
  }

  return true;
 }

 // 根据初触摸点判断是触摸的Rect哪一个角
 public int getTouch(int eventX, int eventY) {
  if (mFloatDrawable.getBounds().left <= eventX
    && eventX < (mFloatDrawable.getBounds().left + mFloatDrawable
      .getBorderWidth())
    && mFloatDrawable.getBounds().top <= eventY
    && eventY < (mFloatDrawable.getBounds().top + mFloatDrawable
      .getBorderHeight())) {
   return EDGE_LT;
  } else if ((mFloatDrawable.getBounds().right - mFloatDrawable
    .getBorderWidth()) <= eventX
    && eventX < mFloatDrawable.getBounds().right
    && mFloatDrawable.getBounds().top <= eventY
    && eventY < (mFloatDrawable.getBounds().top + mFloatDrawable
      .getBorderHeight())) {
   return EDGE_RT;
  } else if (mFloatDrawable.getBounds().left <= eventX
    && eventX < (mFloatDrawable.getBounds().left + mFloatDrawable
      .getBorderWidth())
    && (mFloatDrawable.getBounds().bottom - mFloatDrawable
      .getBorderHeight()) <= eventY
    && eventY < mFloatDrawable.getBounds().bottom) {
   return EDGE_LB;
  } else if ((mFloatDrawable.getBounds().right - mFloatDrawable
    .getBorderWidth()) <= eventX
    && eventX < mFloatDrawable.getBounds().right
    && (mFloatDrawable.getBounds().bottom - mFloatDrawable
      .getBorderHeight()) <= eventY
    && eventY < mFloatDrawable.getBounds().bottom) {
   return EDGE_RB;
  } else if (mFloatDrawable.getBounds().contains(eventX, eventY)) {
   return EDGE_MOVE_IN;
  }
  return EDGE_MOVE_OUT;
 }

 @Override
 protected void onDraw(Canvas canvas) {

  if (mDrawable == null) {
   return;
  }

  if (mDrawable.getIntrinsicWidth() == 0
    || mDrawable.getIntrinsicHeight() == 0) {
   return;
  }

  configureBounds();
  // 在画布上花图片
  mDrawable.draw(canvas);
  canvas.save();
  // 在画布上画浮层FloatDrawable,Region.Op.DIFFERENCE是表示Rect交集的补集
  canvas.clipRect(mDrawableFloat, Region.Op.DIFFERENCE);
  // 在交集的补集上画上灰色用来区分
  canvas.drawColor(Color.parseColor("#a0000000"));
  canvas.restore();
  // 画浮层
  mFloatDrawable.draw(canvas);
 }

 protected void configureBounds() {
  // configureBounds在onDraw方法中调用
  // isFirst的目的是下面对mDrawableSrc和mDrawableFloat只初始化一次,
  // 之后的变化是根据touch事件来变化的,而不是每次执行重新对mDrawableSrc和mDrawableFloat进行设置
  if (isFrist) {
   oriRationWH = ((float) mDrawable.getIntrinsicWidth())
     / ((float) mDrawable.getIntrinsicHeight());

   final float scale = mContext.getResources().getDisplayMetrics().density;
   int w = Math.min(getWidth(), (int) (mDrawable.getIntrinsicWidth()
     * scale + 0.5f));
   int h = (int) (w / oriRationWH);

   int left = (getWidth() - w) / 2;
   int top = (getHeight() - h) / 2;
   int right = left + w;
   int bottom = top + h;

   mDrawableSrc.set(left, top, right, bottom);
   mDrawableDst.set(mDrawableSrc);

   int floatWidth = dipTopx(mContext, cropWidth);
   int floatHeight = dipTopx(mContext, cropHeight);

   if (floatWidth > getWidth()) {
    floatWidth = getWidth();
    floatHeight = cropHeight * floatWidth / cropWidth;
   }

   if (floatHeight > getHeight()) {
    floatHeight = getHeight();
    floatWidth = cropWidth * floatHeight / cropHeight;
   }

   int floatLeft = (getWidth() - floatWidth) / 2;
   int floatTop = (getHeight() - floatHeight) / 2;
   mDrawableFloat.set(floatLeft, floatTop, floatLeft + floatWidth,
     floatTop + floatHeight);

   isFrist = false;
  }

  mDrawable.setBounds(mDrawableDst);
  mFloatDrawable.setBounds(mDrawableFloat);
 }

 // 在up事件中调用了该方法,目的是检查是否把浮层拖出了屏幕
 protected void checkBounds() {
  int newLeft = mDrawableFloat.left;
  int newTop = mDrawableFloat.top;

  boolean isChange = false;
  if (mDrawableFloat.left < getLeft()) {
   newLeft = getLeft();
   isChange = true;
  }

  if (mDrawableFloat.top < getTop()) {
   newTop = getTop();
   isChange = true;
  }

  if (mDrawableFloat.right > getRight()) {
   newLeft = getRight() - mDrawableFloat.width();
   isChange = true;
  }

  if (mDrawableFloat.bottom > getBottom()) {
   newTop = getBottom() - mDrawableFloat.height();
   isChange = true;
  }

  mDrawableFloat.offsetTo(newLeft, newTop);
  if (isChange) {
   invalidate();
  }
 }

 // 进行图片的裁剪,所谓的裁剪就是根据Drawable的新的坐标在画布上创建一张新的图片
 public Bitmap getCropImage() {
  Bitmap tmpBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
    Config.RGB_565);
  Canvas canvas = new Canvas(tmpBitmap);
  mDrawable.draw(canvas);

  Matrix matrix = new Matrix();
  float scale = (float) (mDrawableSrc.width())
    / (float) (mDrawableDst.width());
  matrix.postScale(scale, scale);

  Bitmap ret = Bitmap.createBitmap(tmpBitmap, mDrawableFloat.left,
    mDrawableFloat.top, mDrawableFloat.width(),
    mDrawableFloat.height(), matrix, true);
  tmpBitmap.recycle();
  tmpBitmap = null;

  return ret;
 }

 public int dipTopx(Context context, float dpValue) {
  final float scale = context.getResources().getDisplayMetrics().density;
  return (int) (dpValue * scale + 0.5f);
 }
}

FloatDrawable.java

继承自Drawable

功能:图片上面的浮动框,通过拖动确定位置

public class FloatDrawable extends Drawable {

 private Context mContext;
 private int offset = 50;
 private Paint mLinePaint = new Paint();
 private Paint mLinePaint2 = new Paint();
 {
  mLinePaint.setARGB(200, 50, 50, 50);
  mLinePaint.setStrokeWidth(1F);
  mLinePaint.setStyle(Paint.Style.STROKE);
  mLinePaint.setAntiAlias(true);
  mLinePaint.setColor(Color.WHITE);
  //
  mLinePaint2.setARGB(200, 50, 50, 50);
  mLinePaint2.setStrokeWidth(7F);
  mLinePaint2.setStyle(Paint.Style.STROKE);
  mLinePaint2.setAntiAlias(true);
  mLinePaint2.setColor(Color.WHITE);
 }

 public FloatDrawable(Context context) {
  super();
  this.mContext = context;

 }

 public int getBorderWidth() {
  return dipTopx(mContext, offset);//根据dip计算的像素值,做适配用的
 }

 public int getBorderHeight() {
  return dipTopx(mContext, offset);
 }

 @Override
 public void draw(Canvas canvas) {

  int left = getBounds().left;
  int top = getBounds().top;
  int right = getBounds().right;
  int bottom = getBounds().bottom;

  Rect mRect = new Rect(left + dipTopx(mContext, offset) / 2, top
    + dipTopx(mContext, offset) / 2, right
    - dipTopx(mContext, offset) / 2, bottom
    - dipTopx(mContext, offset) / 2);
  //画默认的选择框
  canvas.drawRect(mRect, mLinePaint);
  //画四个角的四个粗拐角、也就是八条粗线
  canvas.drawLine((left + dipTopx(mContext, offset) / 2 - 3.5f), top
    + dipTopx(mContext, offset) / 2,
    left + dipTopx(mContext, offset) - 8f,
    top + dipTopx(mContext, offset) / 2, mLinePaint2);
  canvas.drawLine(left + dipTopx(mContext, offset) / 2,
    top + dipTopx(mContext, offset) / 2,
    left + dipTopx(mContext, offset) / 2,
    top + dipTopx(mContext, offset) / 2 + 30, mLinePaint2);
  canvas.drawLine(right - dipTopx(mContext, offset) + 8f,
    top + dipTopx(mContext, offset) / 2,
    right - dipTopx(mContext, offset) / 2,
    top + dipTopx(mContext, offset) / 2, mLinePaint2);
  canvas.drawLine(right - dipTopx(mContext, offset) / 2,
    top + dipTopx(mContext, offset) / 2 - 3.5f,
    right - dipTopx(mContext, offset) / 2,
    top + dipTopx(mContext, offset) / 2 + 30, mLinePaint2);
  canvas.drawLine((left + dipTopx(mContext, offset) / 2 - 3.5f), bottom
    - dipTopx(mContext, offset) / 2,
    left + dipTopx(mContext, offset) - 8f,
    bottom - dipTopx(mContext, offset) / 2, mLinePaint2);
  canvas.drawLine((left + dipTopx(mContext, offset) / 2), bottom
    - dipTopx(mContext, offset) / 2,
    (left + dipTopx(mContext, offset) / 2),
    bottom - dipTopx(mContext, offset) / 2 - 30f, mLinePaint2);
  canvas.drawLine((right - dipTopx(mContext, offset) + 8f), bottom
    - dipTopx(mContext, offset) / 2,
    right - dipTopx(mContext, offset) / 2,
    bottom - dipTopx(mContext, offset) / 2, mLinePaint2);
  canvas.drawLine((right - dipTopx(mContext, offset) / 2), bottom
    - dipTopx(mContext, offset) / 2 - 30f,
    right - dipTopx(mContext, offset) / 2,
    bottom - dipTopx(mContext, offset) / 2 + 3.5f, mLinePaint2);

 }

 @Override
 public void setBounds(Rect bounds) {
  super.setBounds(new Rect(bounds.left - dipTopx(mContext, offset) / 2,
    bounds.top - dipTopx(mContext, offset) / 2, bounds.right
      + dipTopx(mContext, offset) / 2, bounds.bottom
      + dipTopx(mContext, offset) / 2));
 }

 @Override
 public void setAlpha(int alpha) {

 }

 @Override
 public void setColorFilter(ColorFilter cf) {

 }

 @Override
 public int getOpacity() {
  return 0;
 }

 public int dipTopx(Context context, float dpValue) {
  final float scale = context.getResources().getDisplayMetrics().density;
  return (int) (dpValue * scale + 0.5f);
 }

}

使用

布局中:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.onehead.cropimage.CropImageView
        android:id="@+id/cropimage"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

Activity中:

public class MainActivity extends ActionBarActivity {
 private CropImageView mView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  mView = (CropImageView) findViewById(R.id.cropimage);
  //设置资源和默认长宽
  mView.setDrawable(getResources().getDrawable(R.drawable.test2), 300,
    300);
  //调用该方法得到剪裁好的图片
  Bitmap mBitmap= mView.getCropImage();
 }

}

最简单的Ubuntu Touch & Android 双系统安装方式 http://www.linuxidc.com/Linux/2014-01/94881.htm

在Nexus上实现Ubuntu和Android 4.4.2 双启动 http://www.linuxidc.com/Linux/2014-05/101849.htm

Ubuntu 14.04 配置 Android SDK 开发环境 http://www.linuxidc.com/Linux/2014-05/101039.htm

64位Ubuntu 11.10下Android开发环境的搭建(JDK+Eclipse+ADT+Android SDK详细) http://www.linuxidc.com/Linux/2013-06/85303.htm

Ubuntu 14.04 x64配置Android 4.4 kitkat编译环境的方法 http://www.linuxidc.com/Linux/2014-04/101148.htm

Ubuntu 12.10 x64 安装 Android SDK http://www.linuxidc.com/Linux/2013-03/82005.htm

更多Android相关信息见Android 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=11

本文永久更新链接地址http://www.linuxidc.com/Linux/2014-12/110764.htm

linux
相关资讯       Android自定义控件  Android图片剪裁 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款