今晚写了一个比较有意思的控件 ,也是项目需要,项目做一个OCR的图片识别 ,上午老板提出一个需求 ,增加触摸翻译的功能 。我觉得比较有趣,就画了2个多小时吧这个控件写出来并分享出来,网上搜了一下,目前还没有类似的控件的功能

代码目前考虑的范围不够全面,等我软件优化完了,这里修复一下,给大家看看思路

后来写了一个升级版的,解决屏幕不适配的问题 :

http://blog.csdn.net/fkgjdkblxckvbxbgb/article/details/77801086

先介绍下百度翻译的OCR功能 :

1:拍照,拿到照片,识别里面的文字 ,根据识别的文字进行翻译 。

2:截图翻译,就是根据截取的屏幕内容对内容进行翻译,准确率更高

3:涂抹翻译,比截图翻译更精确,正对性更强

今天要写的就是第三种 涂抹翻译,原理不难 ,

有点类似刮刮卡的功能 ,然后涂抹区域的bitmap进行重绘制保存

先看效果图 ,虚拟机没看不出来效果,需要用真机,因为有用到SD卡读写权限 ,最后返回的是图片的存储路径

我  

我的代码都比较容易看懂 ,没有那么多的嵌套封装,注释的很清楚,

package com.reeman.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.reeman.view.entity.TouchPoint;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class XfermodeView extends View {private Bitmap mBgBitmap;
    Bitmap mFgBitmap;
    private Paint mPaint;
    private Canvas mCanvas;
    private Path mPath;
    int FINGER_WIDTH = 40;

    public XfermodeView(Context context) {this(context, null);
    }public XfermodeView(Context context, AttributeSet attrs) {this(context, attrs, 0);
    }public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        init();
    }@Override
    protected void onDraw(Canvas canvas) {canvas.drawBitmap(mBgBitmap, 0, 0, null);
        canvas.drawBitmap(mFgBitmap, 0, 0, null);
    }private void init() {mPaint = new Paint();
        mPaint.setAlpha(0);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeWidth(80);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPath = new Path();
//        mBgBitmap = BitmapFactory.decodeResource(getResources(), imageId);
//        mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
//        mCanvas = new Canvas(mFgBitmap);
//        mCanvas.drawColor(BLACK_HALF_COLOR);
    }int BLACK_HALF_COLOR = 0x89000000;
    float startX, startY, endX, endY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:startX = event.getX();
                startY = event.getY();
                listPoint.clear();
                listPoint.add(new TouchPoint(event.getX(), event.getY()));
                Log.i("point", "====添加的点==" + startX + "/" + startY);
                mPath.reset();
                mPath.moveTo(startX, startY);
                break;
            case MotionEvent.ACTION_MOVE:listPoint.add(new TouchPoint(event.getX(), event.getY()));
                mPath.lineTo(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_UP:endX = event.getX();
                endY = event.getY();
                listPoint.add(new TouchPoint(event.getX(), event.getY()));
                changePoints();
                Log.i("point", "====添加的点==" + endX + "/" + endY);
                break;
        }mCanvas.drawPath(mPath, mPaint);
        invalidate();
        return true;
    }List<TouchPoint> listPoint = new ArrayList<>();
    List<Float> pointX = new ArrayList<>();
    List<Float> pointY = new ArrayList<>();

    /***
     * 计算手指滑动的区域
     */
    private void changePoints() {pointX.clear();
        pointY.clear();
        for (int i = 0; i < listPoint.size(); i++) {pointX.add(listPoint.get(i).getPointX());
            pointY.add(listPoint.get(i).getPointY());
        }/***
         * 这里因为用户会反复的涂抹,所以吧所有的移动点添加到集合中,然后对所有的点取值
         * 取出手指的最大范围和最小范围,
         */
        float minX = Collections.min(pointX);
        float minY = Collections.min(pointY);
        float maxX = Collections.max(pointX);
        float maxY = Collections.max(pointY);
        /***
         * 这里加上笔触的宽度
         */
        minX = minX - FINGER_WIDTH;
        minY = minY - FINGER_WIDTH;
        maxX = maxX + FINGER_WIDTH;
        maxY = maxY + FINGER_WIDTH;
        if (minX < 0) {minX = 0;
        }if (minY < 0) {minY = 0;
        }if (maxX > MainActivity.width) {maxX = MainActivity.width;
        }if (maxY > MainActivity.height) {maxY = MainActivity.height;
        }//这是手指绘制的区域
        RectF rect = new RectF(minX, minY, maxX, maxY);
        saveBitmap(rect);
    }/***
     * 保存图片到本地
     * @param rect
     */
    private void saveBitmap(RectF rect) {try {Log.i("point", "====保存的尺寸==" + rect.left + "/" + rect.top + "  ///" + rect.right + "/" + rect.bottom);
            Bitmap bmp = Bitmap.createBitmap((int) rect.width(), (int) rect.height(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bmp);
            canvas.translate(-(rect.left), -(rect.top));
            canvas.drawColor(Color.WHITE);
            canvas.drawBitmap(mBgBitmap, 0, 0, null);
            canvas.save(Canvas.ALL_SAVE_FLAG);
            canvas.restore();
            File file = new File("/sdcard//test.png");
            if (!file.exists()) {file.createNewFile();
            }FileOutputStream fos = new FileOutputStream(file.getPath());
            bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.close();
            listener.touchBackUrl(file.getPath());
        } catch (Exception e) {listener.touchCutError(e.toString());
            e.printStackTrace();
        }}public void translateImage(int imageId) {mBgBitmap = BitmapFactory.decodeResource(getResources(), imageId);
        mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mFgBitmap);
        mCanvas.drawColor(BLACK_HALF_COLOR);
    }TouchCutListener listener;

    public void setOnTouchCutListener(TouchCutListener listener) {this.listener = listener;
    }public interface TouchCutListener {//        void touchPoint(float left, float top, float right, float bottom);
        void touchBackUrl(String filePath);

        void touchCutError(String error);
    }}

布局代码,切记,用真机看效果,虚拟机看不到效果

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.reeman.view.MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:background="@color/colorAccent">

        <ImageView
            android:id="@+id/iv_bitma"
            android:layout_width="300dp"
            android:layout_height="150dp" />

    </RelativeLayout>

    <com.reeman.view.XfermodeView
        android:id="@+id/modeview"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_alignParentBottom="true"
        android:layout_centerInParent="true" />

</RelativeLayout>

主界面代码,为什么要使用真机测试,主界面代码一目了然

package com.reeman.view;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity implements XfermodeView.TouchCutListener {static int width;
    static int height;
    XfermodeView modeview;
    private ImageView iv_bitma;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        WindowManager wm = this.getWindowManager();
        width = wm.getDefaultDisplay().getWidth();
        height = wm.getDefaultDisplay().getHeight();
        setContentView(R.layout.activity_main);

        iv_bitma = (ImageView) findViewById(R.id.iv_bitma);
        modeview = (XfermodeView) findViewById(R.id.modeview);
        modeview.translateImage(R.mipmap.test5);
        modeview.setOnTouchCutListener(this);
    }@Override
    public void touchBackUrl(String filePath) {Bitmap map = BitmapFactory.decodeFile(filePath);
        iv_bitma.setImageBitmap(map);
    }@Override
    public void touchCutError(String error) {}
}

canvas 对图片进行涂抹,涂抹区域保存图片存入本地相关推荐

  1. 小程序画布插入证件照换低并保存图片到本地相册(注意必须保证canvas绘制完成才导出图片)

    这里是将证件照图片写死,背景颜色写死,尺寸写死,大家可按需进行改进 首先最注意的地方是保存图片时,必须保证canvas绘制完成 官网教程(最好是配合来看) 第一步:在 WXML 中添加 canvas ...

  2. 使用canvas对图片进行裁切

    canvas canvas 又叫做画布,是 HTML5 新增内容,可以使用js脚本在其中绘制图像的元素. 常用于制作网页上的一些毕竟炫酷的前端特效. 可以完成图片处理.动画渲染.图表渲染等操作. 最近 ...

  3. vue保存图片到本地

    首先呢我们要,安装一下依赖: npm install html2canvas --save 接下来我们就直接上代码了 <van-tab title="签到二维码">&l ...

  4. IOS15保存图片至本地相机胶卷

    IOS15保存图片至本地相机胶卷 主要源码在这: #import "ViewController.h" #define imageURL @"https://wx4.si ...

  5. url抓取图片存入本地 c#

    一:url抓取图片存入本地  c# 二:将图片和文字存入PDF c# 给大家提供程序下载地址:https://download.csdn.net/download/us2019/12888680 将u ...

  6. iOS 使用钥匙串将用户密码存入本地

    在 iOS 开发中,用户一般注册时候,APP会将用户的用户名和密码直接保存到本地,便于用户下次直接进行登录. 这样就会牵扯到一个问题,用户的密码不能以明文的形式存储在本地,使用钥匙串进行保存用户的密码 ...

  7. 捕获Camera并保存图片到本地(照相功能) -samhy

    Flex博文 捕获Camera并保存图片到本地(照相功能) -samhy 作者:admin 日期:2010-07-12 字体大小: 小 中 大 捕获Camera并保存图片到本地(照相功能) 这几天对C ...

  8. scrapy python3.8_银狐DevNet-网络运维Python初篇(四)netmiko抓取华为网络配置并存入本地...

    1.训练场景:读取excel中设备IP地址,通过Netmiko抓取设备配置,并存入本地 上一小节我们通过excel得到设备IP地址,并用Netmiko批量抓取设备配置并打印出来,真实运维场景我们需要把 ...

  9. 将CSDN600W用户及密码帐号存入本地MySql数据库

    有感于密码文本文件太大,索性将其存入本地MySql,其中有6个账户因为文件编码问题不知密码,将其删去. 1 import java.io.BufferedReader;  2 import java. ...

  10. python save保存图片到本地_python爬取网站上的图片并保存到本地

    1.导入需要的模块requests,BeautifulSoup,os(用于文件读写). 2.创建一个类,并初始化. class BeautifulPicture: def __init__(self) ...

最新文章

  1. pdo mysql bindparam_pdo连接mysql prepare,bindParam插入数据
  2. vnc用户名 查看linux_linux 查看vnc服务器
  3. Linux ps aux指令詳解--转
  4. android跑步软件,手机跑步软件哪个好_安卓手机跑步记录软件_手机跑步app【最新】-太平洋电脑网...
  5. DDD理论学习系列(12)-- 仓储
  6. Node.js 应用故障排查手册 —— 综合性 GC 问题和优化
  7. Python | Socket01 - 创建一个TCP服务器(阻塞+单线程),将TCP客户端发过来的字符串原路返回
  8. 160308_Helloworld_Console Application
  9. php7 ipv6,php将ipv4/ipv6的真实ip转换为数字
  10. ASP .NET基本概念
  11. Box2DWeb_03之Shape
  12. Linux 不同方法查看进程消耗CPU IO 等
  13. 【渝粤教育】国家开放大学2018年春季 0092-22T民法 参考试题
  14. 8 种单例模式写法,助你搞定面试!
  15. ping,python实现批量ping包工具--小案例
  16. php中smarty模板下载,Smarty模板下载|
  17. zookeeper-linux集群搭建小结
  18. qq视频转码失败怎么办_迅捷视频转换器腾讯视频转换失败如何解决?
  19. 用于单图像超分辨率的增强深度残差网络
  20. s11 day103 luffy项目结算部分+认证+django-redis

热门文章

  1. 蔡氏电路matlab仿真实代码验,基于蔡氏电路的MATLAB仿真
  2. Mybatis辅助神器-MyBatis Log Plugin,定位java中SQL问题
  3. python文字教学_【纯文本教程】大学专科的Python基础开发课?p=17
  4. Altium Designer之4层板基本规则设置
  5. 微信屏蔽网址的解决办法:366API轻松实现被微信屏蔽的网址在微信内正常访问
  6. GUI提示功控件,LVGL『Label标签控件』介绍
  7. 北京市海淀区踢球场地指南
  8. 【Vivado那些事】OOC综合方式
  9. 看精神小伙是如何智斗骗子的
  10. 把100PB数据迁移到阿里云,需要几步?