Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/189.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/image/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何像Facebook个人资料图像选择工具那样移动和缩放图像?_Android_Image_Crop - Fatal编程技术网

Android 如何像Facebook个人资料图像选择工具那样移动和缩放图像?

Android 如何像Facebook个人资料图像选择工具那样移动和缩放图像?,android,image,crop,Android,Image,Crop,我想要像Android上的Facebook个人资料图像选择一样裁剪图像,用户可以在Android上移动和缩放图像,从而使其调整大小和/或裁剪: 我怎样才能做到这一点呢?我也有同样的要求。我通过将cropper库中的ImageView替换为PhotoView解决了这个问题 我必须修改CropWindow类,以避免触摸事件没有得到正确处理: public void setImageView(PhotoView pv){ mPhotoView = pv; } @

我想要像Android上的Facebook个人资料图像选择一样裁剪图像,用户可以在Android上移动和缩放图像,从而使其调整大小和/或裁剪:


我怎样才能做到这一点呢?

我也有同样的要求。我通过将cropper库中的
ImageView
替换为
PhotoView
解决了这个问题

我必须修改
CropWindow
类,以避免触摸事件没有得到正确处理:

   public void setImageView(PhotoView pv){
        mPhotoView = pv;
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // If this View is not enabled, don't allow for touch interactions.
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                  boolean dispatch = onActionDown(event.getX(), event.getY());
                  if(!dispatch)
                    mPhotoView.dispatchTouchEvent(event);

                return dispatch;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                getParent().requestDisallowInterceptTouchEvent(false);
                onActionUp();
                return true;

            case MotionEvent.ACTION_MOVE:
                onActionMove(event.getX(), event.getY());
                getParent().requestDisallowInterceptTouchEvent(true);
                return true;

            default:
                return false;
        }
    }
CropImageView
类中,也改变了一些事情:

private void init(Context context) {

    final LayoutInflater inflater = LayoutInflater.from(context);
    final View v = inflater.inflate(R.layout.crop_image_view, this, true);

    mImageView = (PhotoView) v.findViewById(R.id.ImageView_image2);

    setImageResource(mImageResource);
    mCropOverlayView = (CropOverlayView) v.findViewById(R.id.CropOverlayView);
    mCropOverlayView.setInitialAttributeValues(mGuidelines, mFixAspectRatio, mAspectRatioX, mAspectRatioY);
    mCropOverlayView.setImageView(mImageView);
}
您可以注意到,我已在裁剪器库中将
ImageView
替换为
PhotoView
内部
R.layout.crop\u image\u view

Cropper library支持固定大小,PhotoView允许您移动和缩放照片,为您提供两全其美的效果。:)

希望能有帮助

编辑,对于询问如何获取仅在裁剪区域内的图像的用户:

private Bitmap getCurrentDisplayedImage(){
        Bitmap result = Bitmap.createBitmap(mImageView.getWidth(), mImageView.getHeight(), Bitmap.Config.RGB_565);
        Canvas c = new Canvas(result);
        mImageView.draw(c);
        return result;
    }
    public Bitmap getCroppedImage() {

        Bitmap mCurrentDisplayedBitmap = getCurrentDisplayedImage();
        final Rect displayedImageRect = ImageViewUtil2.getBitmapRectCenterInside(mCurrentDisplayedBitmap, mImageView);

        // Get the scale factor between the actual Bitmap dimensions and the
        // displayed dimensions for width.
        final float actualImageWidth =mCurrentDisplayedBitmap.getWidth();
        final float displayedImageWidth = displayedImageRect.width();
        final float scaleFactorWidth = actualImageWidth / displayedImageWidth;

        // Get the scale factor between the actual Bitmap dimensions and the
        // displayed dimensions for height.
        final float actualImageHeight = mCurrentDisplayedBitmap.getHeight();
        final float displayedImageHeight = displayedImageRect.height();
        final float scaleFactorHeight = actualImageHeight / displayedImageHeight;

        // Get crop window position relative to the displayed image.
        final float cropWindowX = Edge.LEFT.getCoordinate() - displayedImageRect.left;
        final float cropWindowY = Edge.TOP.getCoordinate() - displayedImageRect.top;
        final float cropWindowWidth = Edge.getWidth();
        final float cropWindowHeight = Edge.getHeight();

        // Scale the crop window position to the actual size of the Bitmap.
        final float actualCropX = cropWindowX * scaleFactorWidth;
        final float actualCropY = cropWindowY * scaleFactorHeight;
        final float actualCropWidth = cropWindowWidth * scaleFactorWidth;
        final float actualCropHeight = cropWindowHeight * scaleFactorHeight;

        // Crop the subset from the original Bitmap.
        final Bitmap croppedBitmap = Bitmap.createBitmap(mCurrentDisplayedBitmap,
                                                         (int) actualCropX,
                                                         (int) actualCropY,
                                                         (int) actualCropWidth,
                                                         (int) actualCropHeight);

        return croppedBitmap;
    }

    public RectF getActualCropRect() {

        final Rect displayedImageRect = ImageViewUtil.getBitmapRectCenterInside(mBitmap, mImageView);

        final float actualImageWidth = mBitmap.getWidth();
        final float displayedImageWidth = displayedImageRect.width();
        final float scaleFactorWidth = actualImageWidth / displayedImageWidth;

        // Get the scale factor between the actual Bitmap dimensions and the displayed
        // dimensions for height.
        final float actualImageHeight = mBitmap.getHeight();
        final float displayedImageHeight = displayedImageRect.height();
        final float scaleFactorHeight = actualImageHeight / displayedImageHeight;

        // Get crop window position relative to the displayed image.
        final float displayedCropLeft = Edge.LEFT.getCoordinate() - displayedImageRect.left;
        final float displayedCropTop = Edge.TOP.getCoordinate() - displayedImageRect.top;
        final float displayedCropWidth = Edge.getWidth();
        final float displayedCropHeight = Edge.getHeight();

        // Scale the crop window position to the actual size of the Bitmap.
        float actualCropLeft = displayedCropLeft * scaleFactorWidth;
        float actualCropTop = displayedCropTop * scaleFactorHeight;
        float actualCropRight = actualCropLeft + displayedCropWidth * scaleFactorWidth;
        float actualCropBottom = actualCropTop + displayedCropHeight * scaleFactorHeight;

        // Correct for floating point errors. Crop rect boundaries should not exceed the
        // source Bitmap bounds.
        actualCropLeft = Math.max(0f, actualCropLeft);
        actualCropTop = Math.max(0f, actualCropTop);
        actualCropRight = Math.min(mBitmap.getWidth(), actualCropRight);
        actualCropBottom = Math.min(mBitmap.getHeight(), actualCropBottom);

        final RectF actualCropRect = new RectF(actualCropLeft,
                                               actualCropTop,
                                               actualCropRight,
                                               actualCropBottom);

        return actualCropRect;
    }




private boolean onActionDown(float x, float y) {    
        final float left = Edge.LEFT.getCoordinate();
        final float top = Edge.TOP.getCoordinate();
        final float right = Edge.RIGHT.getCoordinate();
        final float bottom = Edge.BOTTOM.getCoordinate();    
        mPressedHandle = HandleUtil.getPressedHandle(x, y, left, top, right, bottom, mHandleRadius);    
        if (mPressedHandle == null)
            return false;
        mTouchOffset = HandleUtil2.getOffset(mPressedHandle, x, y, left, top, right, bottom);

        invalidate();
        return true;
    }

你想要的完全可以通过这个库来实现

我对@Nikola Despotoski的答案有一些补充。 首先,您不必将R.layout.crop_image_视图中的ImageView更改为PhotoView,因为PhotoView逻辑可以简单地作为新的PhotoViewAttacher(mImageView)附加在代码中

同样在默认逻辑中,CropView的覆盖大小仅在初始化时根据imageView位图大小进行计算。因此,这对我们来说是不合适的逻辑,因为我们根据需要通过触摸改变位图大小。因此,我们应该更改CropOverlayView中存储的位图大小,并在每次更改主图像时使其无效

最后是一个范围,用户可以根据图像大小进行正常裁剪,但如果我们将图像放大,它可以超出屏幕边界,因此用户可以将裁剪视图移到边界之外,这是不正确的。因此,我们也应该处理这种情况,并提供限制

以及这三个问题的相应代码部分: 在CropImageView中:

 private void init(Context context) {

    final LayoutInflater inflater = LayoutInflater.from(context);
    final View v = inflater.inflate(R.layout.crop_image_view, this, true);

    mImageView = (ImageView) v.findViewById(R.id.ImageView_image);

    setImageResource(mImageResource);
    mCropOverlayView = (CropOverlayView) v.findViewById(R.id.CropOverlayView);
    mCropOverlayView.setInitialAttributeValues(mGuidelines, mFixAspectRatio, mAspectRatioX, mAspectRatioY);
    mCropOverlayView.setOutlineTouchEventReceiver(mImageView);

    final PhotoViewAttacher photoAttacher = new PhotoViewAttacher(mImageView);
    photoAttacher.setOnMatrixChangeListener(new PhotoViewAttacher.OnMatrixChangedListener() {
        @Override
        public void onMatrixChanged(RectF imageRect) {
        final Rect visibleRect = ImageViewUtil.getBitmapRectCenterInside(photoAttacher.getVisibleRectangleBitmap(), photoAttacher.getImageView());

        imageRect.top = Math.max(imageRect.top, visibleRect.top);
        imageRect.left = Math.max(imageRect.left, visibleRect.left);
        imageRect.right = Math.min(imageRect.right, visibleRect.right);
        imageRect.bottom = Math.min(imageRect.bottom, visibleRect.bottom);

        Rect bitmapRect = new Rect();
        imageRect.round(bitmapRect);

        mCropOverlayView.changeBitmapRectInvalidate(bitmapRect);
        }
    });
}
在CropOverlayView中:

@Override
public boolean onTouchEvent(MotionEvent event) {

    // If this View is not enabled, don't allow for touch interactions.
    if (!isEnabled()) {
        return false;
    }

    switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:
            return onActionDown(event.getX(), event.getY());

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            getParent().requestDisallowInterceptTouchEvent(false);
            return onActionUp();

        case MotionEvent.ACTION_MOVE:
            boolean result = onActionMove(event.getX(), event.getY());
            getParent().requestDisallowInterceptTouchEvent(true);
            return result;

        default:
            return false;
    }
}

public void changeBitmapRectInvalidate(Rect bitmapRect) {
    mBitmapRect = bitmapRect;
    invalidate();
}

private boolean onActionDown(float x, float y) {

    final float left = Edge.LEFT.getCoordinate();
    final float top = Edge.TOP.getCoordinate();
    final float right = Edge.RIGHT.getCoordinate();
    final float bottom = Edge.BOTTOM.getCoordinate();

    mPressedHandle = HandleUtil.getPressedHandle(x, y, left, top, right, bottom, mHandleRadius);

    if (mPressedHandle == null){
        return false;
    }

    // Calculate the offset of the touch point from the precise location
    // of the handle. Save these values in a member variable since we want
    // to maintain this offset as we drag the handle.
    mTouchOffset = HandleUtil.getOffset(mPressedHandle, x, y, left, top, right, bottom);

    invalidate();
    return true;
}

/**
 * Handles a {@link MotionEvent#ACTION_UP} or
 * {@link MotionEvent#ACTION_CANCEL} event.
 * @return true if event vas handled, else - false
 */
private boolean onActionUp() {

    if (mPressedHandle == null)
        return false;

    mPressedHandle = null;

    invalidate();
    return true;
}

/**
 * Handles a {@link MotionEvent#ACTION_MOVE} event.
 * 
 * @param x the x-coordinate of the move event
 * @param y the y-coordinate of the move event
 */
private boolean onActionMove(float x, float y) {

    if (mPressedHandle == null)
        return false;

    // Adjust the coordinates for the finger position's offset (i.e. the
    // distance from the initial touch to the precise handle location).
    // We want to maintain the initial touch's distance to the pressed
    // handle so that the crop window size does not "jump".
    x += mTouchOffset.first;
    y += mTouchOffset.second;

    // Calculate the new crop window size/position.
    if (mFixAspectRatio) {
        mPressedHandle.updateCropWindow(x, y, mTargetAspectRatio, mBitmapRect, mSnapRadius);
    } else {
        mPressedHandle.updateCropWindow(x, y, mBitmapRect, mSnapRadius);
    }
    invalidate();
    return true;
}

为了获得正确的裁剪图像,您应该使用@Nikola Despotoski答案的第二部分,谢谢大家。使用上面的答案,使用Photoview和Cropper库可以实现这一点。添加了从相机或多媒体资料中拾取图像的选项。在Github上共享项目。在项目中添加了apk文件。使用真实设备测试摄像机,因为模拟器不能很好地处理摄像机。这是我的项目的链接


在我的例子中,作物选择位置和大小是固定的,图像可以移动和缩放。你好,尼古拉,回答得很好。我想问一下,你到底是如何裁剪缩放后的图像的。因为我无法准确地得到比例。我尝试的方法之一是使用PhotoViewAttacher类中的getscale函数跟踪图像缩放,并在cropImageView中的getcroppedImage中应用这些缩放。“你能告诉我你是怎么做到的吗?”凯萨里对我迟来的回复表示抱歉。请检查我的上一次编辑,我添加了一些与裁剪相关的方法。@NikolaDespotoski嗨,我也有同样的要求,我尝试了你的代码,但它不起作用。我不知道到底是什么问题。你能分享你的密码吗?@NikolaDespotoski我们能先修好盒子吗?我试过上面的代码块,它是有效的。但在我的场景中,盒子的宽度和高度是固定的。我们能做到吗?@NikolaDespotoski我也使用了相同的库进行裁剪,我面临着一个问题。我会解释的。我正在使用矩阵对图像应用校正在应用校正后,我无法在整个图像中移动cropoverlay视图,即使我也将cropoverlay更改为最小值。我想在整个图像中移动cropoverlay。我怎样才能解决这个问题?请帮助我..谢谢..在CropOverlayView的setOutlineTouchEventReceiver(ImageView ImageView)中需要写什么?@Ramesh Akula除了默认的库代码外,什么都没有。public void setOutlineTouchEventReceiver(查看outlineTouchEventReceiver){this.outlineTouchEventReceiver=outlineTouchEventReceiver;}谢谢,我终于让它工作了。