天天看點

Android 自定義View可拖動移動位置及邊緣拉伸放大縮

首先說一下定義這樣一個View有什麼用?在一些app中,需要設定頭像,而使用者選擇的圖檔可能是使用攝像頭拍攝,也可能是選擇的相冊裡面的圖檔,總之,這樣的圖檔大小不一,就比如在使用某個聊天軟體的時候,設定頭像,需要對圖檔進行截取.

要實作這樣一個功能,首先,需要分析使用者的操作,即使用者所點選的View的位置,如下圖,我把View分為9個區域,

  • 當ACTION_DOWN時如果坐标為1.2.3.4四個區域,則對View進行相應的左上/右上/左下/右下拉伸;
  • 當ACTION_DOWN時如果坐标為5.6.7.8四個區域,則分别對上/右/下/左四個方向進行拉伸; 
  • 當ACTION_DOWN時如果坐标為9這個區域,則對View進行移動;
Android 自定義View可拖動移動位置及邊緣拉伸放大縮

理論分析完成,下面來看具體實作;

在下面的類中,有五個方法center/left/top/bottom/right分别對應移動/向左拉伸/向上拉伸/向下拉伸/向右拉伸,當Action_down為1-4所在的區域時,組合前面的對應的兩個拉伸方法即可,如左上角拉伸則對應執行left+top方法,這也是把四個單獨一條邊的邊緣拉伸獨立出來的原因;

在View中,我設定了View的最小寬度和高度,都是200,是以當使用者點選邊緣進行縮小操作時,能縮小的最小值也就是200;分别在left/top/bottom/right中展現;

public class DragScaleView extends View implements OnTouchListener {

    protected int screenWidth;

    protected int screenHeight;

    protected int lastX;

    protected int lastY;

    private int oriLeft;

    private int oriRight;

    private int oriTop;

    private int oriBottom;

    private int dragDirection;

    private static final int TOP = 0x15;

    private static final int LEFT = 0x16;

    private static final int BOTTOM = 0x17;

    private static final int RIGHT = 0x18;

    private static final int LEFT_TOP = 0x11;

    private static final int RIGHT_TOP = 0x12;

    private static final int LEFT_BOTTOM = 0x13;

    private static final int RIGHT_BOTTOM = 0x14;

    private static final int CENTER = 0x19;

    private int offset = 20;

    protected Paint paint = new Paint();

    protected void initScreenW_H() {

        screenHeight = getResources().getDisplayMetrics().heightPixels - 40;

        screenWidth = getResources().getDisplayMetrics().widthPixels;

    }

    public DragScaleView(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

        setOnTouchListener(this);

        initScreenW_H();

    }

    public DragScaleView(Context context, AttributeSet attrs) {

        super(context, attrs);

        setOnTouchListener(this);

        initScreenW_H();

    }

    public DragScaleView(Context context) {

        super(context);

        setOnTouchListener(this);

        initScreenW_H();

    }

    @Override

    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);

        paint.setColor(Color.RED);

        paint.setStrokeWidth(4.0f);

        paint.setStyle(Style.STROKE);

        canvas.drawRect(offset, offset, getWidth() - offset, getHeight()

                - offset, paint);

    }

    @Override

    public boolean onTouch(View v, MotionEvent event) {

        int action = event.getAction();

        if (action == MotionEvent.ACTION_DOWN) {

            oriLeft = v.getLeft();

            oriRight = v.getRight();

            oriTop = v.getTop();

            oriBottom = v.getBottom();

            lastY = (int) event.getRawY();

            lastX = (int) event.getRawX();

            dragDirection = getDirection(v, (int) event.getX(),

                    (int) event.getY());

        }

        // 處理拖動事件

        delDrag(v, event, action);

        invalidate();

        return false;

    }

    protected void delDrag(View v, MotionEvent event, int action) {

        switch (action) {

        case MotionEvent.ACTION_MOVE:

            int dx = (int) event.getRawX() - lastX;

            int dy = (int) event.getRawY() - lastY;

            switch (dragDirection) {

            case LEFT: // 左邊緣

                left(v, dx);

                break;

            case RIGHT: // 右邊緣

                right(v, dx);

                break;

            case BOTTOM: // 下邊緣

                bottom(v, dy);

                break;

            case TOP: // 上邊緣

                top(v, dy);

                break;

            case CENTER: // 點選中心–>>移動

                center(v, dx, dy);

                break;

            case LEFT_BOTTOM: // 左下

                left(v, dx);

                bottom(v, dy);

                break;

            case LEFT_TOP: // 左上

                left(v, dx);

                top(v, dy);

                break;

            case RIGHT_BOTTOM: // 右下

                right(v, dx);

                bottom(v, dy);

                break;

            case RIGHT_TOP: // 右上

                right(v, dx);

                top(v, dy);

                break;

            }

            if (dragDirection != CENTER) {

                v.layout(oriLeft, oriTop, oriRight, oriBottom);

            }

            lastX = (int) event.getRawX();

            lastY = (int) event.getRawY();

            break;

        case MotionEvent.ACTION_UP:

            dragDirection = 0;

            break;

        }

    }

    private void center(View v, int dx, int dy) {

        int left = v.getLeft() + dx;

        int top = v.getTop() + dy;

        int right = v.getRight() + dx;

        int bottom = v.getBottom() + dy;

        if (left < -offset) {

            left = -offset;

            right = left + v.getWidth();

        }

        if (right > screenWidth + offset) {

            right = screenWidth + offset;

            left = right - v.getWidth();

        }

        if (top < -offset) {

            top = -offset;

            bottom = top + v.getHeight();

        }

        if (bottom > screenHeight + offset) {

            bottom = screenHeight + offset;

            top = bottom - v.getHeight();

        }

        v.layout(left, top, right, bottom);

    }

    private void top(View v, int dy) {

        oriTop += dy;

        if (oriTop < -offset) {

            oriTop = -offset;

        }

        if (oriBottom - oriTop - 2 * offset < 200) {

            oriTop = oriBottom - 2 * offset - 200;

        }

    }

    private void bottom(View v, int dy) {

        oriBottom += dy;

        if (oriBottom > screenHeight + offset) {

            oriBottom = screenHeight + offset;

        }

        if (oriBottom - oriTop - 2 * offset < 200) {

            oriBottom = 200 + oriTop + 2 * offset;

        }

    }

    private void right(View v, int dx) {

        oriRight += dx;

        if (oriRight > screenWidth + offset) {

            oriRight = screenWidth + offset;

        }

        if (oriRight - oriLeft - 2 * offset < 200) {

            oriRight = oriLeft + 2 * offset + 200;

        }

    }

    private void left(View v, int dx) {

        oriLeft += dx;

        if (oriLeft < -offset) {

            oriLeft = -offset;

        }

        if (oriRight - oriLeft - 2 * offset < 200) {

            oriLeft = oriRight - 2 * offset - 200;

        }

    }

    protected int getDirection(View v, int x, int y) {

        int left = v.getLeft();

        int right = v.getRight();

        int bottom = v.getBottom();

        int top = v.getTop();

        if (x < 40 && y < 40) {

            return LEFT_TOP;

        }

        if (y < 40 && right - left - x < 40) {

            return RIGHT_TOP;

        }

        if (x < 40 && bottom - top - y < 40) {

            return LEFT_BOTTOM;

        }

        if (right - left - x < 40 && bottom - top - y < 40) {

            return RIGHT_BOTTOM;

        }

        if (x < 40) {

            return LEFT;

        }

        if (y < 40) {

            return TOP;

        }

        if (right - left - x < 40) {

            return RIGHT;

        }

        if (bottom - top - y < 40) {

            return BOTTOM;

        }

        return CENTER;

    }

    public int getCutWidth() {

        return getWidth() - 2 * offset;

    }

    public int getCutHeight() {

        return getHeight() - 2 * offset;

    }

}

二.使用View,如果想要對View進行移動,需要在xml中配置android:clickable=”true”屬性;

        android:id=”@+id/ds”

        android:layout_width=”180dip”

        android:layout_height=”180dip”

        android:clickable=”true” />