美文网首页
Android按键事件及手势事件(三)实战项目:仿美图秀秀的抠图

Android按键事件及手势事件(三)实战项目:仿美图秀秀的抠图

作者: 古早味蛋糕 | 来源:发表于2023-02-20 19:06 被阅读0次

    一、简介:
    抠图神器,就是从一幅图片中抠出用户想要的某块区域。就像在花店里卖花,先适当修剪花束,再配上一些包装,看起来就很漂亮,不愁用户不喜欢。如何从现有图片抠出指定区域着实是一门学问,抠大还是抠小还得调整合适的角度,全凭用户两根灵活的手指头
    1、需求描述
    前段时间主打图片美颜的美图秀秀大受欢迎,甚至火到了国外。美图的修图功能如此强大,离不开专业的图片加工技术,抠图便是其中重要的一项。点击美图秀秀首页的图片美化按钮,到相册中选择一张图片,就打开了图片加工界面,在界面底部左滑拉出抠图按钮并点击,再选择下方的形状按钮,此时图片中央出现一个方框,这个方框就是待抠的目标区域。
    2、功能分析
    抠图工具通过对图像进行平移、缩放、旋转等操作把图像的某个区域抠下来。抠图工具要提供打开图片和保存图片两种操作,其中打开图片支持从手机相册选取待加工的原始图片、保存图片支持把抠出来的图像保存到存储卡。
    打开原始图片后,工具界面进入抠图模式,主界面上没有任何控制按钮,抠哪块区域完全靠手势操作。需要实现的手势处理有以下5种。
    ● 挪动高亮区域的手势:点击高亮区域内部,再滑动手势,即可将该区域拖曳至指定位置。
    ● 调整高亮区域边界的手势:点击高亮区域边界,再滑动手势,即可将边界拉至指定位置。
    ● 挪动图片的手势:点击高亮区域外部(阴影部分),然后滑动手势,即可将整张图片拖曳至指定位置。
    ● 缩放图片的手势:两只手指同时按压屏幕,然后一起往中心点靠拢或彼此远离中心点,即可实现图片的缩小和放大操作。
    ● 旋转图片的手势:两个手指同时按压屏幕,然后围绕中心点一起顺时针或逆时针转动,即可实现图片的旋转操作。
    下面是自定义的美图视图中关于缩放与旋转手势的判断代码:完整代码MeituView

    if (mListener != null) {
           // 上次两个触摸点之间的距离
          float preWholeDistance = PointUtil.distance(mFirstLastPos, mSecondLastPos);
          // 当前两个触摸点之间的距离
          float nowWholeDistance = PointUtil.distance(firstP, secondP);
          // 主要点在前后两次落点之间的距离
         float primaryDistance = PointUtil.distance(firstP, mFirstLastPos);
         // 次要点在前后两次落点之间的距离
         float secondaryDistance = PointUtil.distance(secondP, mSecondLastPos);
         if (Math.abs(nowWholeDistance - preWholeDistance) >
                                (float) Math.sqrt(2) / 2.0f * (primaryDistance + secondaryDistance)) {
                 // 倾向于在原始线段的相同方向上移动,则判作缩放图像
                 // 触发图像变更监听器的缩放图像动作
                 mListener.onImageScale(nowWholeDistance / preWholeDistance);
         } else { // 倾向于在原始线段的垂直方向上移动,则判作旋转图像
                  // 计算上次触摸事件的旋转角度
                  int preDegree = PointUtil.degree(mFirstLastPos, mSecondLastPos);
                   // 计算本次触摸事件的旋转角度
                  int nowDegree = PointUtil.degree(firstP, secondP);
                  // 触发图像变更监听器的旋转图像动作
                  mListener.onImageRotate(nowDegree - preDegree);
         }
    

    根据落点坐标与矩形边界的相对位置,决定本次拖曳动作的类型的代码如下:完整代码MeituView

    // 根据落点坐标与矩形边界的相对位置,决定本次拖曳动作的类型
    private int getDragMode(float x, float y) {
        int left = mRect.left;
        int top = mRect.top;
        int right = mRect.left + mRect.right;
        int bottom = mRect.top + mRect.bottom;
        if (Math.abs(x - left) <= mInterval && Math.abs(y - top) <= mInterval) {
            return DRAG_LEFT_TOP; // 拖动矩形边界的左上角
        } else if (Math.abs(x - right) <= mInterval && Math.abs(y - top) <= mInterval) {
            return DRAG_RIGHT_TOP; // 拖动矩形边界的右上角
        } else if (Math.abs(x - left) <= mInterval && Math.abs(y - bottom) <= mInterval) {
            return DRAG_LEFT_BOTTOM; // 拖动矩形边界的左下角
        } else if (Math.abs(x - right) <= mInterval && Math.abs(y - bottom) <= mInterval) {
            return DRAG_RIGHT_BOTTOM; // 拖动矩形边界的右下角
        } else if (Math.abs(x - left) <= mInterval && y > top + mInterval && y < bottom - mInterval) {
            return DRAG_LEFT; // 拖动矩形边界的左边缘
        } else if (Math.abs(x - right) <= mInterval && y > top + mInterval && y < bottom - mInterval) {
            return DRAG_RIGHT; // 拖动矩形边界的右边缘
        } else if (Math.abs(y - top) <= mInterval && x > left + mInterval && x < right - mInterval) {
            return DRAG_TOP; // 拖动矩形边界的上边缘
        } else if (Math.abs(y - bottom) <= mInterval && x > left + mInterval && x < right - mInterval) {
            return DRAG_BOTTOM; // 拖动矩形边界的下边缘
        } else if (x > left + mInterval && x < right - mInterval
                && y > top + mInterval && y < bottom - mInterval) {
            return DRAG_WHOLE; // 拖动整个矩形边界框
        } else if (x + mInterval < left || x - mInterval > right || y + mInterval < top || y - mInterval > bottom) {
            return IMAGE_TRANSLATE; // 平移图像
        } else {
            return DRAG_NONE; // 无拖曳动作
        }
    }
    

    其中需要实现内容获取动作的意图,然后进行保存抠取图片,部分代码:完整代码MeituActivity

    // 在选中菜单项时调用
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_file_open) { // 点击了“打开文件”
            // 创建一个内容获取动作的意图(准备跳到系统相册)
            Intent albumIntent = new Intent(Intent.ACTION_GET_CONTENT);
            albumIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); // 是否允许多选
            albumIntent.setType("image/*"); // 类型为图像
            startActivityForResult(albumIntent, CHOOSE_CODE); // 打开系统相册
        } else if (item.getItemId() == R.id.menu_file_save) { // 点击了“保存文件”
            Bitmap bitmap = mv_content.getCropBitmap(); // 获取美图视图处理后的位图
            // 生成图片文件的保存路径
            String path = String.format("%s/%s.jpg",
                    getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),
                    DateUtil.getNowDateTime());
            BitmapUtil.saveImage(path, bitmap); // 把位图保存为图片文件
            BitmapUtil.notifyPhotoAlbum(this, path); // 通知相册来了张新图片
            Toast.makeText(this, "已保存抠好的图片 "+path, Toast.LENGTH_SHORT).show();
        }
        return super.onOptionsItemSelected(item);
    }
    

    二、效果展示
    1、打开抠图App,点击右上角的三点图标,弹出读写图片文件的菜单,如图【读写图片文件的菜单】所示


    读写图片文件的菜单.png

    2、选择菜单项“打开图片”,打开待加工的图片文件,初始界面如图【原始图片】所示。


    原始图片.png
    3、接着拖动原始图片与高亮区域,并适当放大移动图片,使白兔灯位于高亮区域中间部,如图【抠图裁剪过程】全部手势调整结束,点击保存图片。
    抠图裁剪过程.png
    3、完成抠图打开相册查看效果如图【抠图裁剪完成后】所示。
    抠图裁剪完成后.png

    相关文章

      网友评论

          本文标题:Android按键事件及手势事件(三)实战项目:仿美图秀秀的抠图

          本文链接:https://www.haomeiwen.com/subject/uxmgkdtx.html