• [Android] 图片裁剪总结——自定义裁剪工具


    上次弄完调用系统裁剪之后,我又试着做一个自定义的裁剪工具。

    原文地址请保留http://www.cnblogs.com/rossoneri/p/3988405.html

    老习惯,文章开始前还是先把我参考的资料贴出来。您愿意节省点时间看别人的更好的就直接从下面链接跳走~愿意看看我怎么做的那就先谢谢了!

    GitHub上老外做的一个非常棒的demo,代码也很漂亮

    android自定义view实现裁剪图片功能,不使用系统的

    第一个链接代码写的太好了,不过很多我用不上,也不需要那么麻烦的文件结构;第二个代码比较简单,但有些地方还是有借鉴意义的。

    下面是我的代码,时间紧,就先不写太详细了:

    注意几点:

    我是在平板上做的测试,代码可能不适应手机,这个很好改..

    我写这个是当作从外部传递一个绝对路径进来再做裁剪的,所以图省事儿就在设备里/sdcard/下放了一张图片,从mainActivity传进去..所以运行前自己先随便整个图片进去..或者自己改代码..

    这个做起来不难.就是特麻烦.我也是粗略做做..UI什么的都没去搞..有空再弄吧..好歹也是个能用用的工具..

    做好了会传到github上..到时候发链接..

    activity_main.xml

     1 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:id="@+id/scrollview"
     4     android:layout_width="wrap_content"
     5     android:layout_height="wrap_content" >
     6 
     7     <LinearLayout
     8         android:layout_width="match_parent"
     9         android:layout_height="wrap_content"
    10         android:layout_marginLeft="30dp"
    11         android:layout_marginRight="30dp"
    12         android:orientation="vertical" >
    13 
    14         <Button
    15             android:id="@+id/btn_crop"
    16             android:layout_width="wrap_content"
    17             android:layout_height="wrap_content"
    18             android:text="Crop" />
    19 
    20         <Button
    21             android:id="@+id/btn_cancel"
    22             android:layout_width="wrap_content"
    23             android:layout_height="wrap_content"
    24             android:text="Cancel" />
    25 
    26         <com.example.crop_image_my.MyCropView
    27             android:id="@+id/myCropView"
    28             android:layout_width="900dp"
    29             android:layout_height="600dp"
    30             android:src="@drawable/violetsky" />
    31 
    32         <ImageView
    33             android:id="@+id/croppedImageView"
    34             android:layout_width="wrap_content"
    35             android:layout_height="wrap_content" />
    36     </LinearLayout>
    37 
    38 </ScrollView>

    mainActivity.java

     1 package com.example.crop_image_my;
     2 
     3 import android.os.Bundle;
     4 import android.app.Activity;
     5 import android.graphics.Bitmap;
     6 import android.view.Menu;
     7 import android.view.MotionEvent;
     8 import android.view.View;
     9 import android.view.View.OnClickListener;
    10 import android.widget.Button;
    11 import android.widget.ImageView;
    12 import android.widget.ScrollView;
    13 
    14 public class MainActivity extends Activity implements OnClickListener {
    15 
    16     private MyCropView myCropView;
    17     private Button btnCrop;
    18     private Button btnCancel;
    19     private ImageView croppedImageView;
    20     private ScrollView sv;
    21 
    22     // 假设从图片选择器传递来的图片路径如下
    23     private static final String CROP_IMAGE_PATH = "/sdcard/crop.jpg";
    24 
    25     @Override
    26     protected void onCreate(Bundle savedInstanceState) {
    27         super.onCreate(savedInstanceState);
    28         setContentView(R.layout.activity_main);
    29 
    30         myCropView = (MyCropView) findViewById(R.id.myCropView);
    31         btnCrop = (Button) findViewById(R.id.btn_crop);
    32         btnCancel = (Button) findViewById(R.id.btn_cancel);
    33         croppedImageView = (ImageView) findViewById(R.id.croppedImageView);
    34         sv = (ScrollView) findViewById(R.id.scrollview);
    35 
    36         myCropView.setBmpPath(CROP_IMAGE_PATH);
    37         btnCrop.setOnClickListener(this);
    38         btnCancel.setOnClickListener(this);
    39 
    40         sv.setOnTouchListener(new View.OnTouchListener() {
    41 
    42             @Override
    43             public boolean onTouch(View v, MotionEvent event) {
    44                 // TODO Auto-generated method stub
    45                 myCropView.getParent().requestDisallowInterceptTouchEvent(false);
    46                 return false;
    47             }
    48         });
    49     }
    50 
    51     @Override
    52     public void onClick(View v) {
    53         // TODO Auto-generated method stub
    54         switch (v.getId()) {
    55         case R.id.btn_crop:
    56             Bitmap croppedImage = myCropView.getCroppedImage();
    57 
    58             croppedImageView.setImageBitmap(croppedImage);
    59             break;
    60         case R.id.btn_cancel:
    61 
    62             break;
    63         default:
    64             break;
    65         }
    66     }
    67 
    68     @Override
    69     public boolean onCreateOptionsMenu(Menu menu) {
    70         // Inflate the menu; this adds items to the action bar if it is present.
    71         getMenuInflater().inflate(R.menu.activity_main, menu);
    72         return true;
    73     }
    74 
    75 }

    MyCropView.java

      1 package com.example.crop_image_my;
      2 
      3 import android.content.Context;
      4 import android.drm.DrmStore.RightsStatus;
      5 import android.graphics.Bitmap;
      6 import android.graphics.BitmapFactory;
      7 import android.graphics.Canvas;
      8 import android.graphics.Color;
      9 import android.graphics.Paint;
     10 import android.graphics.Paint.Style;
     11 import android.graphics.PointF;
     12 import android.graphics.RectF;
     13 import android.util.AttributeSet;
     14 import android.view.MotionEvent;
     15 import android.view.View;
     16 import android.widget.Toast;
     17 
     18 public class MyCropView extends View {
     19 
     20     // Private Constants ///////////////////////////////////////////////////////
     21     private static final float BMP_LEFT = 0f;
     22     private static final float BMP_TOP = 20f;
     23 
     24     private static final float DEFAULT_BORDER_RECT_WIDTH = 200f;
     25     private static final float DEFAULT_BORDER_RECT_HEIGHT = 200f;
     26 
     27     private static final int POS_TOP_LEFT = 0;
     28     private static final int POS_TOP_RIGHT = 1;
     29     private static final int POS_BOTTOM_LEFT = 2;
     30     private static final int POS_BOTTOM_RIGHT = 3;
     31     private static final int POS_TOP = 4;
     32     private static final int POS_BOTTOM = 5;
     33     private static final int POS_LEFT = 6;
     34     private static final int POS_RIGHT = 7;
     35     private static final int POS_CENTER = 8;
     36 
     37     // this constant would be best to use event number
     38     private static final float BORDER_LINE_WIDTH = 6f;
     39     private static final float BORDER_CORNER_LENGTH = 30f;
     40     private static final float TOUCH_FIELD = 10f;
     41 
     42     // Member Variables ////////////////////////////////////////////////////////
     43     private String mBmpPath;
     44     private Bitmap mBmpToCrop;
     45     private RectF mBmpBound;
     46     private Paint mBmpPaint;
     47 
     48     private Paint mBorderPaint;// 裁剪区边框
     49     private Paint mGuidelinePaint;
     50     private Paint mCornerPaint;
     51     private Paint mBgPaint;
     52 
     53     private RectF mDefaultBorderBound;
     54     private RectF mBorderBound;
     55 
     56     private PointF mLastPoint = new PointF();
     57 
     58     private float mBorderWidth;
     59     private float mBorderHeight;
     60 
     61     private int touchPos;
     62 
     63     // Constructors ////////////////////////////////////////////////////////////
     64     public MyCropView(Context context) {
     65         super(context);
     66         // TODO Auto-generated constructor stub
     67         init(context);
     68     }
     69 
     70     public MyCropView(Context context, AttributeSet attrs) {
     71         super(context, attrs);
     72         init(context);
     73     }
     74 
     75     // View Methods ////////////////////////////////////////////////////////////
     76     @Override
     77     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
     78         // TODO Auto-generated method stub
     79         // super.onSizeChanged(w, h, oldw, oldh);
     80     }
     81 
     82     @Override
     83     protected void onDraw(Canvas canvas) {
     84         // TODO Auto-generated method stub
     85         // super.onDraw(canvas);
     86         if (mBmpPath != null) {
     87             canvas.drawBitmap(mBmpToCrop, null, mBmpBound, mBmpPaint);
     88             canvas.drawRect(mBorderBound.left, mBorderBound.top, mBorderBound.right, mBorderBound.bottom, mBorderPaint);
     89             drawGuidlines(canvas);
     90             drawBackground(canvas);
     91         }
     92     }
     93 
     94     @Override
     95     public boolean onTouchEvent(MotionEvent event) {
     96         // TODO Auto-generated method stub
     97         // super.onTouchEvent(event);
     98         switch (event.getAction()) {
     99         case MotionEvent.ACTION_DOWN:
    100             setLastPosition(event);
    101             getParent().requestDisallowInterceptTouchEvent(true);
    102             // onActionDown(event.getX(), event.getY());
    103             touchPos = detectTouchPosition(event.getX(), event.getY());
    104             break;
    105         case MotionEvent.ACTION_MOVE:
    106             onActionMove(event.getX(), event.getY());
    107             setLastPosition(event);
    108             break;
    109         case MotionEvent.ACTION_UP:
    110             break;
    111         }
    112 
    113         return true;
    114     }
    115 
    116     // Public Methods //////////////////////////////////////////////////////////
    117     public String getBmpPath() {
    118         return mBmpPath;
    119     }
    120 
    121     public void setBmpPath(String picPath) {
    122         this.mBmpPath = picPath;
    123         setBmp();
    124     }
    125 
    126     public Bitmap getCroppedImage() {
    127         // 先不考虑图片被压缩的情况 就当作现在的图片就是1:1的
    128 
    129         return Bitmap.createBitmap(mBmpToCrop, (int) mBorderBound.left, (int) mBorderBound.top, (int) mBorderWidth,
    130                 (int) mBorderHeight);
    131     }
    132 
    133     // Private Methods /////////////////////////////////////////////////////////
    134     private void init(Context context) {
    135 
    136         mBmpPaint = new Paint();
    137         // 以下是抗锯齿
    138         mBmpPaint.setAntiAlias(true);// 防止边缘的锯齿
    139         mBmpPaint.setFilterBitmap(true);// 对位图进行滤波处理
    140 
    141         mBorderPaint = new Paint();
    142         mBorderPaint.setStyle(Style.STROKE);
    143         mBorderPaint.setColor(Color.parseColor("#AAFFFFFF"));
    144         mBorderPaint.setStrokeWidth(BORDER_LINE_WIDTH);
    145 
    146         mGuidelinePaint = new Paint();
    147         mGuidelinePaint.setColor(Color.parseColor("#AAFFFFFF"));
    148         mGuidelinePaint.setStrokeWidth(1f);
    149 
    150         mCornerPaint = new Paint();
    151 
    152         mBgPaint = new Paint();
    153         mBgPaint.setColor(Color.parseColor("#B0000000"));
    154         mBgPaint.setAlpha(150);
    155 
    156     }
    157 
    158     private void setBmp() {
    159         mBmpToCrop = BitmapFactory.decodeFile(mBmpPath);
    160 
    161         mBmpBound = new RectF();
    162         mBmpBound.left = BMP_LEFT;
    163         mBmpBound.top = BMP_TOP;
    164         mBmpBound.right = mBmpBound.left + mBmpToCrop.getWidth();
    165         mBmpBound.bottom = mBmpBound.top + mBmpToCrop.getHeight();
    166 
    167         // 使裁剪框一开始出现在图片的中心位置
    168         mDefaultBorderBound = new RectF();
    169         mDefaultBorderBound.left = (mBmpBound.left + mBmpBound.right - DEFAULT_BORDER_RECT_WIDTH) / 2;
    170         mDefaultBorderBound.top = (mBmpBound.top + mBmpBound.bottom - DEFAULT_BORDER_RECT_HEIGHT) / 2;
    171         mDefaultBorderBound.right = mDefaultBorderBound.left + DEFAULT_BORDER_RECT_WIDTH;
    172         mDefaultBorderBound.bottom = mDefaultBorderBound.top + DEFAULT_BORDER_RECT_HEIGHT;
    173 
    174         mBorderBound = new RectF();
    175         mBorderBound.left = mDefaultBorderBound.left;
    176         mBorderBound.top = mDefaultBorderBound.top;
    177         mBorderBound.right = mDefaultBorderBound.right;
    178         mBorderBound.bottom = mDefaultBorderBound.bottom;
    179 
    180         getBorderEdgeLength();
    181         invalidate();
    182     }
    183 
    184     private void drawBackground(Canvas canvas) {
    185 
    186         /*-
    187           -------------------------------------
    188           |                top                |
    189           -------------------------------------
    190           |      |                    |       |<——————————mBmpBound
    191           |      |                    |       |
    192           | left |                    | right |
    193           |      |                    |       |
    194           |      |                  <─┼───────┼────mBorderBound
    195           -------------------------------------
    196           |              bottom               |
    197           -------------------------------------
    198          */
    199 
    200         // Draw "top", "bottom", "left", then "right" quadrants.
    201         // because the border line width is larger than 1f, in order to draw a complete border rect ,
    202         // i have to change zhe rect coordinate to draw
    203         float delta = BORDER_LINE_WIDTH / 2;
    204         float left = mBorderBound.left - delta;
    205         float top = mBorderBound.top - delta;
    206         float right = mBorderBound.right + delta;
    207         float bottom = mBorderBound.bottom + delta;
    208 
    209         // -------------------------------------------------------------------------------移动到上下两端会多出来阴影
    210         canvas.drawRect(mBmpBound.left, mBmpBound.top, mBmpBound.right, top, mBgPaint);
    211         canvas.drawRect(mBmpBound.left, bottom, mBmpBound.right, mBmpBound.bottom, mBgPaint);
    212         canvas.drawRect(mBmpBound.left, top, left, bottom, mBgPaint);
    213         canvas.drawRect(right, top, mBmpBound.right, bottom, mBgPaint);
    214     }
    215 
    216     // 画裁剪区域中间的参考线
    217     private void drawGuidlines(Canvas canvas) {
    218         // Draw vertical guidelines.
    219         final float oneThirdCropWidth = mBorderBound.width() / 3;
    220 
    221         final float x1 = mBorderBound.left + oneThirdCropWidth;
    222         canvas.drawLine(x1, mBorderBound.top, x1, mBorderBound.bottom, mGuidelinePaint);
    223         final float x2 = mBorderBound.right - oneThirdCropWidth;
    224         canvas.drawLine(x2, mBorderBound.top, x2, mBorderBound.bottom, mGuidelinePaint);
    225 
    226         // Draw horizontal guidelines.
    227         final float oneThirdCropHeight = mBorderBound.height() / 3;
    228 
    229         final float y1 = mBorderBound.top + oneThirdCropHeight;
    230         canvas.drawLine(mBorderBound.left, y1, mBorderBound.right, y1, mGuidelinePaint);
    231         final float y2 = mBorderBound.bottom - oneThirdCropHeight;
    232         canvas.drawLine(mBorderBound.left, y2, mBorderBound.right, y2, mGuidelinePaint);
    233     }
    234 
    235     private void onActionDown(float x, float y) {
    236 
    237     }
    238 
    239     private void onActionMove(float x, float y) {
    240         float deltaX = x - mLastPoint.x;
    241         float deltaY = y - mLastPoint.y;
    242         // 这里先不考虑裁剪框放最大的情况
    243         switch (touchPos) {
    244         case POS_CENTER:
    245             mBorderBound.left += deltaX;
    246             // fix border position
    247             if (mBorderBound.left < mBmpBound.left)
    248                 mBorderBound.left = mBmpBound.left;
    249             if (mBorderBound.left > mBmpBound.right - mBorderWidth)
    250                 mBorderBound.left = mBmpBound.right - mBorderWidth;
    251 
    252             mBorderBound.top += deltaY;
    253             if (mBorderBound.top < mBmpBound.top)
    254                 mBorderBound.top = mBmpBound.top;
    255 
    256             if (mBorderBound.top > mBmpBound.bottom - mBorderHeight)
    257                 mBorderBound.top = mBmpBound.bottom - mBorderHeight;
    258 
    259             mBorderBound.right = mBorderBound.left + mBorderWidth;
    260             mBorderBound.bottom = mBorderBound.top + mBorderHeight;
    261 
    262             break;
    263 
    264         case POS_TOP:
    265             resetTop(deltaY);
    266             break;
    267         case POS_BOTTOM:
    268             resetBottom(deltaY);
    269             break;
    270         case POS_LEFT:
    271             resetLeft(deltaX);
    272             break;
    273         case POS_RIGHT:
    274             resetRight(deltaX);
    275             break;
    276         case POS_TOP_LEFT:
    277             resetTop(deltaY);
    278             resetLeft(deltaX);
    279             break;
    280         case POS_TOP_RIGHT:
    281             resetTop(deltaY);
    282             resetRight(deltaX);
    283             break;
    284         case POS_BOTTOM_LEFT:
    285             resetBottom(deltaY);
    286             resetLeft(deltaX);
    287             break;
    288         case POS_BOTTOM_RIGHT:
    289             resetBottom(deltaY);
    290             resetRight(deltaX);
    291             break;
    292         default:
    293 
    294             break;
    295         }
    296         invalidate();
    297     }
    298 
    299     private void onActionUp(float x, float y) {
    300 
    301     }
    302 
    303     private int detectTouchPosition(float x, float y) {
    304         if (x > mBorderBound.left + TOUCH_FIELD && x < mBorderBound.right - TOUCH_FIELD
    305                 && y > mBorderBound.top + TOUCH_FIELD && y < mBorderBound.bottom - TOUCH_FIELD)
    306             return POS_CENTER;
    307 
    308         if (x > mBorderBound.left + BORDER_CORNER_LENGTH && x < mBorderBound.right - BORDER_CORNER_LENGTH) {
    309             if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + TOUCH_FIELD)
    310                 return POS_TOP;
    311             if (y > mBorderBound.bottom - TOUCH_FIELD && y < mBorderBound.bottom + TOUCH_FIELD)
    312                 return POS_BOTTOM;
    313         }
    314 
    315         if (y > mBorderBound.top + BORDER_CORNER_LENGTH && y < mBorderBound.bottom - BORDER_CORNER_LENGTH) {
    316             if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + TOUCH_FIELD)
    317                 return POS_LEFT;
    318             if (x > mBorderBound.right - TOUCH_FIELD && x < mBorderBound.right + TOUCH_FIELD)
    319                 return POS_RIGHT;
    320         }
    321 
    322         // 前面的逻辑已经排除掉了几种情况 所以后面的 ┏ ┓ ┗ ┛ 边角就按照所占区域的方形来判断就可以了
    323         if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + BORDER_CORNER_LENGTH) {
    324             if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)
    325                 return POS_TOP_LEFT;
    326             if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)
    327                 return POS_BOTTOM_LEFT;
    328         }
    329 
    330         if (x > mBorderBound.right - BORDER_CORNER_LENGTH && x < mBorderBound.right + TOUCH_FIELD) {
    331             if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)
    332                 return POS_TOP_RIGHT;
    333             if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)
    334                 return POS_BOTTOM_RIGHT;
    335         }
    336 
    337         return -1;
    338     }
    339 
    340     private void setLastPosition(MotionEvent event) {
    341         mLastPoint.x = event.getX();
    342         mLastPoint.y = event.getY();
    343     }
    344 
    345     private void getBorderEdgeLength() {
    346         mBorderWidth = mBorderBound.width();
    347         mBorderHeight = mBorderBound.height();
    348     }
    349 
    350     private void getBorderEdgeWidth() {
    351         mBorderWidth = mBorderBound.width();
    352     }
    353 
    354     private void getBorderEdgeHeight() {
    355         mBorderHeight = mBorderBound.height();
    356     }
    357 
    358     private void resetLeft(float delta) {
    359         mBorderBound.left += delta;
    360 
    361         getBorderEdgeWidth();
    362         fixBorderLeft();
    363     }
    364 
    365     private void resetTop(float delta) {
    366         mBorderBound.top += delta;
    367         getBorderEdgeHeight();
    368         fixBorderTop();
    369     }
    370 
    371     private void resetRight(float delta) {
    372         mBorderBound.right += delta;
    373 
    374         getBorderEdgeWidth();
    375         fixBorderRight();
    376 
    377     }
    378 
    379     private void resetBottom(float delta) {
    380         mBorderBound.bottom += delta;
    381 
    382         getBorderEdgeHeight();
    383         fixBorderBottom();
    384     }
    385 
    386     private void fixBorderLeft() {
    387         // fix left
    388         if (mBorderBound.left < mBmpBound.left)
    389             mBorderBound.left = mBmpBound.left;
    390         if (mBorderWidth < 2 * BORDER_CORNER_LENGTH)
    391             mBorderBound.left = mBorderBound.right - 2 * BORDER_CORNER_LENGTH;
    392     }
    393 
    394     private void fixBorderTop() {
    395         // fix top
    396         if (mBorderBound.top < mBmpBound.top)
    397             mBorderBound.top = mBmpBound.top;
    398         if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)
    399             mBorderBound.top = mBorderBound.bottom - 2 * BORDER_CORNER_LENGTH;
    400     }
    401 
    402     private void fixBorderRight() {
    403         // fix right
    404         if (mBorderBound.right > mBmpBound.right)
    405             mBorderBound.right = mBmpBound.right;
    406         if (mBorderWidth < 2 * BORDER_CORNER_LENGTH)
    407             mBorderBound.right = mBorderBound.left + 2 * BORDER_CORNER_LENGTH;
    408     }
    409 
    410     private void fixBorderBottom() {
    411         // fix bottom
    412         if (mBorderBound.bottom > mBmpBound.bottom)
    413             mBorderBound.bottom = mBmpBound.bottom;
    414         if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)
    415             mBorderBound.bottom = mBorderBound.top + 2 * BORDER_CORNER_LENGTH;
    416     }
    417 }

    补两张截图先:

    界面神丑,无所谓啦,功能是有了  代码其实很简单了,一看就懂,但是有一些知识点,比如说下面这两段:

    1 sv.setOnTouchListener(new View.OnTouchListener() {
    2 
    3             @Override
    4             public boolean onTouch(View v, MotionEvent event) {
    5                 // TODO Auto-generated method stub
    6                 myCropView.getParent().requestDisallowInterceptTouchEvent(false);
    7                 return false;
    8             }
    9         });
    1 case MotionEvent.ACTION_DOWN:
    2             setLastPosition(event);
    3             getParent().requestDisallowInterceptTouchEvent(true);

    这个代码就是为了让  移动或变化裁剪窗口  和  scrollview不冲突 

    具体内容会在后面更新博客~~  文章入口   (其实想写好多东西。。就是没空。。慢慢来)

  • 相关阅读:
    开博说两句
    学习总结 (持续更新)
    ip代理 120203
    [vs2005]关于预编绎网站的问题[已预编译此应用程序的错误]
    JAVA类基础
    集合类和泛型
    IO流——字符流
    多线程和包
    多态和内部类
    抽象类与接口
  • 原文地址:https://www.cnblogs.com/rossoneri/p/3988405.html
Copyright © 2020-2023  润新知