1. 程式人生 > >CropImageView android上的一個圖片裁剪控制元件

CropImageView android上的一個圖片裁剪控制元件

CropImageView

文前:本文非常容易讓讀者看的雲裡霧裡,建議直接看效果圖,覺得有用就去看原始碼吧。

CropImageView的原型來自Cropimage_demo,是android上的一個圖片裁剪控制元件。

原作者的部落格Android 自定義控制元件——圖片剪裁,如果讀者想要有更詳細的瞭解,請轉至原作者部落格。

之所以做這個控制元件是因為前段時間寫了一個截圖應用需要用到裁剪功能,現在把裁剪的控制元件單獨拿出來寫一個demo。但是說實話我並沒有完全理解原始碼,希望有透徹瞭解的讀者還是去看原作者的部落格吧。

程式碼不多,用到的兩個類加一起500行左右。

效果展示

gif效果展示:

特點

其實是相較於原版的特點。有兩個主要的改進:

  1. 裁剪框不會超出控制元件邊界
    無論是整體移動裁剪框還是移動四角,提升使用者體驗。
  2. 控制元件自適應圖片大小
    圖片超過螢幕尺寸則縮放圖片,否則縮放控制元件適應圖片大小,這樣可以避免裁剪框剪下到圖片以外的內容。

具體實現

控制元件適應圖片

因為我們需要這個控制元件居中顯示,而且控制元件必須和載入的圖片一樣大(否則裁剪框超出圖片的話會截到黑邊),所以在繪製這個控制元件的時候要測量圖片大小根據大小調整控制元件大小。

重寫onMeasure()方法

    @Override
    protected void
onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (mBitmap != null) { if ((mBitmap.getHeight() > heightSize) && (mBitmap.getHeight() > mBitmap.getWidth())) { widthSize = heightSize * mBitmap.getWidth() / mBitmap.getHeight(); } else
if ((mBitmap.getWidth() > widthSize) && (mBitmap.getWidth() > mBitmap.getHeight())) { heightSize = widthSize * mBitmap.getHeight() / mBitmap.getWidth(); } else { heightSize = mBitmap.getHeight(); widthSize = mBitmap.getWidth(); } } setMeasuredDimension(widthSize, heightSize); }

調整邊界

調整邊界我其實有三個思路來調整,但寫下來的三種實現方法都不完美。

介紹三種思路之前先解釋一下“拖動時調整邊界”和“繪圖時邊界”,“拖動時調整邊界”就是在onTouchEvent()內調整邊界,“繪圖時調整邊界”是在onTouchEvent()不做限制,在onDraw()的時候再調整。

拖動時調整邊界 一

當手指座標達到佈局邊界超出圖片座標(即超過承載著圖片的CropImageView的範圍)後,停止裁剪框邊界座標變化響應。

public boolean onTouchEvent(MotionEvent event) {                
  ...
    case MotionEvent.ACTION_MOVE:
  ...
        if (event.getX() > getWidth() || event.getX() < 0 || event.getY() > getHeight() || event.getY() < 0) {
            break;
        }

但是這個有個小問題,實際操作中發現會產生一個移動四個角過快的時候不容易移到到邊界的現象,慢慢操作還是可以移動到CropImageView的邊界的,但操作不友好。

拖動時調整邊界 二

以上邊界為例,當拖動上邊兩個角超出上邊界的時候,設定裁剪框的上邊界為CropImageView的上邊界。其他三邊依次類推。注 裁剪框的右邊界座標為CropImageView的getWidth()。

if (mDrawableFloat.top < 0) {
    mDrawableFloat.top = 0;
    break;
}

但是請思考,如果使用者是在裁剪框內拖動這個框呢?

這時候你不能光判斷裁剪框的邊界在哪,你還得思考使用者滑動的方向(dx,dy是滑動便宜量)。

        case EDGE_MOVE_IN:
            // 因為手指一直在移動,應該實時判斷是否超出裁剪框(手指移動到圖片範圍外)
            isTouchInSquare = mDrawableFloat.contains((int) event.getX(), (int) event.getY());
            if (isTouchInSquare) {
                if (mDrawableFloat.left == 0 && dx < 0
                        ||mDrawableFloat.top == 0 && dy < 0
                        ||mDrawableFloat.right == getWidth() && dx > 0
                        ||mDrawableFloat.bottom == getHeight() && dy > 0) {
                    break;
                }
                mDrawableFloat.offset(dx, dy);
            }
            break;

實測效果 拖動裁剪框的時候有些不跟手,可能跟程式碼執行效率有關?依然操作不友好。

繪圖時調整邊界

這個就不在onTouchEvent()搞事情了,你隨便拖動,拖出去算我輸。但是拖動的過程也伴隨著實時繪製的過程,在onDraw()裡面調整也是調整裁剪框的邊界了。

這裡要區分是整體拖動還是四角拖動,看到程式碼吧

    protected void configureBounds() {
        if () {
            ...
        } else if (getTouch((int) mX_1, (int) mY_1) == EDGE_MOVE_IN) {
            if (mDrawableFloat.left < 0) {
                mDrawableFloat.right = mDrawableFloat.width();
                mDrawableFloat.left = 0;
            }
            if (mDrawableFloat.top < 0) {
                mDrawableFloat.bottom = mDrawableFloat.height();
                mDrawableFloat.top = 0;
            }
            if (mDrawableFloat.right > getWidth()) {
                mDrawableFloat.left = getWidth() - mDrawableFloat.width();
                mDrawableFloat.right = getWidth();
            }
            if (mDrawableFloat.bottom > getHeight()) {
                mDrawableFloat.top = getHeight() - mDrawableFloat.height();
                mDrawableFloat.bottom = getHeight();
            }
            mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.right, mDrawableFloat.bottom);
        } else {
            if (mDrawableFloat.left < 0) {
                mDrawableFloat.left = 0;
            }
            if (mDrawableFloat.top < 0) {
                mDrawableFloat.top = 0;
            }
            if (mDrawableFloat.right > getWidth()) {
                mDrawableFloat.right = getWidth();
                mDrawableFloat.left = getWidth() - mDrawableFloat.width();
            }
            if (mDrawableFloat.bottom > getHeight()) {
                mDrawableFloat.bottom = getHeight();
                mDrawableFloat.top = getHeight() - mDrawableFloat.height();
            }
            mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.right, mDrawableFloat.bottom);
        }

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

使用方法

使用超簡單

佈局檔案中新增控制元件

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/translucent">

        <com.hcz017.cropimage.CropImageView
            android:id="@+id/cropimage"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:visibility="gone"/>
    </FrameLayout>

PS: 因為我們在java程式碼中設定控制元件大小隨載入的圖片變化,所以這裡的layout_width和layout_width屬性值意義不大。

提供的介面

裁剪控制元件嘛,兩個主要方法,設定圖片進去,返回裁減後的圖片(bitmap)。

mCropImageView.setDrawable(bitmap, 200, 300);
mCropImageView.getCropImage();

感謝看到這裡的大家~