代码语言
.
CSharp
.
JS
Java
Asp.Net
C
MSSQL
PHP
Css
PLSQL
Python
Shell
EBS
ASP
Perl
ObjC
VB.Net
VBS
MYSQL
GO
Delphi
AS
DB2
Domino
Rails
ActionScript
Scala
代码分类
文件
系统
字符串
数据库
网络相关
图形/GUI
多媒体
算法
游戏
Jquery
Extjs
Android
HTML5
菜单
网页交互
WinForm
控件
企业应用
安全与加密
脚本/批处理
开放平台
其它
【
Java
】
手机相册拍照、相册选取照片的裁剪
作者:
依依
/ 发布于
2016/3/15
/
575
之前项目中处理照片的逻辑以及相关处理 可以直接对照片进行裁剪,支持被裁剪照片的放大缩小处理 因为时间问题,没有加选择拍照和选择相册的popWindow,使用单一功能直接在SampleActivity中button的onClick事件切换 可以解决在低端手机内存导致的图片无法裁剪的问题
package com.dingji.webpdemo; import android.content.Context; import android.util.DisplayMetrics; import android.util.FloatMath; import android.view.MotionEvent; import android.view.ViewConfiguration; /** * @author Almer Thie (code.almeros.com) * Copyright (c) 2013, Almer Thie (code.almeros.com) * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer * in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY * OF SUCH DAMAGE. */ public abstract class TwoFingerGestureDetector extends BaseGestureDetector { private final float mEdgeSlop; private float mRightSlopEdge; private float mBottomSlopEdge; protected float mPrevFingerDiffX; protected float mPrevFingerDiffY; protected float mCurrFingerDiffX; protected float mCurrFingerDiffY; private float mCurrLen; private float mPrevLen; public TwoFingerGestureDetector(Context context) { super(context); ViewConfiguration config = ViewConfiguration.get(context); mEdgeSlop = config.getScaledEdgeSlop(); } @Override protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event); @Override protected abstract void handleInProgressEvent(int actionCode, MotionEvent event); protected void updateStateByEvent(MotionEvent curr) { super.updateStateByEvent(curr); final MotionEvent prev = mPrevEvent; mCurrLen = -1; mPrevLen = -1; // Previous final float px0 = prev.getX(0); final float py0 = prev.getY(0); final float px1 = prev.getX(1); final float py1 = prev.getY(1); final float pvx = px1 - px0; final float pvy = py1 - py0; mPrevFingerDiffX = pvx; mPrevFingerDiffY = pvy; // Current final float cx0 = curr.getX(0); final float cy0 = curr.getY(0); final float cx1 = curr.getX(1); final float cy1 = curr.getY(1); final float cvx = cx1 - cx0; final float cvy = cy1 - cy0; mCurrFingerDiffX = cvx; mCurrFingerDiffY = cvy; } /** * Return the current distance between the two pointers forming the * gesture in progress. * * @return Distance between pointers in pixels. */ public float getCurrentSpan() { if (mCurrLen == -1) { final float cvx = mCurrFingerDiffX; final float cvy = mCurrFingerDiffY; mCurrLen = FloatMath.sqrt(cvx * cvx + cvy * cvy); } return mCurrLen; } /** * Return the previous distance between the two pointers forming the * gesture in progress. * * @return Previous distance between pointers in pixels. */ public float getPreviousSpan() { if (mPrevLen == -1) { final float pvx = mPrevFingerDiffX; final float pvy = mPrevFingerDiffY; mPrevLen = FloatMath.sqrt(pvx * pvx + pvy * pvy); } return mPrevLen; } /** * MotionEvent has no getRawX(int) method; simulate it pending future API approval. * * @param event * @param pointerIndex * @return */ protected static float getRawX(MotionEvent event, int pointerIndex) { float offset = event.getX() - event.getRawX(); if (pointerIndex < event.getPointerCount()) { return event.getX(pointerIndex) + offset; } return 0f; } /** * MotionEvent has no getRawY(int) method; simulate it pending future API approval. * * @param event * @param pointerIndex * @return */ protected static float getRawY(MotionEvent event, int pointerIndex) { float offset = event.getY() - event.getRawY(); if (pointerIndex < event.getPointerCount()) { return event.getY(pointerIndex) + offset; } return 0f; } /** * Check if we have a sloppy gesture. Sloppy gestures can happen if the edge * of the user's hand is touching the screen, for example. * * @param event * @return */ protected boolean isSloppyGesture(MotionEvent event) { // As orientation can change, query the metrics in touch down DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); mRightSlopEdge = metrics.widthPixels - mEdgeSlop; mBottomSlopEdge = metrics.heightPixels - mEdgeSlop; final float edgeSlop = mEdgeSlop; final float rightSlop = mRightSlopEdge; final float bottomSlop = mBottomSlopEdge; final float x0 = event.getRawX(); final float y0 = event.getRawY(); final float x1 = getRawX(event, 1); final float y1 = getRawY(event, 1); boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop || y0 > bottomSlop; boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlop || y1 > bottomSlop; if (p0sloppy && p1sloppy) { return true; } else if (p0sloppy) { return true; } else if (p1sloppy) { return true; } return false; } } ShoveGestureDetector.java package com.dingji.webpdemo; import android.content.Context; import android.view.MotionEvent; public class ShoveGestureDetector extends TwoFingerGestureDetector { /** * Listener which must be implemented which is used by ShoveGestureDetector * to perform callbacks to any implementing class which is registered to a * ShoveGestureDetector via the constructor. * * @see ShoveGestureDetector.SimpleOnShoveGestureListener */ public interface OnShoveGestureListener { public boolean onShove(ShoveGestureDetector detector); public boolean onShoveBegin(ShoveGestureDetector detector); public void onShoveEnd(ShoveGestureDetector detector); } /** * Helper class which may be extended and where the methods may be * implemented. This way it is not necessary to implement all methods * of OnShoveGestureListener. */ public static class SimpleOnShoveGestureListener implements OnShoveGestureListener { public boolean onShove(ShoveGestureDetector detector) { return false; } public boolean onShoveBegin(ShoveGestureDetector detector) { return true; } public void onShoveEnd(ShoveGestureDetector detector) { // Do nothing, overridden implementation may be used } } private float mPrevAverageY; private float mCurrAverageY; private final OnShoveGestureListener mListener; private boolean mSloppyGesture; public ShoveGestureDetector(Context context, OnShoveGestureListener listener) { super(context); mListener = listener; } @Override protected void handleStartProgressEvent(int actionCode, MotionEvent event) { switch (actionCode) { case MotionEvent.ACTION_POINTER_DOWN: // At least the second finger is on screen now resetState(); // In case we missed an UP/CANCEL event mPrevEvent = MotionEvent.obtain(event); mTimeDelta = 0; updateStateByEvent(event); // See if we have a sloppy gesture mSloppyGesture = isSloppyGesture(event); if (!mSloppyGesture) { // No, start gesture now mGestureInProgress = mListener.onShoveBegin(this); } break; case MotionEvent.ACTION_MOVE: if (!mSloppyGesture) { break; } // See if we still have a sloppy gesture mSloppyGesture = isSloppyGesture(event); if (!mSloppyGesture) { // No, start normal gesture now mGestureInProgress = mListener.onShoveBegin(this); } break; case MotionEvent.ACTION_POINTER_UP: if (!mSloppyGesture) { break; } break; } } @Override protected void handleInProgressEvent(int actionCode, MotionEvent event) { switch (actionCode) { case MotionEvent.ACTION_POINTER_UP: // Gesture ended but updateStateByEvent(event); if (!mSloppyGesture) { mListener.onShoveEnd(this); } resetState(); break; case MotionEvent.ACTION_CANCEL: if (!mSloppyGesture) { mListener.onShoveEnd(this); } resetState(); break; case MotionEvent.ACTION_MOVE: updateStateByEvent(event); // Only accept the event if our relative pressure is within // a certain limit. This can help filter shaky data as a // finger is lifted. Also check that shove is meaningful. if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD && Math.abs(getShovePixelsDelta()) > 0.5f) { final boolean updatePrevious = mListener.onShove(this); if (updatePrevious) { mPrevEvent.recycle(); mPrevEvent = MotionEvent.obtain(event); } } break; } } @Override protected void resetState() { super.resetState(); mSloppyGesture = false; mPrevAverageY = 0.0f; mCurrAverageY = 0.0f; } @Override protected void updateStateByEvent(MotionEvent curr) { super.updateStateByEvent(curr); final MotionEvent prev = mPrevEvent; float py0 = prev.getY(0); float py1 = prev.getY(1); mPrevAverageY = (py0 + py1) / 2.0f; float cy0 = curr.getY(0); float cy1 = curr.getY(1); mCurrAverageY = (cy0 + cy1) / 2.0f; } @Override protected boolean isSloppyGesture(MotionEvent event) { boolean sloppy = super.isSloppyGesture(event); if (sloppy) return true; // If it's not traditionally sloppy, we check if the angle between fingers // is acceptable. double angle = Math.abs(Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX)); // about 20 degrees, left or right return !((0.0f < angle && angle < 0.35f) || 2.79f < angle && angle < Math.PI); } /** * Return the distance in pixels from the previous shove event to the current * event. * * @return The current distance in pixels. */ public float getShovePixelsDelta() { return mCurrAverageY - mPrevAverageY; } } SampleActivity.java package com.dingji.webpdemo; import java.io.File; import android.app.Activity; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; public class SampleActivity extends Activity { private ImageView imageView; private Button button; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initViews(); } private void initViews() { imageView = (ImageView) findViewById(R.id.iv_webp); button = (Button) findViewById(R.id.btn_view_webp); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { choiceFromCamera(); // choiceFromAlbum(); } }); } public void choiceFromCamera() { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { Toast.makeText(getApplicationContext(), "没有储存卡", 0).show(); return; } Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); File out = new File(Environment.getExternalStorageDirectory(), "camera.jpg"); Uri uri = Uri.fromFile(out); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); try { startActivityForResult(intent, 716); } catch (Exception exception) { Toast.makeText(getApplicationContext(), "相机出错", 0).show(); } } public void choiceFromAlbum() { try { Intent localIntent = new Intent(Intent.ACTION_PICK); localIntent.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*"); startActivityForResult(localIntent, 714); } catch (Exception e) { Toast.makeText(getApplicationContext(), "相册不可用", 0).show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 716) { String fileName = Environment.getExternalStorageDirectory() + "/camera.jpg"; startCropImage(this, fileName, 320, 320); } else if (requestCode == 728) { if (data != null) { String path = data.getStringExtra(ClipPictureActivity.IMAGE_PATH); if (path == null) { return; } imageView.setImageBitmap(BitmapFactory.decodeFile(path)); } } else if (requestCode == 714) { if (data != null) { Uri uri = data.getData(); String filePath; if (uri.toString().startsWith("file")) { filePath = uri.getPath(); startCropImage(this, filePath, 320, 320); } else { ContentResolver resolver = this.getContentResolver(); Cursor cursor = resolver.query(uri, null, null, null, null); if (cursor != null) { cursor.moveToFirst(); filePath = cursor.getString(cursor.getColumnIndex("_data")); startCropImage(this, filePath, 320, 320); } } } } } /** * 选择过照片之后,开始编辑图片,可以放大缩小图片 * * @param instance * @param imagePath */ public static void startCropImage(Activity instance, String imagePath, int outputX, int outputY) { Intent intent = new Intent(instance, ClipPictureActivity.class); intent.putExtra(ClipPictureActivity.IMAGE_PATH, imagePath); intent.putExtra(ClipPictureActivity.ASPECT_X, 3); intent.putExtra(ClipPictureActivity.ASPECT_Y, 2); intent.putExtra(ClipPictureActivity.OUTPUT_X, outputX); intent.putExtra(ClipPictureActivity.OUTPUT_Y, outputY); instance.startActivityForResult(intent, 728); } } RotateGestureDetector.java package com.dingji.webpdemo; import android.content.Context; import android.view.MotionEvent; public class RotateGestureDetector extends TwoFingerGestureDetector { /** * Listener which must be implemented which is used by RotateGestureDetector * to perform callbacks to any implementing class which is registered to a * RotateGestureDetector via the constructor. * * @see RotateGestureDetector.SimpleOnRotateGestureListener */ public interface OnRotateGestureListener { public boolean onRotate(RotateGestureDetector detector); public boolean onRotateBegin(RotateGestureDetector detector); public void onRotateEnd(RotateGestureDetector detector); } /** * Helper class which may be extended and where the methods may be * implemented. This way it is not necessary to implement all methods * of OnRotateGestureListener. */ public static class SimpleOnRotateGestureListener implements OnRotateGestureListener { public boolean onRotate(RotateGestureDetector detector) { return false; } public boolean onRotateBegin(RotateGestureDetector detector) { return true; } public void onRotateEnd(RotateGestureDetector detector) { // Do nothing, overridden implementation may be used } } private final OnRotateGestureListener mListener; private boolean mSloppyGesture; public RotateGestureDetector(Context context, OnRotateGestureListener listener) { super(context); mListener = listener; } @Override protected void handleStartProgressEvent(int actionCode, MotionEvent event) { switch (actionCode) { case MotionEvent.ACTION_POINTER_DOWN: // At least the second finger is on screen now resetState(); // In case we missed an UP/CANCEL event mPrevEvent = MotionEvent.obtain(event); mTimeDelta = 0; updateStateByEvent(event); // See if we have a sloppy gesture mSloppyGesture = isSloppyGesture(event); if (!mSloppyGesture) { // No, start gesture now mGestureInProgress = mListener.onRotateBegin(this); } break; case MotionEvent.ACTION_MOVE: if (!mSloppyGesture) { break; } // See if we still have a sloppy gesture mSloppyGesture = isSloppyGesture(event); if (!mSloppyGesture) { // No, start normal gesture now mGestureInProgress = mListener.onRotateBegin(this); } break; case MotionEvent.ACTION_POINTER_UP: if (!mSloppyGesture) { break; } break; } } @Override protected void handleInProgressEvent(int actionCode, MotionEvent event) { switch (actionCode) { case MotionEvent.ACTION_POINTER_UP: // Gesture ended but updateStateByEvent(event); if (!mSloppyGesture) { mListener.onRotateEnd(this); } resetState(); break; case MotionEvent.ACTION_CANCEL: if (!mSloppyGesture) { mListener.onRotateEnd(this); } resetState(); break; case MotionEvent.ACTION_MOVE: updateStateByEvent(event); // Only accept the event if our relative pressure is within // a certain limit. This can help filter shaky data as a // finger is lifted. if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { final boolean updatePrevious = mListener.onRotate(this); if (updatePrevious) { mPrevEvent.recycle(); mPrevEvent = MotionEvent.obtain(event); } } break; } } @Override protected void resetState() { super.resetState(); mSloppyGesture = false; } /** * Return the rotation difference from the previous rotate event to the current * event. * * @return The current rotation //difference in degrees. */ public float getRotationDegreesDelta() { double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX) - Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX); return (float) (diffRadians * 180 / Math.PI); } } MoveGestureDetector.java package com.dingji.webpdemo; import android.content.Context; import android.graphics.PointF; import android.view.MotionEvent; public class MoveGestureDetector extends BaseGestureDetector { /** * Listener which must be implemented which is used by MoveGestureDetector * to perform callbacks to any implementing class which is registered to a * MoveGestureDetector via the constructor. * * @see MoveGestureDetector.SimpleOnMoveGestureListener */ public interface OnMoveGestureListener { public boolean onMove(MoveGestureDetector detector); public boolean onMoveBegin(MoveGestureDetector detector); public void onMoveEnd(MoveGestureDetector detector); } /** * Helper class which may be extended and where the methods may be * implemented. This way it is not necessary to implement all methods * of OnMoveGestureListener. */ public static class SimpleOnMoveGestureListener implements OnMoveGestureListener { public boolean onMove(MoveGestureDetector detector) { return false; } public boolean onMoveBegin(MoveGestureDetector detector) { return true; } public void onMoveEnd(MoveGestureDetector detector) { // Do nothing, overridden implementation may be used } } private static final PointF FOCUS_DELTA_ZERO = new PointF(); private final OnMoveGestureListener mListener; private PointF mCurrFocusInternal; private PointF mPrevFocusInternal; private PointF mFocusExternal = new PointF(); private PointF mFocusDeltaExternal = new PointF(); public MoveGestureDetector(Context context, OnMoveGestureListener listener) { super(context); mListener = listener; } @Override protected void handleStartProgressEvent(int actionCode, MotionEvent event) { switch (actionCode) { case MotionEvent.ACTION_DOWN: resetState(); // In case we missed an UP/CANCEL event mPrevEvent = MotionEvent.obtain(event); mTimeDelta = 0; updateStateByEvent(event); break; case MotionEvent.ACTION_MOVE: mGestureInProgress = mListener.onMoveBegin(this); break; } } @Override protected void handleInProgressEvent(int actionCode, MotionEvent event) { switch (actionCode) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mListener.onMoveEnd(this); resetState(); break; case MotionEvent.ACTION_MOVE: updateStateByEvent(event); // Only accept the event if our relative pressure is within // a certain limit. This can help filter shaky data as a // finger is lifted. if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { final boolean updatePrevious = mListener.onMove(this); if (updatePrevious) { mPrevEvent.recycle(); mPrevEvent = MotionEvent.obtain(event); } } break; } } protected void updateStateByEvent(MotionEvent curr) { super.updateStateByEvent(curr); final MotionEvent prev = mPrevEvent; // Focus intenal mCurrFocusInternal = determineFocalPoint(curr); mPrevFocusInternal = determineFocalPoint(prev); // Focus external // - Prevent skipping of focus delta when a finger is added or removed boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount(); mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, mCurrFocusInternal.y - mPrevFocusInternal.y); // - Don't directly use mFocusInternal (or skipping will occur). Add // unskipped delta values to mFocusExternal instead. mFocusExternal.x += mFocusDeltaExternal.x; mFocusExternal.y += mFocusDeltaExternal.y; } /** * Determine (multi)finger focal point (a.k.a. center point between all * fingers) * * @param MotionEvent * e * @return PointF focal point */ private PointF determineFocalPoint(MotionEvent e) { // Number of fingers on screen final int pCount = e.getPointerCount(); float x = 0f; float y = 0f; for (int i = 0; i < pCount; i++) { x += e.getX(i); y += e.getY(i); } return new PointF(x / pCount, y / pCount); } public float getFocusX() { return mFocusExternal.x; } public float getFocusY() { return mFocusExternal.y; } public PointF getFocusDelta() { return mFocusDeltaExternal; } } MonitoredActivity.java package com.dingji.webpdemo; import java.util.ArrayList; import android.app.Activity; import android.os.Bundle; public class MonitoredActivity extends Activity { private final ArrayList<LifeCycleListener> mListeners = new ArrayList<LifeCycleListener>(); public static interface LifeCycleListener { public void onActivityCreated(MonitoredActivity activity); public void onActivityDestroyed(MonitoredActivity activity); public void onActivityPaused(MonitoredActivity activity); public void onActivityResumed(MonitoredActivity activity); public void onActivityStarted(MonitoredActivity activity); public void onActivityStopped(MonitoredActivity activity); } public static class LifeCycleAdapter implements LifeCycleListener { public void onActivityCreated(MonitoredActivity activity) { } public void onActivityDestroyed(MonitoredActivity activity) { } public void onActivityPaused(MonitoredActivity activity) { } public void onActivityResumed(MonitoredActivity activity) { } public void onActivityStarted(MonitoredActivity activity) { } public void onActivityStopped(MonitoredActivity activity) { } } public void addLifeCycleListener(LifeCycleListener listener) { if (mListeners.contains(listener)) return; mListeners.add(listener); } public void removeLifeCycleListener(LifeCycleListener listener) { mListeners.remove(listener); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); for (LifeCycleListener listener : mListeners) { listener.onActivityCreated(this); } } @Override protected void onDestroy() { super.onDestroy(); for (LifeCycleListener listener : mListeners) { listener.onActivityDestroyed(this); } } @Override protected void onStart() { super.onStart(); for (LifeCycleListener listener : mListeners) { listener.onActivityStarted(this); } } @Override protected void onStop() { super.onStop(); for (LifeCycleListener listener : mListeners) { listener.onActivityStopped(this); } } } ClipView.java package com.dingji.webpdemo; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; public class ClipView extends View { private Paint mStrokePaint; private Paint mBroadStrokePaint; private Paint mGrayPaint; private int mLeft; private int mRight; private int mTop; private int mBottom; public ClipView(Context context) { super(context); init(); } public ClipView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ClipView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mGrayPaint = new Paint(); mGrayPaint.setColor(0xaa000000); mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mStrokePaint.setColor(Color.WHITE); mStrokePaint.setStyle(Style.STROKE); mBroadStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBroadStrokePaint.setColor(Color.BLACK); mBroadStrokePaint.setStrokeWidth(3); mBroadStrokePaint.setStyle(Style.STROKE); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = this.getWidth(); int height = this.getHeight(); // top canvas.drawRect(0, 0, width, mTop, mGrayPaint); // left canvas.drawRect(0, mTop, mLeft, mBottom, mGrayPaint); // right canvas.drawRect(mRight, mTop, width, mBottom, mGrayPaint); // bottom canvas.drawRect(0, mBottom, width, height, mGrayPaint); // gray border canvas.drawRect(mLeft, mTop, mRight, mBottom, mBroadStrokePaint); canvas.drawRect(mLeft, mTop, mRight, mBottom, mStrokePaint); } /** * clip area * * @return clip area */ public Rect getClipRect() { return new Rect(mLeft, mTop, mRight, mBottom); } /** * * @param left * @param top * @param right * @param bottom */ public void setClipRect(int left, int top, int right, int bottom) { mLeft = left; mTop = top; mRight = right; mBottom = bottom; } /** * * @param rect */ public void setClipRect(Rect rect) { mLeft = rect.left; mTop = rect.top; mRight = rect.right; mBottom = rect.bottom; } } ClipPictureActivity.java package com.dingji.webpdemo; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.text.TextUtils; import android.util.Log; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.ViewTreeObserver.OnPreDrawListener; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.TextView; import android.widget.Toast; public class ClipPictureActivity extends MonitoredActivity implements OnTouchListener { private static final String TAG = "ClipPictureActivity"; final int IMAGE_MAX_SIZE = 1024; public static final String IMAGE_PATH = "image-path"; public static final String ASPECT_X = "aspectX"; public static final String ASPECT_Y = "aspectY"; public static final String OUTPUT_X = "outputX"; public static final String OUTPUT_Y = "outputY"; public static final String RETURN_DATA_AS_BITMAP = "data"; public static final String ACTION_INLINE_DATA = "inline-data"; public static final String RETURN_DATA = "return-data"; private Bitmap.CompressFormat mOutputFormat = Bitmap.CompressFormat.JPEG; private ContentResolver mContentResolver; private final Handler mHandler = new Handler(); private ImageView mSrcImage; private ClipView mClipview; private TextView sure; private TextView cancel; private int mAspectX; private int mAspectY; private int mOutputX; private int mOutputY; private String mImagePath; private Uri mSaveUri = null; private String mSavePath; private Bitmap mBitmap; private Rect mClipRect; private boolean isCreated; private int mImageViewWidth; private int mImageViewHeight; private Matrix mMatrix = new Matrix(); private float mScaleFactor = .4f; private float mRotationDegrees = 0.f; private float mFocusX = 0.f; private float mFocusY = 0.f; private int mAlpha = 255; private int mImageHeight, mImageWidth; private ScaleGestureDetector mScaleDetector; private RotateGestureDetector mRotateDetector; private MoveGestureDetector mMoveDetector; private ShoveGestureDetector mShoveDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.clip_layout); mContentResolver = getContentResolver(); // Setup Gesture Detectors initGesture(); initData(); initViews(); } private void initGesture() { mScaleDetector = new ScaleGestureDetector(getApplicationContext(), new ScaleListener()); mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateListener()); mMoveDetector = new MoveGestureDetector(getApplicationContext(), new MoveListener()); mShoveDetector = new ShoveGestureDetector(getApplicationContext(), new ShoveListener()); } private void initData() { Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) { mOutputX = extras.getInt(OUTPUT_X, 512); mOutputY = extras.getInt(OUTPUT_Y, 512); mImagePath = extras.getString(IMAGE_PATH); if (extras.containsKey(ASPECT_X) && extras.get(ASPECT_X) instanceof Integer) { mAspectX = extras.getInt(ASPECT_X); } else { throw new IllegalArgumentException("aspect_x must be integer"); } if (extras.containsKey(ASPECT_Y) && extras.get(ASPECT_Y) instanceof Integer) { mAspectY = extras.getInt(ASPECT_Y); } else { throw new IllegalArgumentException("aspect_y must be integer"); } mBitmap = getBitmap(mImagePath); mSavePath = getSaveImagePath(mImagePath); mSaveUri = getImageUri(mSavePath); } if (mBitmap == null) { Log.d(TAG, "finish!!!"); Toast.makeText(getApplicationContext(), "图片加载失败,不能进行图片裁剪!", Toast.LENGTH_LONG).show(); finish(); return; } } private String getSaveImagePath(String imagePath) { String sdcardRootPath = Environment.getExternalStorageDirectory().getPath(); if (!TextUtils.isEmpty(imagePath)) { int len = imagePath.lastIndexOf("/"); String fileName = imagePath.substring(len + 1); String[] file = fileName.split("\\."); if (file != null && file.length >= 2) { fileName = file[0] + "_clip" + "." + file[file.length - 1]; } else { fileName = fileName + "_clip"; } if (!imagePath.startsWith(sdcardRootPath) || imagePath.startsWith("/system")) { return sdcardRootPath + "/" + fileName; } return imagePath.substring(0, len + 1) + fileName; } return imagePath; } private void initViews() { mClipview = (ClipView) this.findViewById(R.id.clipview); mSrcImage = (ImageView) this.findViewById(R.id.src_pic); mSrcImage.setOnTouchListener(this); mSrcImage.setScaleType(ScaleType.MATRIX); mSrcImage.setImageBitmap(mBitmap); isCreated = true; mClipview.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { public boolean onPreDraw() { if (isCreated) { isCreated = false; initClipRange(); } return true; } }); sure = (TextView) this.findViewById(R.id.edit_pic_sure_text); sure.setOnClickListener(new OnClickListener() { public void onClick(View v) { try { onSaveClicked(); } catch (Exception e) { e.printStackTrace(); } } }); cancel = (TextView) this.findViewById(R.id.edit_pic_cancel_text); cancel.setOnClickListener(new OnClickListener() { public void onClick(View v) { finish(); } }); ImageView roteBtn = (ImageView) this.findViewById(R.id.edit_pic_rote_imageview); roteBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mRotationDegrees <= 0) { mRotationDegrees = 270; } else { mRotationDegrees -= 90; } setImageMatrix(mSrcImage); } }); } @SuppressWarnings("deprecation") public boolean onTouch(View v, MotionEvent event) { mScaleDetector.onTouchEvent(event); // mRotateDetector.onTouchEvent(event); mMoveDetector.onTouchEvent(event); // mShoveDetector.onTouchEvent(event); ImageView view = (ImageView) v; setImageMatrix(view); view.setAlpha(mAlpha); return true; // indicate event was handled } private void setImageMatrix(ImageView view) { float scaledImageCenterX = (mImageWidth * mScaleFactor) / 2; float scaledImageCenterY = (mImageHeight * mScaleFactor) / 2; mMatrix.reset(); mMatrix.postScale(mScaleFactor, mScaleFactor); mMatrix.postRotate(mRotationDegrees, scaledImageCenterX, scaledImageCenterY); mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY); view.setImageMatrix(mMatrix); } private void initClipRange() { mImageViewWidth = mClipview.getWidth(); mImageViewHeight = mClipview.getHeight(); int cx = mImageViewWidth / 2; // 中心点x。 int cy = mImageViewHeight / 2; // 中心点y。 int aw = mImageViewWidth - 100; // 从中心点向外扩充的长度。 int ah = aw * mOutputY / mOutputX; int left = cx - aw / 2; int top = cy - ah / 2; int right = cx + aw / 2; int bottom = cy + ah / 2; mClipRect = new Rect(left, top, right, bottom); mClipview.setClipRect(mClipRect); mImageWidth = mBitmap.getWidth(); mImageHeight = mBitmap.getHeight(); int scaleValue = mImageWidth > mImageHeight ? mImageWidth : mImageHeight; int scaleDenominator = mClipRect.width() > mClipRect.height() ? mClipRect.width() : mClipRect.height(); mScaleFactor = (float) scaleDenominator / scaleValue; mFocusX = (int) mImageViewWidth / 2; mFocusY = (int) mImageViewHeight / 2; setImageMatrix(mSrcImage); } /** * Draw the view into a bitmap. */ private Bitmap getViewBitmap(View v) { v.clearFocus(); v.setPressed(false); boolean willNotCache = v.willNotCacheDrawing(); v.setWillNotCacheDrawing(false); // Reset the drawing cache background color to fully transparent // for the duration of this operation int color = v.getDrawingCacheBackgroundColor(); v.setDrawingCacheBackgroundColor(0); if (color != 0) { v.destroyDrawingCache(); } v.buildDrawingCache(); Bitmap cacheBitmap = v.getDrawingCache(); if (cacheBitmap == null) { return null; } Bitmap result = Bitmap.createBitmap(mOutputX, mOutputY, Config.ARGB_8888); Canvas canvas = new Canvas(result); Rect src = mClipview.getClipRect(); Rect dst = new Rect(0, 0, mOutputX, mOutputY); canvas.drawBitmap(cacheBitmap, src, dst, new Paint()); // Restore the view v.destroyDrawingCache(); v.setWillNotCacheDrawing(willNotCache); v.setDrawingCacheBackgroundColor(color); return result; } private Uri getImageUri(String path) { if (!TextUtils.isEmpty(path)) { File file = new File(path); if (file != null) { return Uri.fromFile(file); } } return null; } private Bitmap getBitmap(String path) { Uri uri = getImageUri(path); InputStream in = null; try { if (uri != null) { in = mContentResolver.openInputStream(uri); // Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, o); in.close(); int scale = 1; if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) { scale = (int) Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5))); } BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; in = mContentResolver.openInputStream(uri); Bitmap b = BitmapFactory.decodeStream(in, null, o2); in.close(); return b; } } catch (FileNotFoundException e) { Log.e(TAG, "file " + path + " not found"); } catch (IOException e) { Log.e(TAG, "file " + path + " not found"); } catch (Exception e) { e.printStackTrace(); } return null; } boolean mSaving; // Whether the "save" button is already clicked. private void onSaveClicked() throws Exception { if (mSaving) return; mSaving = true; Bitmap croppedImage = getViewBitmap(mSrcImage); // Return the cropped image directly or save it to the specified URI. Bundle myExtras = getIntent().getExtras(); if (myExtras != null && myExtras.getBoolean(RETURN_DATA)) { Bundle extras = new Bundle(); extras.putParcelable(RETURN_DATA_AS_BITMAP, croppedImage); setResult(RESULT_OK, (new Intent()).setAction(ACTION_INLINE_DATA).putExtras(extras)); finish(); } else { final Bitmap b = croppedImage; startBackgroundJob(this, null, "正在保存图片。。。", new Runnable() { public void run() { saveOutput(b); } }, mHandler); } } private static class BackgroundJob extends MonitoredActivity.LifeCycleAdapter implements Runnable { private final MonitoredActivity mActivity; private final ProgressDialog mDialog; private final Runnable mJob; private final Handler mHandler; private final Runnable mCleanupRunner = new Runnable() { public void run() { mActivity.removeLifeCycleListener(BackgroundJob.this); if (mDialog.getWindow() != null) mDialog.dismiss(); } }; public BackgroundJob(MonitoredActivity activity, Runnable job, ProgressDialog dialog, Handler handler) { mActivity = activity; mDialog = dialog; mJob = job; mActivity.addLifeCycleListener(this); mHandler = handler; } public void run() { try { mJob.run(); } finally { mHandler.post(mCleanupRunner); } } @Override public void onActivityDestroyed(MonitoredActivity activity) { // We get here only when the onDestroyed being called before // the mCleanupRunner. So, run it now and remove it from the queue mCleanupRunner.run(); mHandler.removeCallbacks(mCleanupRunner); } @Override public void onActivityStopped(MonitoredActivity activity) { mDialog.hide(); } @Override public void onActivityStarted(MonitoredActivity activity) { mDialog.show(); } } public static void startBackgroundJob(MonitoredActivity activity, String title, String message, Runnable job, Handler handler) { // Make the progress dialog uncancelable, so that we can gurantee // the thread will be done before the activity getting destroyed. ProgressDialog dialog = ProgressDialog.show(activity, title, message, true, false); new Thread(new BackgroundJob(activity, job, dialog, handler)).start(); } private void saveOutput(Bitmap croppedImage) { if (mSaveUri != null) { OutputStream outputStream = null; try { outputStream = mContentResolver.openOutputStream(mSaveUri); if (outputStream != null) { croppedImage.compress(mOutputFormat, 90, outputStream); } } catch (IOException ex) { Log.e(TAG, "Cannot open file: " + mSaveUri, ex); setResult(RESULT_CANCELED); finish(); return; } finally { closeSilently(outputStream); } Bundle extras = new Bundle(); Intent intent = new Intent(mSaveUri.toString()); intent.putExtras(extras); intent.putExtra(IMAGE_PATH, mSavePath); setResult(RESULT_OK, intent); } else { Log.e(TAG, "not defined image url"); } croppedImage.recycle(); finish(); } public static void closeSilently(Closeable c) { if (c == null) return; try { c.close(); } catch (Throwable t) { // do nothing } } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); // scale change since // previous event // Don't let the object get too small or too large. mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f)); return true; } } private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener { @Override public boolean onRotate(RotateGestureDetector detector) { mRotationDegrees -= detector.getRotationDegreesDelta(); return true; } } private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener { @Override public boolean onMove(MoveGestureDetector detector) { PointF d = detector.getFocusDelta(); mFocusX += d.x; mFocusY += d.y; // mFocusX = detector.getFocusX(); // mFocusY = detector.getFocusY(); return true; } } private class ShoveListener extends ShoveGestureDetector.SimpleOnShoveGestureListener { @Override public boolean onShove(ShoveGestureDetector detector) { mAlpha += detector.getShovePixelsDelta(); if (mAlpha > 255) mAlpha = 255; else if (mAlpha < 0) mAlpha = 0; return true; } } } BaseGestureDetector.java package com.dingji.webpdemo; import android.content.Context; import android.view.MotionEvent; public abstract class BaseGestureDetector { protected final Context mContext; protected boolean mGestureInProgress; protected MotionEvent mPrevEvent; protected MotionEvent mCurrEvent; protected float mCurrPressure; protected float mPrevPressure; protected long mTimeDelta; /** * This value is the threshold ratio between the previous combined pressure * and the current combined pressure. When pressure decreases rapidly * between events the position values can often be imprecise, as it usually * indicates that the user is in the process of lifting a pointer off of the * device. This value was tuned experimentally. */ protected static final float PRESSURE_THRESHOLD = 0.67f; public BaseGestureDetector(Context context) { mContext = context; } /** * All gesture detectors need to be called through this method to be able to * detect gestures. This method delegates work to handler methods * (handleStartProgressEvent, handleInProgressEvent) implemented in * extending classes. * * @param event * @return */ public boolean onTouchEvent(MotionEvent event) { final int actionCode = event.getAction() & MotionEvent.ACTION_MASK; if (!mGestureInProgress) { handleStartProgressEvent(actionCode, event); } else { handleInProgressEvent(actionCode, event); } return true; } /** * Called when the current event occurred when NO gesture is in progress * yet. The handling in this implementation may set the gesture in progress * (via mGestureInProgress) or out of progress * * @param actionCode * @param event */ protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event); /** * Called when the current event occurred when a gesture IS in progress. The * handling in this implementation may set the gesture out of progress (via * mGestureInProgress). * * @param action * @param event */ protected abstract void handleInProgressEvent(int actionCode, MotionEvent event); protected void updateStateByEvent(MotionEvent curr) { final MotionEvent prev = mPrevEvent; // Reset mCurrEvent if (mCurrEvent != null) { mCurrEvent.recycle(); mCurrEvent = null; } mCurrEvent = MotionEvent.obtain(curr); // Delta time mTimeDelta = curr.getEventTime() - prev.getEventTime(); // Pressure mCurrPressure = curr.getPressure(curr.getActionIndex()); mPrevPressure = prev.getPressure(prev.getActionIndex()); } protected void resetState() { if (mPrevEvent != null) { mPrevEvent.recycle(); mPrevEvent = null; } if (mCurrEvent != null) { mCurrEvent.recycle(); mCurrEvent = null; } mGestureInProgress = false; } /** * Returns {@code true} if a gesture is currently in progress. * * @return {@code true} if a gesture is currently in progress, {@code false} otherwise. */ public boolean isInProgress() { return mGestureInProgress; } /** * Return the time difference in milliseconds between the previous accepted * GestureDetector event and the current GestureDetector event. * * @return Time difference since the last move event in milliseconds. */ public long getTimeDelta() { return mTimeDelta; } /** * Return the event time of the current GestureDetector event being * processed. * * @return Current GestureDetector event time in milliseconds. */ public long getEventTime() { return mCurrEvent.getEventTime(); } } clip_layout.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <RelativeLayout android:id="@+id/edit_pic_buttom_layout" android:layout_width="match_parent" android:layout_height="50dp" android:layout_alignParentBottom="true" > <TextView android:id="@+id/edit_pic_cancel_text" android:layout_width="50dp" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_marginLeft="10dp" android:gravity="center" android:text="返回" android:textSize="18sp" /> <TextView android:id="@+id/edit_pic_sure_text" android:layout_width="50dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:gravity="center" android:text="确定" android:textSize="18sp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toLeftOf="@id/edit_pic_sure_text" android:layout_toRightOf="@id/edit_pic_cancel_text" > <ImageView android:layout_width="0.4dp" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:paddingBottom="10dp" android:paddingTop="10dp" /> <ImageView android:layout_width="0.4dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:paddingBottom="10dp" android:paddingTop="10dp" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="可移动和缩放" android:textSize="18sp" /> </RelativeLayout> </RelativeLayout> <FrameLayout android:id="@+id/edit_pic_frame_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/edit_pic_buttom_layout" android:layout_alignParentTop="true" > <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/src_pic" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="matrix" /> <com.dingji.webpdemo.ClipView android:id="@+id/clipview" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> <ImageView android:id="@+id/edit_pic_rote_imageview" android:layout_width="60dp" android:layout_height="wrap_content" android:layout_gravity="top|right" android:layout_marginRight="10dp" android:layout_marginTop="10dp" /> </FrameLayout> </RelativeLayout> main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/btn_view_webp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="@string/str_btn_view_webp" /> <ImageView android:id="@+id/iv_webp" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="fitCenter" android:src="@drawable/ic_launcher" /> </LinearLayout>
试试其它关键字
手机相册
选取照片
同语言下
.
List 切割成几份 工具类
.
一行一行读取txt的内容
.
Java PDF转换成图片并输出给前台展示
.
java 多线程框架
.
double类型如果小数点后为零则显示整数否则保留两位小
.
将图片转换为Base64字符串公共类抽取
.
sqlParser 处理SQL(增删改查) 替换schema 用于多租户
.
JAVA 月份中的第几周处理 1-7属于第一周 依次类推 29-
.
java计算两个经纬度之间的距离
.
输入时间参数计算年龄
可能有用的
.
C#实现的html内容截取
.
List 切割成几份 工具类
.
SQL查询 多列合并成一行用逗号隔开
.
一行一行读取txt的内容
.
C#动态修改文件夹名称(FSO实现,不移动文件)
.
c# 移动文件或文件夹
.
c#图片添加水印
.
Java PDF转换成图片并输出给前台展示
.
网站后台修改图片尺寸代码
.
处理大图片在缩略图时的展示
依依
贡献的其它代码
(
32
)
.
间隔5秒循环执行一个方法
.
星期判断
.
总在最前
.
计算标准差
.
查看Hive表信息及占用空间的方法
.
定期移植数据的存储过程
.
检查 HTTP 请求的返回代码
.
获取某段时间内的所有日期
.
判断操作系统
.
获取执行插入数据后的id
Copyright © 2004 - 2024 dezai.cn. All Rights Reserved
站长博客
粤ICP备13059550号-3