package net.oschina.app.widget; import net.oschina.app.AppConfig; import net.oschina.app.R; import net.oschina.app.common.UIHelper; import net.oschina.app.ui.BaseActivity; import android.annotation.SuppressLint; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.os.Build; import android.util.Log; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; /** * * 实现截图功能的View * * @author yeguozhong@yeah.net * */ public class ScreenShotView extends View implements OnTouchListener { private Paint mGrayPaint; private Rect topRect; private Rect rightRect; private Rect bottomRect; private Rect leftRect; private int screenWidth; private int screeenHeight; private OnScreenShotListener mListener; private BaseActivity mContext; // 截图内框的坐标 private int top, right, bottom, left; // 辅助变量,用于暂时保存内框坐标 private int tmpLeft, tmpTop, tmpRight, tmpBottom; private int innerWidth, innerHeight; // 触摸屏幕前后坐标 private int downX, downY, moveX, moveY; private final static int TOUCH_SELECT = 1; // 选择标识 private final static int TOUCH_MOVE = 2; // 移动选区 private int touchFlag = TOUCH_SELECT; private boolean isInMoveSelection; // 是否在移动选区内 private boolean isInTopPullArea; // 上拉调整大小 private boolean isInRightPullArea; // 右拉调整大小 private boolean isInBottomPullArea; private boolean isInLeftPullArea; private int innnerClickCount = 0; // 选框内双击中的有效点击次数 private int outterClickCount = 0; // 选框外双击的有效点击次数 private long timeInterval; // 触碰事件间隔 private long firstClickTime; // 第一次单击时刻 private long secondClickTime; // 第二次单击时刻 private long TouchDownTime; // 刚触碰的时刻 private long TouchUpTime; // 触碰结束时刻 private final static long VALID_CLICK_INTERVAL = 500; // 触摸down,up一下500毫秒以内为一次单击 private final static long VALID_DOUBLE_CLICK_INTERNAL = 1000;// 两次单击之间时间距离 private final static int VALID_PULL_DISTANCE = 30; // 有效拉大拉小距离 private boolean isTimeToTip = false; private boolean isHide = false; public final static String TEMP_SHARE_FILE_NAME = AppConfig.DEFAULT_SAVE_IMAGE_PATH + "_share_tmp.jpg"; private static Bitmap bmDoubleClickTip; /** * 监听截图完成 */ public interface OnScreenShotListener { public void onComplete(Bitmap bm); } @SuppressLint("NewApi") public ScreenShotView(BaseActivity context, OnScreenShotListener listener) { super(context); mListener = listener; mContext = context; mContext.setAllowDestroy(false, this); Point p = getDisplaySize(context.getWindowManager().getDefaultDisplay()); screenWidth = p.x; screeenHeight = p.y; mGrayPaint = new Paint(); mGrayPaint.setARGB(100, 0, 0, 0); topRect = new Rect(); rightRect = new Rect(); bottomRect = new Rect(); leftRect = new Rect(); topRect.set(0, 0, screenWidth, screeenHeight); setOnTouchListener(this); if(bmDoubleClickTip==null){ bmDoubleClickTip = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable._pointer)); } UIHelper.ToastMessage(mContext, "请滑动手指确定选区!"); } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawRect(topRect, mGrayPaint); canvas.drawRect(rightRect, mGrayPaint); canvas.drawRect(bottomRect, mGrayPaint); canvas.drawRect(leftRect, mGrayPaint); if (isTimeToTip) { // 在中间位置出现提示 innerWidth = right - left; innerHeight = bottom - top; int bmWidth = bmDoubleClickTip.getWidth(); int bmHeight = bmDoubleClickTip.getHeight(); int x = (int) (left + (innerWidth - bmWidth) * 0.5); int y = (int) (top + (innerHeight - bmHeight) * 0.5); canvas.drawBitmap(bmDoubleClickTip, x, y, null); isTimeToTip = false; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: isTimeToTip = false; downX = (int) event.getX(); downY = (int) event.getY(); isInMoveSelection = isInSeletion(downX, downY); isInTopPullArea = isInTopPullArea(downX, downY); isInRightPullArea = isInRightPullArea(downX, downY); isInBottomPullArea = isInBottomPullArea(downX, downY); isInLeftPullArea = isInLeftPullArea(downX, downY); TouchDownTime = System.currentTimeMillis(); break; case MotionEvent.ACTION_MOVE: isTimeToTip = false; moveX = (int) event.getX(); moveY = (int) event.getY(); if (touchFlag == TOUCH_SELECT) { tmpLeft = left = getMin(downX, moveX); tmpTop = top = getMin(downY, moveY); tmpRight = right = getMax(downX, moveX); tmpBottom = bottom = getMax(downY, moveY); isInMoveSelection = false; } else if (touchFlag == TOUCH_MOVE && isInMoveSelection) { int xDistance = moveX - downX; int yDistance = moveY - downY; decideCoordinate(xDistance, yDistance); } else if (touchFlag == TOUCH_MOVE && isInTopPullArea) { int yDistance = downY - moveY; int extremeY = (bottom - 2 * VALID_PULL_DISTANCE); top = (tmpTop - yDistance) < extremeY ? (tmpTop - yDistance) : extremeY; } else if (touchFlag == TOUCH_MOVE && isInRightPullArea) { int xDistance = moveX - downX; int extremeX = (left + 2 * VALID_PULL_DISTANCE); right = (tmpRight + xDistance) > extremeX ? (tmpRight + xDistance) : extremeX; } else if (touchFlag == TOUCH_MOVE && isInBottomPullArea) { int yDistance = downY - moveY; int extremeY = (top + 2 * VALID_PULL_DISTANCE); bottom = (tmpBottom - yDistance) > extremeY ? (tmpBottom - yDistance) : extremeY; } else if (touchFlag == TOUCH_MOVE && isInLeftPullArea) { int xDistance = downX - moveX; int extremeX = (right - 2 * VALID_PULL_DISTANCE); left = (tmpLeft - xDistance) < extremeX ? (tmpLeft - xDistance) : extremeX; } setInnerBorder(left, top, right, bottom); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (touchFlag == TOUCH_SELECT) { touchFlag = TOUCH_MOVE; } else if (touchFlag == TOUCH_MOVE) { tmpLeft = left; tmpTop = top; tmpRight = right; tmpBottom = bottom; } if (isShouldShowTip() && isTimeToTip == false) { isTimeToTip = true; invalidate(); } TouchUpTime = System.currentTimeMillis(); timeInterval = TouchUpTime - TouchDownTime; // 选区内的双击则为截图,选区外的双击则为取消截图 if (timeInterval < VALID_CLICK_INTERVAL && isInMoveSelection) { innnerClickCount = innnerClickCount + 1; if (innnerClickCount == 1) { firstClickTime = System.currentTimeMillis(); } else if (innnerClickCount == 2) { secondClickTime = System.currentTimeMillis(); if ((secondClickTime - firstClickTime) < VALID_DOUBLE_CLICK_INTERNAL) { isTimeToTip = false; mListener.onComplete(getCutImage()); dismiss(); } innnerClickCount = 0; } } else if (timeInterval < VALID_CLICK_INTERVAL && !isInMoveSelection) { outterClickCount = outterClickCount + 1; if (outterClickCount == 1) { firstClickTime = System.currentTimeMillis(); } else if (outterClickCount == 2) { secondClickTime = System.currentTimeMillis(); if ((secondClickTime - firstClickTime) < VALID_DOUBLE_CLICK_INTERNAL) { isTimeToTip = false; dismiss(); } outterClickCount = 0; } } break; default: break; } return true; } private int getMin(int x, int y) { return x > y ? y : x; } private int getMax(int x, int y) { return x > y ? x : y; } /** * 是否在左拉动选区 * * @return */ private boolean isInLeftPullArea(int x, int y) { if (((left - VALID_PULL_DISTANCE) <= x && x < (left + VALID_PULL_DISTANCE)) && ((top + VALID_PULL_DISTANCE) < y && y < (bottom - VALID_PULL_DISTANCE))) { return true; } return false; } /** * 是否在右拉动选区 * * @return */ private boolean isInRightPullArea(int x, int y) { if (((right - VALID_PULL_DISTANCE) <= x && x < (right + VALID_PULL_DISTANCE)) && ((top + VALID_PULL_DISTANCE) < y && y < (bottom - VALID_PULL_DISTANCE))) { return true; } return false; } /** * 是否在上拉动选区 * * @return */ private boolean isInTopPullArea(int x, int y) { if (((left + VALID_PULL_DISTANCE) <= x && x < (right - VALID_PULL_DISTANCE)) && ((top - VALID_PULL_DISTANCE) < y && y < (top + VALID_PULL_DISTANCE))) { return true; } return false; } /** * 是否在下拉动选区 * * @return */ private boolean isInBottomPullArea(int x, int y) { if (((left + VALID_PULL_DISTANCE) <= x && x < (right - VALID_PULL_DISTANCE)) && ((bottom - VALID_PULL_DISTANCE) < y && y < (bottom + VALID_PULL_DISTANCE))) { return true; } return false; } /** * 判断触碰的点有没有在移动选区内 * * @param x * @param y * @return */ private boolean isInSeletion(int x, int y) { if ((left != 0 || right != 0 || top != 0 || bottom != 0) && ((left + VALID_PULL_DISTANCE) <= x && x <= (right - VALID_PULL_DISTANCE)) && ((top + VALID_PULL_DISTANCE) <= y && y <= (bottom - VALID_PULL_DISTANCE))) { return true; } return false; } /** * 判断是否应该出现提示 * * @return */ private boolean isShouldShowTip() { if ((right - left) > 100 && (bottom - top) > 100) { return true; } return false; } /** * 决定内层选框的坐标 * * @param xDistance * @param yDistance */ private void decideCoordinate(int xDistance, int yDistance) { innerWidth = right - left; innerHeight = bottom - top; // 决定左右坐标 if ((tmpLeft + xDistance) < 0) { right = innerWidth; left = 0; } else if ((tmpRight + xDistance) > screenWidth) { left = screenWidth - innerWidth; right = screenWidth; } else { left = tmpLeft + xDistance; right = tmpRight + xDistance; } // 决定上下坐标 if ((tmpTop + yDistance) < 0) { bottom = innerHeight; top = 0; } else if ((tmpBottom + yDistance) > screeenHeight) { top = screeenHeight - innerHeight; bottom = screeenHeight; } else { top = tmpTop + yDistance; bottom = tmpBottom + yDistance; } } /** * 设置 内层的边框坐标 */ private void setInnerBorder(int left, int top, int right, int bottom) { Log.i("com.example", "left:" + left + ",top:" + top + ",right:" + right + ",bottom:" + bottom); topRect.set(0, 0, screenWidth, top); rightRect.set(right, top, screenWidth, bottom); bottomRect.set(0, bottom, screenWidth, screeenHeight); leftRect.set(0, top, left, bottom); this.invalidate(); } /** * 截取内层边框中的View. */ private Bitmap getCutImage() { View view = mContext.getWindow().getDecorView(); Bitmap bmp = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bmp); view.draw(canvas); return imageCrop(bmp); } /** * 裁剪图片 */ private Bitmap imageCrop(Bitmap bitmap) { int x = left<0?0:left; int y = top + getStatusHeight(); int width = right- left; int height = bottom - top; if((width+x)>bitmap.getWidth()){ width = bitmap.getWidth()-x; } if((y+height)>bitmap.getHeight()){ height = bitmap.getHeight()-y; } return Bitmap.createBitmap(bitmap,x,y,width,height , null, false); } /** * 返回状态栏的高度 * * @return */ private int getStatusHeight() { Rect frame = new Rect(); mContext.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); return frame.top; } /** * 注销该view */ private void dismiss() { isHide = true; mGrayPaint.setARGB(0, 0, 0, 0); setOnTouchListener(null); mContext.setAllowFullScreen(true); invalidate(); } /** * 获取不同设备的宽高 * @param display * @return */ @SuppressLint("NewApi") private static Point getDisplaySize(final Display display) { final Point point = new Point(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { display.getSize(point); } else { point.x = display.getWidth(); point.y = display.getHeight(); } return point; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && isHide == false) { mContext.setAllowDestroy(false); dismiss(); } else if ((keyCode == KeyEvent.KEYCODE_BACK && isHide == true)) { mContext.setAllowDestroy(true); } return false; } }