仿微信头像剪切
来源:互联网 发布:Java开发dwg批量转jpg 编辑:程序博客网 时间:2024/05/16 17:36
来个动图:
裁剪效果其实就是图片在底部,上层一个视图重叠在上面,这里参考的是洪洋大神的仿微信头像篇,文章说的相当清楚了,我只是简洁的删改一点,看裁剪这个页面:
两个View重叠,就有了这个效果。看看底部View实现思路:底部View其实就是个ImageView,可以缩放,支持手势,直接贴代码吧
/** * 缩放图片的View * <p> * <p> * Created by dong.he on 2016/9/3. * <p> * blog:http://blog.csdn.net/hedong_77 */public class ClipZoomImageView extends ImageView implements OnScaleGestureListener, OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener { public static float SCALE_MAX = 4.0f; private static float SCALE_MID = 2.0f; /** * 初始化时的缩放比例 */ private float initScale = 1.0f; private boolean once = true; /** * 用于存放矩阵 */ private final float[] matrixValues = new float[9]; /** * 缩放的手势检验 */ private ScaleGestureDetector mScaleGestureDetector = null; private final Matrix mScaleMatrix = new Matrix(); /** * 用于双击 */ private GestureDetector mGestureDetector; private boolean isAutoScale; private int mTouchSlop; private float mLastX; private float mLastY; private boolean isCanDrag; private int lastPointerCount; /** * 水平方向与View的边距 */ private int mHorizontalPadding; public ClipZoomImageView(Context context) { this(context, null); } public ClipZoomImageView(Context context, AttributeSet attrs) { super(context, attrs); setScaleType(ScaleType.MATRIX); mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { if (isAutoScale == true) return true; float x = e.getX(); float y = e.getY(); if (getScale() < SCALE_MID) { ClipZoomImageView.this.postDelayed( new AutoScaleRunnable(SCALE_MID, x, y), 16); isAutoScale = true; } else { ClipZoomImageView.this.postDelayed( new AutoScaleRunnable(initScale, x, y), 16); isAutoScale = true; } return true; } }); mScaleGestureDetector = new ScaleGestureDetector(context, this); this.setOnTouchListener(this); } /** * 自动缩放的任务 */ private class AutoScaleRunnable implements Runnable { static final float BIGGER = 1.07f; static final float SMALLER = 0.93f; private float mTargetScale; private float tmpScale; private float x; private float y; /** * 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小 * * @param targetScale */ public AutoScaleRunnable(float targetScale, float x, float y) { this.mTargetScale = targetScale; this.x = x; this.y = y; if (getScale() < mTargetScale) { tmpScale = BIGGER; } else { tmpScale = SMALLER; } } @Override public void run() { // 进行缩放 mScaleMatrix.postScale(tmpScale, tmpScale, x, y); checkBorder(); setImageMatrix(mScaleMatrix); final float currentScale = getScale(); // 如果值在合法范围内,继续缩放 if (((tmpScale > 1f) && (currentScale < mTargetScale)) || ((tmpScale < 1f) && (mTargetScale < currentScale))) { ClipZoomImageView.this.postDelayed(this, 16); } else // 设置为目标的缩放比例 { final float deltaScale = mTargetScale / currentScale; mScaleMatrix.postScale(deltaScale, deltaScale, x, y); checkBorder(); setImageMatrix(mScaleMatrix); isAutoScale = false; } } } @Override public boolean onScale(ScaleGestureDetector detector) { float scale = getScale(); float scaleFactor = detector.getScaleFactor(); if (getDrawable() == null) return true; /** * 缩放的范围控制 */ if ((scale < SCALE_MAX && scaleFactor > 1.0f) || (scale > initScale && scaleFactor < 1.0f)) { /** * 最大值最小值判断 */ if (scaleFactor * scale < initScale) { scaleFactor = initScale / scale; } if (scaleFactor * scale > SCALE_MAX) { scaleFactor = SCALE_MAX / scale; } /** * 设置缩放比例 */ mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); checkBorder(); setImageMatrix(mScaleMatrix); } return true; } /** * 根据当前图片的Matrix获得图片的范围 * * @return */ private RectF getMatrixRectF() { Matrix matrix = mScaleMatrix; RectF rect = new RectF(); Drawable d = getDrawable(); if (null != d) { rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rect); } return rect; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } @Override public boolean onTouch(View v, MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) return true; mScaleGestureDetector.onTouchEvent(event); float x = 0, y = 0; // 拿到触摸点的个数 final int pointerCount = event.getPointerCount(); // 得到多个触摸点的x与y均�? for (int i = 0; i < pointerCount; i++) { x += event.getX(i); y += event.getY(i); } x = x / pointerCount; y = y / pointerCount; /** * 每当触摸点发生变化时,重置mLasX , mLastY */ if (pointerCount != lastPointerCount) { isCanDrag = false; mLastX = x; mLastY = y; } lastPointerCount = pointerCount; switch (event.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - mLastX; float dy = y - mLastY; if (!isCanDrag) { isCanDrag = isCanDrag(dx, dy); } if (isCanDrag) { if (getDrawable() != null) { RectF rectF = getMatrixRectF(); // 如果宽度小于屏幕宽度,则禁止左右移动 if (rectF.width() <= getWidth() - mHorizontalPadding * 2) { dx = 0; } // 如果高度小雨屏幕高度,则禁止上下移动 if (rectF.height() <= getHeight() - getHVerticalPadding() * 2) { dy = 0; } mScaleMatrix.postTranslate(dx, dy); checkBorder(); setImageMatrix(mScaleMatrix); } } mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: lastPointerCount = 0; break; } return true; } /** * 获得当前的缩放比例 * * @return */ public final float getScale() { mScaleMatrix.getValues(matrixValues); return matrixValues[Matrix.MSCALE_X]; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeGlobalOnLayoutListener(this); } /** * 垂直方向与View的边距 */ // private int getHVerticalPadding(); @Override public void onGlobalLayout() { if (once) { Drawable d = getDrawable(); if (d == null) return; int width = getWidth(); int height = getHeight(); // 拿到图片的宽和高 int drawableW = d.getIntrinsicWidth(); int drawableH = d.getIntrinsicHeight(); float scale = 1.0f; int frameSize = getWidth() - mHorizontalPadding * 2; // 大图 if (drawableW > frameSize && drawableH < frameSize) { scale = 1.0f * frameSize / drawableH; } else if (drawableH > frameSize && drawableW < frameSize) { scale = 1.0f * frameSize / drawableW; } else if (drawableW > frameSize && drawableH > frameSize) { float scaleW = frameSize * 1.0f / drawableW; float scaleH = frameSize * 1.0f / drawableH; scale = Math.max(scaleW, scaleH); } // 太小的图片放大处理 if (drawableW < frameSize && drawableH > frameSize) { scale = 1.0f * frameSize / drawableW; } else if (drawableH < frameSize && drawableW > frameSize) { scale = 1.0f * frameSize / drawableH; } else if (drawableW < frameSize && drawableH < frameSize) { float scaleW = 1.0f * frameSize / drawableW; float scaleH = 1.0f * frameSize / drawableH; scale = Math.max(scaleW, scaleH); } initScale = scale; SCALE_MID = initScale * 2; SCALE_MAX = initScale * 4; mScaleMatrix.postTranslate((width - drawableW) / 2, (height - drawableH) / 2); mScaleMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2); // 图片移动至屏幕中�? setImageMatrix(mScaleMatrix); once = false; } } /** * 剪切图片,返回剪切后的bitmap对象 * * @return */ public Bitmap clip() { Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); draw(canvas); return Bitmap.createBitmap(bitmap, mHorizontalPadding, getHVerticalPadding(), getWidth() - 2 * mHorizontalPadding, getWidth() - 2 * mHorizontalPadding); } /** * 边界线 */ private void checkBorder() { RectF rect = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); // 如果宽或高大于屏幕,则控制范围; 这里是因为精度丢失会产生问题,但是误差一般很小,所以我们直接加了0.01 if (rect.width() + 0.01 >= width - 2 * mHorizontalPadding) { if (rect.left > mHorizontalPadding) { deltaX = -rect.left + mHorizontalPadding; } if (rect.right < width - mHorizontalPadding) { deltaX = width - mHorizontalPadding - rect.right; } } if (rect.height() + 0.01 >= height - 2 * getHVerticalPadding()) { if (rect.top > getHVerticalPadding()) { deltaY = -rect.top + getHVerticalPadding(); } if (rect.bottom < height - getHVerticalPadding()) { deltaY = height - getHVerticalPadding() - rect.bottom; } } mScaleMatrix.postTranslate(deltaX, deltaY); } /** * 是否是拖动行为 * * @param dx * @param dy * @return */ private boolean isCanDrag(float dx, float dy) { return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; } public void setHorizontalPadding(int mHorizontalPadding) { this.mHorizontalPadding = mHorizontalPadding; } private int getHVerticalPadding() { return (getHeight() - (getWidth() - 2 * mHorizontalPadding)) / 2; }}
注释很详细了。上层的View 是ClipImageBorderView,就是画边框而已,看看:
这里面有一个对外公开的方法,可以自动设置边距。
裁剪完成之后直接保存图片就Ok了。裁剪工作在ClipImageActivity里面做,后面源码。
为了考虑6.0的权限问题,demo里面也加入了权限适配,看一下MainActivity的源码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, EasyPermissions.PermissionCallbacks { private Button takePhoto; private Button selectImg; private ImageView headImg; private final int START_ALBUM_REQUESTCODE = 1; private final int CAMERA_WITH_DATA = 2; private final int CROP_RESULT_CODE = 3; public static String TMP_PATH = "my.jpg"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); headImg = (ImageView) findViewById(R.id.iv_head); takePhoto = (Button) findViewById(R.id.btn_take); selectImg = (Button) findViewById(R.id.btn_select); takePhoto.setOnClickListener(this); selectImg.setOnClickListener(this); permissionAlertDialog = new AlertDialog.Builder(getApplicationContext()) .setTitle(R.string.permission_request) .setCancelable(false) .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { PermissionUtil.goSetting(getApplicationContext()); System.exit(0); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { System.exit(0); } }) .create(); } private void takePhoto() { // 拍照 if (EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA)) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File( Environment.getExternalStorageDirectory(), TMP_PATH))); startActivityForResult(intent, CAMERA_WITH_DATA); } else { EasyPermissions.requestPermissions(this, getString(R.string.rationale_camera), PermissionUtil.PERMISSION_CAMERA_REQ, Manifest.permission.CAMERA); } } // 从相册选择 private void selectImgByAlbum() { Intent intent = new Intent(Intent.ACTION_PICK, null); intent.setDataAndType( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent, START_ALBUM_REQUESTCODE); } // 裁剪图片的Activity private void startCropImageActivity(String path) { ClipImageActivity.startActivity(this, path, CROP_RESULT_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case CROP_RESULT_CODE: //最终路径(裁剪后的路径) final String path = data.getStringExtra(ClipImageActivity.RESULT_PATH); runOnUiThread(new Runnable() { @Override public void run() { Glide.with(MainActivity.this).load(path).asBitmap().into(headImg); } }); break; case START_ALBUM_REQUESTCODE: //返回裁剪 if (data != null) { startCropImageActivity(Utils.getFilePath(this,data.getData())); } else { Toast.makeText(this, R.string.toast_cannot_retrieve_selected_image, Toast.LENGTH_SHORT).show(); } break; case CAMERA_WITH_DATA: // 照相机程序返回的,再次调用图片剪辑程序去修剪图片 startCropImageActivity(Environment.getExternalStorageDirectory() + "/" + TMP_PATH); break; default: break; } } } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btn_take: takePhoto(); break; case R.id.btn_select: selectImgByAlbum(); break; } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == PermissionUtil.PERMISSION_WRITE_EXTERNAL_STORAGE_REQ) { if (grantResults.length != 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { showPermissionWarning(getString(R.string.permission_storage_warning)); } } super.onRequestPermissionsResult(requestCode, permissions, grantResults); // Forward results to EasyPermissions EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } @Override protected void onResume() { super.onResume();// if (!permissionAlertDialog.isShowing()) { if (PermissionUtil.checkPermission(this, PermissionUtil.PERMISSION_WRITE_EXTERNAL_STORAGE, PermissionUtil.PERMISSION_WRITE_EXTERNAL_STORAGE_REQ)) { }// } } private AlertDialog permissionAlertDialog; private void showPermissionWarning(String message) { permissionAlertDialog.setMessage(message); permissionAlertDialog.show(); } @Override public void onPermissionsGranted(int requestCode, List<String> perms) { } @Override public void onPermissionsDenied(int requestCode, List<String> perms) { if (requestCode == PermissionUtil.PERMISSION_CAMERA_REQ) { EasyPermissions.checkDeniedPermissionsNeverAskAgain(this, getString(R.string.permission_camera_warning), R.string.setting, R.string.cancel, perms); } }}
注释清晰,源码下载:http://download.csdn.net/detail/hedong_77/9740640
2 0
- 仿微信头像剪切
- jquery 头像剪切
- cocos2dx 剪切圆形头像
- Django imgareaselect 手动剪切头像
- 使用jcrop进行头像剪切
- 使用jcrop进行头像剪切
- 多种方式选择剪切头像
- WPF 自定义图片剪切器 - 头像剪切。你懂得
- 通过js包ImgAreaSelect剪切头像
- Android开发实现圆形头像剪切
- 实现头像上传及剪切功能(cakephp+jquery)
- Android 上传头像自定义(剪切、平移,缩放)
- 会员中心上传头像时的,上传图片并剪切
- Android 7.0 图片剪切问题,选择头像上传
- WPF 自定义图片剪切器 - 头像剪切(扩展与完善、实时截图)
- 剪切
- 剪切
- 利用Jquery的cropper插件实现拖动层动态头剪切(裁剪头像)图片
- ubuntu修改hosts
- 1036
- ava NIO使用及原理分析(三)
- ubuntu创建a sudo user
- ubuntu 16.04 安装Chrome
- 仿微信头像剪切
- 队列的链表存储实现
- mapreduce pv
- Java NIO使用及原理分析 (四)
- 1006
- Idea 快捷键
- MySQL更改数据库数据存储目录
- zoj1141
- oracle导入日期提示ora-01843