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


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

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

  • 當ACTION_DOWN時如果坐標為1.2.3.4四個區域,則對View進行相應的左上/右上/左下/右下拉伸;
  • 當ACTION_DOWN時如果坐標為5.6.7.8四個區域,則分別對上/右/下/左四個方向進行拉伸; 
  • 當ACTION_DOWN時如果坐標為9這個區域,則對View進行移動;

 

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

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

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

/**
 * @see http://www.cnblogs.com/a284628487/
 * @author Cj
 * 
 */
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;
    }

    /**
     * 處理拖動事件
     * 
     * @param v
     * @param event
     * @param action
     */
    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;
        }
    }

    /**
     * 觸摸點為中心->>移動
     * 
     * @param v
     * @param dx
     * @param dy
     */
    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);
    }

    /**
     * 觸摸點為上邊緣
     * 
     * @param v
     * @param dy
     */
    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;
        }
    }

    /**
     * 觸摸點為下邊緣
     * 
     * @param v
     * @param dy
     */
    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;
        }
    }

    /**
     * 觸摸點為右邊緣
     * 
     * @param v
     * @param dx
     */
    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;
        }
    }

    /**
     * 觸摸點為左邊緣
     * 
     * @param v
     * @param dx
     */
    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;
        }
    }

    /**
     * 獲取觸摸點flag
     * 
     * @param v
     * @param x
     * @param y
     * @return
     */
    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;
    }

    /**
     * 獲取截取寬度
     * 
     * @return
     */
    public int getCutWidth() {
        return getWidth() - 2 * offset;
    }

    /**
     * 獲取截取高度
     * 
     * @return
     */
    public int getCutHeight() {
        return getHeight() - 2 * offset;
    }
}

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

    <xxx.DragScaleView
        android:id="@+id/ds"
        android:layout_width="180dip"
        android:layout_height="180dip"
        android:clickable="true" />

  三.效果圖,右圖是拉伸后的效果
  

  四.關於MotionEvent.getX()和MotionEvent.getRawX()的區別

  getX表示觸摸點距離View的左邊緣的距離,而getRawX表示觸摸點距離手機屏幕左側的距離;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM