一.首先說一下定義這樣一個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表示觸摸點距離手機屏幕左側的距離;