- 浏览: 2196712 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (1240)
- mac/IOS (287)
- flutter (1)
- J2EE (115)
- android基础知识 (582)
- android中级知识 (55)
- android组件(Widget)开发 (18)
- android 错误 (21)
- javascript (18)
- linux (70)
- 树莓派 (18)
- gwt/gxt (1)
- 工具(IDE)/包(jar) (18)
- web前端 (17)
- java 算法 (8)
- 其它 (5)
- chrome (7)
- 数据库 (8)
- 经济/金融 (0)
- english (2)
- HTML5 (7)
- 网络安全 (14)
- 设计欣赏/设计窗 (8)
- 汇编/C (8)
- 工具类 (4)
- 游戏 (5)
- 开发频道 (5)
- Android OpenGL (1)
- 科学 (4)
- 运维 (0)
- 好东西 (6)
- 美食 (1)
最新评论
-
liangzai_cool:
请教一下,文中,shell、C、Python三种方式控制led ...
树莓派 - MAX7219 -
jiazimo:
...
Kafka源码分析-序列5 -Producer -RecordAccumulator队列分析 -
hp321:
Windows该命令是不是需要安装什么软件才可以?我试过不行( ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
hp321:
Chenzh_758 写道其实直接用一下代码就可以解决了:JP ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
huanghonhpeng:
大哥你真强什么都会,研究研究。。。。小弟在这里学到了很多知识。 ...
android 浏览器
安卓实现方形头像裁剪
实现思路,界面可见区域为2层View
最顶层的View是显示层,主要绘制半透明边框区域和白色裁剪区域,代码比较容易。
第二层继承ImageView,使用ImageView的Matrix实现显示部分图片,及挪动,放大缩小等操作。
比较复杂的地方在于多指操作对ImageView的影响,详见代码:
ClipSquareImageView.java
代码下载:https://github.com/h3clikejava/AndroidClipSquare
实现思路,界面可见区域为2层View
最顶层的View是显示层,主要绘制半透明边框区域和白色裁剪区域,代码比较容易。
第二层继承ImageView,使用ImageView的Matrix实现显示部分图片,及挪动,放大缩小等操作。
比较复杂的地方在于多指操作对ImageView的影响,详见代码:
ClipSquareImageView.java
package com.h3c.androidclipsquare; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewTreeObserver; import android.widget.ImageView; /** * Created by H3c on 12/13/14. */ public class ClipSquareImageView extends ImageView implements View.OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener { private static final int BORDERDISTANCE = ClipSquareView.BORDERDISTANCE; public static final float DEFAULT_MAX_SCALE = 4.0f; public static final float DEFAULT_MID_SCALE = 2.0f; public static final float DEFAULT_MIN_SCALE = 1.0f; private float minScale = DEFAULT_MIN_SCALE; private float midScale = DEFAULT_MID_SCALE; private float maxScale = DEFAULT_MAX_SCALE; private MultiGestureDetector multiGestureDetector; private boolean isIniting;// 正在初始化 private Matrix defaultMatrix = new Matrix();// 初始化的图片矩阵,控制图片撑满屏幕及显示区域 private Matrix dragMatrix = new Matrix();// 拖拽放大过程中动态的矩阵 private Matrix finalMatrix = new Matrix();// 最终显示的矩阵 private final RectF displayRect = new RectF();// 图片的真实大小 private final float[] matrixValues = new float[9]; private int borderlength; public ClipSquareImageView(Context context, AttributeSet attrs) { super(context, attrs); super.setScaleType(ScaleType.MATRIX); setOnTouchListener(this); multiGestureDetector = new MultiGestureDetector(context); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this); } @SuppressWarnings("deprecation") @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeGlobalOnLayoutListener(this); } @Override public void onGlobalLayout() { if(isIniting) { return; } // 调整视图位置 initBmpPosition(); } /** * 初始化图片位置 */ private void initBmpPosition() { isIniting = true; super.setScaleType(ScaleType.MATRIX); Drawable drawable = getDrawable(); if(drawable == null) { return; } final float viewWidth = getWidth(); final float viewHeight = getHeight(); final int drawableWidth = drawable.getIntrinsicWidth(); final int drawableHeight = drawable.getIntrinsicHeight(); if(viewWidth < viewHeight) { borderlength = (int) (viewWidth - 2 * BORDERDISTANCE); } else { borderlength = (int) (viewHeight - 2 * BORDERDISTANCE); } float screenScale = 1f; // 小于屏幕的图片会被撑满屏幕 if(drawableWidth <= drawableHeight) {// 竖图片 screenScale = (float) borderlength / drawableWidth; } else {// 横图片 screenScale = (float) borderlength / drawableHeight; } defaultMatrix.setScale(screenScale, screenScale); if(drawableWidth <= drawableHeight) {// 竖图片 float heightOffset = (viewHeight - drawableHeight * screenScale) / 2.0f; if(viewWidth <= viewHeight) {// 竖照片竖屏幕 defaultMatrix.postTranslate(BORDERDISTANCE, heightOffset); } else {// 竖照片横屏幕 defaultMatrix.postTranslate((viewWidth - borderlength) / 2.0f, heightOffset); } } else { float widthOffset = (viewWidth - drawableWidth * screenScale) / 2.0f; if(viewWidth <= viewHeight) {// 横照片,竖屏幕 defaultMatrix.postTranslate(widthOffset, (viewHeight - borderlength) / 2.0f); } else {// 横照片,横屏幕 defaultMatrix.postTranslate(widthOffset, BORDERDISTANCE); } } resetMatrix(); } /** * Resets the Matrix back to FIT_CENTER, and then displays it.s */ private void resetMatrix() { if(dragMatrix == null) { return; } dragMatrix.reset(); setImageMatrix(getDisplayMatrix()); } private Matrix getDisplayMatrix() { finalMatrix.set(defaultMatrix); finalMatrix.postConcat(dragMatrix); return finalMatrix; } @Override public boolean onTouch(View view, MotionEvent motionEvent) { return multiGestureDetector.onTouchEvent(motionEvent); } private class MultiGestureDetector extends GestureDetector.SimpleOnGestureListener implements ScaleGestureDetector.OnScaleGestureListener { private final ScaleGestureDetector scaleGestureDetector; private final GestureDetector gestureDetector; private final float scaledTouchSlop; private VelocityTracker velocityTracker; private boolean isDragging; private float lastTouchX; private float lastTouchY; private float lastPointerCount;// 上一次是几个手指事件 public MultiGestureDetector(Context context) { scaleGestureDetector = new ScaleGestureDetector(context, this); gestureDetector = new GestureDetector(context, this); gestureDetector.setOnDoubleTapListener(this); final ViewConfiguration configuration = ViewConfiguration.get(context); scaledTouchSlop = configuration.getScaledTouchSlop(); } @Override public boolean onScale(ScaleGestureDetector scaleGestureDetector) { float scale = getScale(); float scaleFactor = scaleGestureDetector.getScaleFactor(); if(getDrawable() != null && ((scale < maxScale && scaleFactor > 1.0f) || (scale > minScale && scaleFactor < 1.0f))){ if(scaleFactor * scale < minScale){ scaleFactor = minScale / scale; } if(scaleFactor * scale > maxScale){ scaleFactor = maxScale / scale; } dragMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2, getHeight() / 2); checkAndDisplayMatrix(); } return true; } @Override public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {} public boolean onTouchEvent(MotionEvent event) { if (gestureDetector.onTouchEvent(event)) { return true; } scaleGestureDetector.onTouchEvent(event); /* * Get the center x, y of all the pointers */ float x = 0, y = 0; final int pointerCount = event.getPointerCount(); for (int i = 0; i < pointerCount; i++) { x += event.getX(i); y += event.getY(i); } x = x / pointerCount; y = y / pointerCount; /* * If the pointer count has changed cancel the drag */ if (pointerCount != lastPointerCount) { isDragging = false; if (velocityTracker != null) { velocityTracker.clear(); } lastTouchX = x; lastTouchY = y; lastPointerCount = pointerCount; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); } else { velocityTracker.clear(); } velocityTracker.addMovement(event); lastTouchX = x; lastTouchY = y; isDragging = false; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: lastPointerCount = 0; if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } break; case MotionEvent.ACTION_MOVE: { final float dx = x - lastTouchX, dy = y - lastTouchY; if (isDragging == false) { // Use Pythagoras to see if drag length is larger than // touch slop isDragging = Math.sqrt((dx * dx) + (dy * dy)) >= scaledTouchSlop; } if (isDragging) { if (getDrawable() != null) { dragMatrix.postTranslate(dx, dy); checkAndDisplayMatrix(); } lastTouchX = x; lastTouchY = y; if (velocityTracker != null) { velocityTracker.addMovement(event); } } break; } } return true; } @Override public boolean onDoubleTap(MotionEvent event) { try { float scale = getScale(); float x = getWidth() / 2; float y = getHeight() / 2; if (scale < midScale) { post(new AnimatedZoomRunnable(scale, midScale, x, y)); } else if ((scale >= midScale) && (scale < maxScale)) { post(new AnimatedZoomRunnable(scale, maxScale, x, y)); } else {// 双击缩小小于最小值 post(new AnimatedZoomRunnable(scale, minScale, x, y)); } } catch (Exception e) { // Can sometimes happen when getX() and getY() is called } return true; } } private class AnimatedZoomRunnable implements Runnable { // These are 'postScale' values, means they're compounded each iteration static final float ANIMATION_SCALE_PER_ITERATION_IN = 1.07f; static final float ANIMATION_SCALE_PER_ITERATION_OUT = 0.93f; private final float focalX, focalY; private final float targetZoom; private final float deltaScale; public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, final float focalX, final float focalY) { this.targetZoom = targetZoom; this.focalX = focalX; this.focalY = focalY; if (currentZoom < targetZoom) { deltaScale = ANIMATION_SCALE_PER_ITERATION_IN; } else { deltaScale = ANIMATION_SCALE_PER_ITERATION_OUT; } } @Override public void run() { dragMatrix.postScale(deltaScale, deltaScale, focalX, focalY); checkAndDisplayMatrix(); final float currentScale = getScale(); if (((deltaScale > 1f) && (currentScale < targetZoom)) || ((deltaScale < 1f) && (targetZoom < currentScale))) { // We haven't hit our target scale yet, so post ourselves // again postOnAnimation(ClipSquareImageView.this, this); } else { // We've scaled past our target zoom, so calculate the // necessary scale so we're back at target zoom final float delta = targetZoom / currentScale; dragMatrix.postScale(delta, delta, focalX, focalY); checkAndDisplayMatrix(); } } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void postOnAnimation(View view, Runnable runnable) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { view.postOnAnimation(runnable); } else { view.postDelayed(runnable, 16); } } /** * Returns the current scale value * * @return float - current scale value */ public final float getScale() { dragMatrix.getValues(matrixValues); return matrixValues[Matrix.MSCALE_X]; } /** * Helper method that simply checks the Matrix, and then displays the result */ private void checkAndDisplayMatrix() { checkMatrixBounds(); setImageMatrix(getDisplayMatrix()); } private void checkMatrixBounds() { final RectF rect = getDisplayRect(getDisplayMatrix()); if (null == rect) { return; } float deltaX = 0, deltaY = 0; final float viewWidth = getWidth(); final float viewHeight = getHeight(); // 判断移动或缩放后,图片显示是否超出裁剪框边界 final float heightBorder = (viewHeight - borderlength) / 2; final float weightBorder = (viewWidth - borderlength) / 2; if(rect.top > heightBorder){ deltaY = heightBorder - rect.top; } if(rect.bottom < (viewHeight - heightBorder)){ deltaY = viewHeight - heightBorder - rect.bottom; } if(rect.left > weightBorder){ deltaX = weightBorder - rect.left; } if(rect.right < viewWidth - weightBorder){ deltaX = viewWidth - weightBorder - rect.right; } // Finally actually translate the matrix dragMatrix.postTranslate(deltaX, deltaY); } /** * 获取图片相对Matrix的距离 * * @param matrix * - Matrix to map Drawable against * @return RectF - Displayed Rectangle */ private RectF getDisplayRect(Matrix matrix) { Drawable d = getDrawable(); if (null != d) { displayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(displayRect); return displayRect; } return null; } /** * 剪切图片,返回剪切后的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, (getWidth() - borderlength) / 2, (getHeight() - borderlength) / 2, borderlength, borderlength); } }
代码下载:https://github.com/h3clikejava/AndroidClipSquare
发表评论
-
带你深入理解 FLUTTER 中的字体“冷”知识
2020-08-10 23:40 626本篇将带你深入理解 Flutter 开发过程中关于字体和文 ... -
Flutter -自定义日历组件
2020-03-01 17:56 1100颜色文件和屏幕适配的文件 可以自己给定 import ... -
Dart高级(一)——泛型与Json To Bean
2020-02-23 19:13 993从 Flutter 发布到现在, 越来越多人开始尝试使用 Da ... -
flutter loading、Progress进度条
2020-02-21 17:03 1166Flutter Progress 1 条形无固定值进度条 ... -
Flutter使用Https加载图片
2020-02-21 01:39 1004Flutter使用Https加载图片 使用http加载图片出 ... -
flutter shared_preferences 异步变同步
2020-02-21 00:55 839前言 引用 在开发原生iOS或Native应用时,一般有判断上 ... -
Flutter TextField边框颜色
2020-02-19 21:31 929监听要销毁 myController.dispose(); T ... -
flutter Future的正确用法
2020-02-18 21:55 800在flutter中经常会用到异步任务,dart中异步任务异步处 ... -
记一次Flutter简单粗暴处理HTTPS证书检验方法
2020-02-18 14:13 949最近在做Flutter项目到了遇到一个无解的事情,当使用Ima ... -
flutter 获取屏幕宽度高度 通知栏高度等屏幕信息
2019-07-27 08:39 1327##MediaQuery MediaQuery.of(con ... -
关于flutter RefreshIndicator扩展listview下拉刷新的问题
2019-07-10 19:40 1113当条目过少时listview某些嵌套情况下可能不会滚动(条目 ... -
flutter listview 改变状态的时候一直无限添加
2019-07-10 16:01 778setstate的时候会一直无限的调用listview.bui ... -
Flutter Android端启动白屏问题的解决
2019-07-09 00:51 1507问题描述 Flutter 应用在 Android 端上启动时 ... -
Flutter中SnackBar使用
2019-07-08 23:43 766底部弹出,然后在指定时间后消失。 注意: build(Bui ... -
Flutter 之点击空白区域收起键盘
2019-07-08 18:43 1782点击空白处取消TextField焦点这个需求是非常简单的,在学 ... -
Flutter 弹窗 Dialog ,AlertDialog,IOS风格
2019-07-08 18:04 1370import 'package:flutter/mate ... -
flutter ---TextField 之 输入类型、长度限制
2019-07-08 14:30 2313TextField想要实现输入类型、长度限制需要先引入impo ... -
【flutter 溢出BUG】键盘上显示bottom overflowed by 104 PIXELS
2019-07-08 11:13 1549一开始直接使用Scaffold布局,body:new Colu ... -
解决Flutter项目卡在Initializing gradle...界面的问题
2019-07-07 12:53 865Flutter最近很火,我抽出了一点时间对Flutter进行了 ... -
关于android O 上 NotificationChannel 的一些注意事项
2019-07-04 11:47 932最近在适配android O,遇到个问题,应用中原本有设置界面 ...
相关推荐
该源码项目是一个安卓实现方形头像裁剪,源码AndroidClipSquare,安卓实现方形头像裁剪,实现思路,界面可见区域为2层View,最顶层的View是显示层,主要绘制半透明边框区域和白色裁剪区域,代码比较容易。...
在Android开发中,为了提供类似微信的用户体验,经常会遇到实现头像裁剪功能的需求。"Android 仿微信头像裁剪"是一个常见的实践项目,它允许用户选择一张图片,并通过手势操作来裁剪出合适的头像。这个项目的核心是...
总的来说,Android头像裁剪功能的实现有多种途径,从简单的系统调用到复杂的自定义视图,每种方法都有其适用场景。理解这些方法的工作原理和优缺点,可以帮助开发者快速而高效地完成头像裁剪功能的集成,提升应用的...
总之,"头像裁剪Shader"是一个实用的技术,它通过自定义Shader实现了图像的动态裁剪,使得在方形和圆形头像之间的切换变得轻松便捷。通过深入学习和实践,开发者可以将这种技术扩展到更广泛的图像处理需求中,提升...
这个"安卓头像制作图片圆角剪裁相关-android按比例裁剪上传.rar"压缩包文件包含了实现这一功能的相关代码示例。下面我们将深入探讨这个主题。 首先,我们来理解“图片圆角剪裁”。在Android中,为了实现图片的圆角...
"安卓头像制作图片圆角剪裁相关-android-crop是一个图片裁剪工具可以选择裁剪照片进行裁剪裁剪区域可拖动"的资源就是针对这一需求提供的解决方案。这个压缩包包含了一个名为`android-crop`的开源库,它允许用户在...
"头像裁剪上传.zip"这个压缩包文件显然包含了实现这一功能的相关代码或资源。以下将详细介绍关于“mui 图像头像裁剪上传”涉及的技术点、应用场景以及可能用到的工具和方法。 首先,"mui"是一个轻量级的前端框架,...
本教程将详细讲解如何实现"android 头像与封面的裁剪",并提供关键知识点。 一、头像裁剪与预览 头像通常需要呈现出圆形效果,以符合人眼的视觉习惯。在Android中,我们可以利用`BitmapShader`类创建圆形图片。首先...
实现了从相机获取图片和相册获得图片裁剪上传到服务器
2. 实现裁剪框,可以是一个可拖动、缩放的矩形或椭圆形,用于定义裁剪区域。 3. 处理手势事件,更新裁剪框的位置和大小。 4. 在用户完成裁剪后,根据裁剪框的位置和大小截取原始图片的相应部分。 四、图片处理 裁剪...
在Android开发中,实现用户头像裁剪是一项常见的需求,特别是在社交应用中。"Android裁剪头像的demo"是一个示例项目,它模仿了QQ应用程序的头像裁剪功能,提供了一种高效且用户体验良好的解决方案。这个项目的核心是...
本篇将详细介绍如何利用Android中的开源库来实现“最好的android图片裁剪及头像截取工具”。 首先,我们可以看到描述中提到的“cropper-master”这一文件名,这很可能是指一个名为“CropImage”的开源项目,它是...
在Android开发中,实现用户头像裁剪是一项常见的功能,特别是在社交应用或者个人资料设置界面。这个"Android头像裁剪"项目提供了一个自定义的解决方案,允许用户通过单指操作来裁剪出正方形的头像。接下来,我们将...
"Android-Android仿微信的图片选择器带裁剪功能支持圆形和方形裁剪"这个项目就是这样一个实现,它为开发者提供了完整的解决方案。 首先,我们来探讨一下该项目的核心知识点: 1. **自定义相册**: 在Android中,...
在IT行业中,头像裁剪是一项常见的需求,特别是在社交媒体、论坛和在线社区等平台。"Flash头像裁剪工具"是这样一个解决方案,它允许用户自定义裁剪图像以创建适合自己需求的头像。这个工具的独特之处在于其能够生成...
在Android中,我们可以使用`android.graphics.Bitmap`类的`createBitmap`方法结合`Matrix`来实现裁剪。首先,我们需要获取用户选择图片的原始Bitmap,然后根据预设的裁剪区域计算出新的矩阵,最后通过矩阵创建新的...
本插件专注于提供一个便捷的头像裁剪解决方案,结合前端与后端技术,实现了用户友好的拖动裁剪功能。 该插件的核心是利用JavaScript库JCrop,这是一个强大的图像裁剪工具,支持像素级精确选择和拖动裁剪区域。JCrop...
本文将深入探讨如何在Android平台上实现这一功能。 首先,我们需要了解Android中的Bitmap类,它是处理图片的基础。Bitmap可以加载、创建、绘制和操作位图图像。当我们需要对图片进行裁剪成正方形时,通常会涉及到...
6. **圆形裁剪**: 为了实现圆形裁剪,我们需要将方形裁剪区域转换为圆形。这可以通过在`DrawImage`之前,计算出裁剪区域内所有像素距离圆形边界的最短距离。如果距离大于半径,说明该像素在圆形外部,应忽略不绘制。...
github上第一个第三方开源的图片修剪截取利器,功能强大,设计良好,提供了非常丰富的图片截取修剪功能,涵盖常用的基本需求功能,如图片的按比例截取(4:3,16:9,7:5等等)、截取成圆形、自由裁剪、锁定比例裁剪、...