Android App开发实战项目之模仿美图秀秀的抠图工具(附源码 简单易懂 可直接使用)

news2025/1/14 3:59:32

需要图片集和源码请点赞关注收藏后评论区留言~~~

所谓抠图神器,就是从一副图片中扣出用户想要的某块区域

一、需求描述

美图的修图功能如此强大,离不开专业的图片加工技术,抠图便是其中重要的一项功能。在App界面底部点击抠图按钮,再选择下方的形状按钮就会出现等待抠图的目标区域,然后通过手指触摸挪动方框,也可两指并用缩放或者旋转方框,调整方框大小以及角度后,再点击右下角的三点按钮,选择弹出菜单中的存为贴纸,就可以在贴纸功能中查看已经扣好的图片。

示意图如下

 

 二、功能分析

抠图工具通过对图像进行平移,缩放,旋转等操作把图像的某个区域扣下来,抠图工具要提供打工图片和保存图片两种操作,其中打开图片支持从手机相册选取待加工的原始图片,保存图片支持把抠出来的图像保存到存储卡

打开原始图片后 工具界面进入抠图模式 主界面上没有任何控制按钮,抠哪个区域完全靠手势操作 需要实现的手势处理有以下五种

1:挪动高亮区域的手势

2:调整高亮区域边界的手势

3:挪动图片的手势

4:缩放图片的手势

5:旋转图片的手势

 三、效果展示

点击下方按钮后即可进入抠图软件的界面

点击右上角的三角按钮 可以打开和保存图片

 

 可以对图像进行放缩 旋转等操作

 四、代码

java类

package com.example.event;

import com.example.event.util.BitmapUtil;
import com.example.event.util.DateUtil;
import com.example.event.widget.BitmapView;
import com.example.event.widget.MeituView;
import com.example.event.widget.MeituView.ImageChangetListener;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import android.os.Environment;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

public class MeituActivity extends Activity implements ImageChangetListener {
    private final static String TAG = "MeituActivity";
    private int CHOOSE_CODE = 3; // 只在相册挑选图片的请求码
    private MeituView mv_content; // 声明一个美图视图对象
    private BitmapView bv_content; // 声明一个位图视图对象
    private TextView tv_hint; // 声明一个文本视图对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
    private void initView() {
        Toolbar tl_head = findViewById(R.id.tl_head);
        tl_head.setTitle("抠图工具");
        setSupportActionBar(tl_head); // 替换系统自带的ActionBar
        // 设置工具栏左侧导航图标的点击监听器
        tl_head.setNavigationOnClickListener(view -> finish());
        mv_content = findViewById(R.id.mv_content);
        mv_content.setImageChangetListener(this); // 设置美图视图的图像变更监听器
        bv_content = findViewById(R.id.bv_content);
        bv_content.setDrawingCacheEnabled(true); // 开启位图视图的绘图缓存
        tv_hint = findViewById(R.id.tv_hint);
    }

    private void setSupportActionBar(Toolbar tl_head) {
    }

    // 刷新图像展示
    private void refreshImage(boolean is_first) {
        Bitmap bitmap = bv_content.getDrawingCache(); // 从绘图缓存获取位图对象
        mv_content.setOrigBitmap(bitmap); // 设置美图视图的原始位图
        if (is_first) { // 首次打开
            int left = bitmap.getWidth() / 4;
            int top = bitmap.getHeight() / 4;
            // 设置美图视图的位图边界
            mv_content.setBitmapRect(new Rect(left, top, left * 2, top * 2));
        } else { // 非首次打开
            // 设置美图视图的位图边界
            mv_content.setBitmapRect(mv_content.getBitmapRect());
        }
    }

    // 在图片平移时触发
    @Override
    public void onImageTraslate(int offsetX, int offsetY, boolean bReset) {
        bv_content.setOffset(offsetX, offsetY, bReset); // 设置位图视图的偏移距离
        refreshImage(false); // 刷新图像展示
    }

    // 在图片缩放时触发
    @Override
    public void onImageScale(float ratio) {
        bv_content.setScaleRatio(ratio, false); // 设置位图视图的缩放比率
        refreshImage(false); // 刷新图像展示
    }

    // 在图片旋转时触发
    @Override
    public void onImageRotate(int degree) {
        bv_content.setRotateDegree(degree, false); // 设置位图视图的旋转角度
        refreshImage(false); // 刷新图像展示
    }

    // 在图片点击时触发
    @Override
    public void onImageClick() {}

    // 在图片长按时触发
    @Override
    public void onImageLongClick() {}

    // 在创建选项菜单时调用
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_meitu, menu);
        return true;
    }

    // 在选中菜单项时调用
    @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);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (resultCode == RESULT_OK && requestCode == CHOOSE_CODE) { // 从相册返回
            if (intent.getData() != null) { // 从相册选择一张照片
                Uri uri = intent.getData(); // 获得已选择照片的路径对象
                // 根据指定图片的uri,获得自动缩小后的位图对象
                Bitmap bitmap = BitmapUtil.getAutoZoomImage(this, uri);
                bv_content.setImageBitmap(bitmap); // 设置位图视图的位图对象
                refreshImage(true); // 刷新图像展示
                tv_hint.setVisibility(View.GONE);
            }
        }
    }

}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/tl_head"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/blue_light"
        app:navigationIcon="@drawable/icon_back" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <com.example.event.widget.BitmapView
            android:id="@+id/bv_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/white"
            android:scaleType="centerCrop" />

        <com.example.event.widget.MeituView
            android:id="@+id/mv_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <TextView
            android:id="@+id/tv_hint"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="请点击右上角的三点图标,先打开图片文件,再开始抠图操作"
            android:textColor="@color/black"
            android:textSize="17sp" />

    </RelativeLayout>
</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/19324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql查询:行转列与列转行

目录 1. 行转列 1.1 什么是行转列 1.2 准备工作 1.3 行转列的实现原理 2. 列转行 2.1 什么是列转行 2.2 准备工作 2.3 列转行的实现原理 3. 总结 1. 行转列 1.1 什么是行转列 从表面理解&#xff0c;就是表里存储在行上的数据&#xff0c;在查询结果里展示在列上面。举…

基于stm32单片机随机数自动摇号抽奖系统

资料编号&#xff1a;099 下面是相关功能视频演示&#xff1a; 99-基于stm32单片机随机数自动摇号抽奖系统&#xff08;源码仿真全套资料&#xff09;采用stm32单片机作为主控&#xff0c;LCD1602显示&#xff0c;通过按键来重置生成随机数&#xff0c;类似于摇号和抽奖系统 …

如何实现广义的元交易(Meta Transaction)

在合约内启用元交易是一个强大的补充。要求用户持有ETH来支付Gas一直以来都是而且仍然是新用户进入的最大挑战之一。如果只是简单的点击,谁知道现在会有多少人在使用以太坊? 但有时,解决方案可以在你的合约中加入元交易能力。实现起来可能比你想象的要容易。 什么是元交易?…

(带你分分种学会linux的文件类型和软硬链接)linxu的文件类型(硬链接和软链接详解)

文章目录一&#xff0c;时区部分1&#xff0c;时区状态的展示2,列出所有时区二&#xff0c;linux的文件类型1&#xff0c;列出目录和文件类型2&#xff0c;查看文件类型和状态并且修改用户密码3&#xff0c;文件类型表4&#xff0c;文件的三次修改时间三&#xff0c;linux的软硬…

记录Manjaro Linux安装nvidia显卡驱动失败的经历

我的Manjaro: 2022.11.19的最新系统&#xff0c;通过manjaro-kde-21.3.7-220816-linux515.iso安装&#xff0c;然后通过pacman -Syyu更新的我的设备&#xff1a; CPU intel 12700H&#xff0c;大小核架构&#xff0c;使用系统默认的linux5.15内核会发现启动firefox浏览器的速度…

LeetCode 142. 环形链表 II

题目链接&#xff1a;https://leetcode.cn/problems/linked-list-cycle-ii/ 思路如下&#xff1a; 用两个指针 fast, slow 同时从起点开始走&#xff0c;fast 每次走两步&#xff0c;slow 每次走一步。 如果过程中 fast 走到 null&#xff0c;则说明不存在环。否则当 fast 和…

基于Python3.6配置开发环境

前言 最近在CTF上遇到了瓶颈&#xff0c;本人不会python&#xff0c;导致有些题做不出来。而且python的实用性实在太强了&#xff0c;所以就找了一个培训机构的课程边学习边实践。现将经验总结&#xff0c;分享给大家。 正文 1、配置python安装路径和环境变量 怎么下载pyth…

【Acwing】最短路+二分 通信线路

340. 通信线路 - AcWing题库 题意&#xff1a; 思路&#xff1a; 首先因为贪心&#xff0c;免费升级的肯定是最贵的那几根 因此这道题可以简化为&#xff1a; 给定一张图&#xff0c;求结点1到结点N的所有路径中第K1大的边权的最小值 可以发现我们要求剩下的边中最大值的最…

Qt程序打包成安装包exe

本章介绍把Qt开发的程序打包成安装包的方法&#xff0c;程序打包成install.exe&#xff0c;可双击安装&#xff0c;有默认安装路径&#xff0c;也可以选择安装目录&#xff0c;自动生成桌面快捷方式和开始菜单选项&#xff0c;可以在操作系统–>设置–>应用程序里看到&am…

【响应式布局】使用 flexbox 实现简单响应式布局

场景和需求 屏幕 > 540px&#xff0c;前两个部分展示两列&#xff0c;屏幕 < 540px&#xff0c;前两个部分展示一列屏幕 < 540px&#xff0c;第一部分要反转展示屏幕 > 769px&#xff0c;第三个部分展示三列屏幕 < 769px&#xff0c;第三个部分展示一列 效果图…

java计算机毕业设计自习室管理系统(附源码、数据库)

java计算机毕业设计自习室管理系统&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目…

java进阶编程思想(七天)

编程核心思想基本框架第一天&#xff08;继承&#xff09;第二天&#xff08;抽象和接口&#xff09;第四天&#xff08;多态、DATA、Object、正则&#xff09;第五天&#xff08;遍历、Set、斗地主游戏案例&#xff09;第六天第七天b站链接:懂不懂我意思明不明白哈哈哈基本框架…

声明式事务@Transactional

事务 事务管理在 Web 系统开发中是非常重要的&#xff0c;可以在一定程度上保证数据的一致性。Spring提供了非常优秀的事务管理机制&#xff0c;主要分为&#xff1a; 编程式事务声明式事务 编程式事物 在代码中手动的管理事务的提交、回滚等操作&#xff0c;代码侵入性比较…

【毕业设计】66-基于物联网技术的智能家居系统的研究(仿真工程、原理图、低重复率设计文档、答辩PPT)

【毕业设计】66-基于物联网技术的智能家居系统的研究&#xff08;仿真工程、原理图、低重复率设计文档、答辩PPT&#xff09; 文章目录【毕业设计】66-基于物联网技术的智能家居系统的研究&#xff08;仿真工程、原理图、低重复率设计文档、答辩PPT&#xff09;资料下载链接任务…

代码随想录算法训练营第58、59天 | 739. 每日温度 496.下一个更大元素 I 503.下一个更大元素II 42. 接雨水

代码随想录系列文章目录 单调栈篇 文章目录代码随想录系列文章目录739. 每日温度496.下一个更大元素 I503.下一个更大元素II42.接雨水双指针解法dp解法单调栈解法单调栈具体的处理逻辑739. 每日温度 题目链接 暴力解法&#xff0c;双指针&#xff0c;超时, 因为数据长度是100…

【考研复试】计算机专业考研复试英语常见问题一(家庭/家乡/学校篇)

相关链接&#xff1a; 【考研复试】计算机专业考研复试英语常见问题一&#xff08;家庭/家乡/学校篇&#xff09;【考研复试】计算机专业考研复试英语常见问题二&#xff08;研究方向/前沿技术/本科毕设篇&#xff09;【考研复试】计算机专业考研复试英语常见问题三&#xff0…

Spring Boot+Vue3前后端分离实战wiki知识库系统之用户管理单点登录

用户表设计与代码生成 用户表设计 生成持久层代码 同样是在gennerator.xml中添加并执行&#xff1a; 完成用户表基本增删查改功能 首先我们应该改造controller 接着是service,service的查询条件要根据loginname来查找&#xff1a; 接着修改三个实体&#xff1a; 注…

Windows OpenGL 图像单色

目录 一.OpenGL 图像单色 1.原始图片2.效果演示 二.OpenGL 图像单色源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 OpenGL ES 学习…

基于stm32单片机的电压报警系统Proteus仿真

资料编号&#xff1a;112 下面是相关功能视频演示&#xff1a; 112-基于stm32单片机的电压报警系统Proteus仿真&#xff08;源码仿真全套资料&#xff09;功能介绍&#xff1a; 采用stm32单片机的12位ADC采集电压&#xff0c;当电压值超过设置值蜂鸣器和LED产生报警&#xff…

知识蒸馏 | YOLOv7知识蒸馏实战篇 | 2/2*

知识蒸馏 | YOLOv7知识蒸馏实战篇 | 2/2* 文章目录 知识蒸馏 | YOLOv7知识蒸馏实战篇 | 2/2*0. 环境准备1. 训练学生网络2. 训练教师网络3. 知识蒸馏训练4. YOLOv7官方项目修改说明5. 源码0. 环境准备 终端键入: pip install -r requirements.txt -i https://pypi.tuna.tsin…