Android DIY之路 (一) 指定区域多图片合成 放大 缩小 镜像 旋转 等(转)

时间:2023-02-20 20:21:37

惯例先看效果图

Android DIY之路 (一) 指定区域多图片合成 放大 缩小 镜像 旋转 等(转)

// 注意做类似这种模板功能时候 方位由后台数据提供,这里我们用假数据 4个点 或者xy 加区域来做示例 
//一开始我们公司用的是透明盖住 操作图片 但发现 局限性较大。后来直接限定区域。将操作图片层级移到模板图上面 随意叠加 
1. 背景图上绘制操作区域,(操作区域可以不止一个,比如美图秀秀的模板) 
2. 操作区域内加上 编辑素材图的容器与编辑区域相同 
3. 添加可编辑的view(这里方便观看用的ImageView) 
4. 合成将你想要的View范围截屏(也可以去掉你不想要的那部分)

合成将你想要的View范围截屏文件

知道这个 可以简化你自己做图生成图片的 省略 很多复杂方法 所以先放出来 后面DIY也是用的最多的方法。 
flRoot.setClipChildren(false);//是否限制子View在其范围内*(注意这个属性必须放在拖动布局的爷爷布局才能生效) 
还有这个方法。使其显示View的全局哪怕被遮挡。方便用户在编辑图片的时候更清晰 
1. 
View view = 你想要合成的当前显示的view 
 Bitmap bitmap = Bitmap.createBitmap(view .getWidth(), view .getHeight(),  Bitmap.Config.ARGB_8888); 
Canvas canvas = new Canvas(bitmap); 
canvas.drawColor(Color.WHITE); 
view .draw(canvas); 
如果本例中我是ivTemps 那么整个图片就局部截屏下来了。就类似局部截屏。当然这个bitmap可以继续操作。又或者你可以把4个按钮隐藏掉后 再局部截屏 
2.

bitmap存文件
public static String saveBitmap(Context context, Bitmap bitmap) {
File file = null;
try {
file = new File(getCacheDir(), getFileName());/这是你的文件存的路径 自己定义吧
FileOutputStream os = new FileOutputStream(file);
bitmap = small(bitmap,0.5f);
mmmmmm = Bitmap.createBitmap(bitmap).copy(Bitmap.Config.ARGB_8888, true);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} return file.getPath();
}

背景图上绘制操作区域

<com.rex.refreshapp.ImagesTemplates
android:id="@+id/ivTemps"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

自定义的FrameLayout 
ImagesTemplates带的布局 View view = View.inflate(mContext, R.layout.view_images_templates, null); 
addView(view); 
简单形成一个组合控件。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/flRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF"
> <ImageView
android:id="@+id/ivBg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/> <FrameLayout
android:id="@+id/flZone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#50654786"
android:clipChildren="false"
> </FrameLayout> </FrameLayout>

操作区域内加上 编辑素材图的容器与编辑区域相同

 initBg(30, 90, 800, 1200);//背景起点 长宽
private void initBg(int x, int y, int width, int height) {
ivBg.setX(x);
ivBg.setY(y);
ivBg.setScaleType(ImageView.ScaleType.CENTER_CROP);
ivBg.setImageResource(R.mipmap.yudi);
ViewGroup.LayoutParams layoutParams = ivBg.getLayoutParams();
layoutParams.width = width;
layoutParams.height = height;
ivBg.setLayoutParams(layoutParams);
} //可多个 ----------------------- initEditZone(30, 30, 600, 800);// 展现编辑区域 虚线 和半透明 可能多个拓展
/**
* 通过起点和区域范围 得到编辑区域
*
* @param x
* @param y
* @param width
* @param height
*/
private void initEditZone(final int x, final int y, final int width, final int height) { ViewTreeObserver vto = ivBg.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() { //监听一次马上结束 if (Build.VERSION.SDK_INT < 16) {
ivBg.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
ivBg.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} Bitmap bitmap = Bitmap.createBitmap(ivBg.getWidth(), ivBg.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
ivBg.draw(canvas); Paint p = new Paint();
p.setStyle(Paint.Style.STROKE);
//设置虚线效果
p.setPathEffect(new DashPathEffect(new float[]{10, 5}, 0));
p.setStrokeWidth(3);
p.setColor(Color.parseColor("#D0104C")); Path path = new Path();
path.moveTo(x, y);
path.lineTo(x + width, y);
path.lineTo(x + width, y + height);
path.lineTo(x, y + height);
path.close();
canvas.drawPath(path, p); ivBg.setImageBitmap(bitmap); //显示编辑区域范围
flZone.setX(x + ivBg.getX());
flZone.setY(y + ivBg.getY());
FrameLayout.LayoutParams layoutParams = (LayoutParams) flZone.getLayoutParams();
layoutParams.width = width;
layoutParams.height = height; flZone.setLayoutParams(layoutParams); } } ); }
    1.

添加可编辑的view(这里方便观看用的ImageView)

当然你可以一个ImageView画4个bitmap搞定 但是后面我否定了这个方法 重写了这种比较直观的4个ImageView 作为按钮盖在主图View方法,中间的View可以替换。也方便理解,简化了后面的步骤。 
R.layout.add_img_item

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
> <ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:scaleType="centerCrop"/> <ImageView
android:id="@+id/ivLeft"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="top|left"
android:src="@mipmap/r_close"
/> <ImageView
android:id="@+id/ivRight"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="top|right"
android:src="@mipmap/r_rotate"
/> <ImageView
android:id="@+id/ivBLeft"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="bottom|left"
android:src="@mipmap/r_symmetric"
/> <ImageView
android:id="@+id/ivBRight"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="bottom|right"
android:src="@mipmap/r_zoom"
/>
</FrameLayout>
 private void initEditImage() {

        final View flEditImage = View.inflate(mContext, R.layout.add_img_item, null);
flZone.addView(flEditImage); final ImageView iv = (ImageView) flEditImage.findViewById(R.id.iv);
final ImageView ivLeft = (ImageView) flEditImage.findViewById(R.id.ivLeft);
final ImageView ivRight = (ImageView) flEditImage.findViewById(R.id.ivRight);
final ImageView ivBLeft = (ImageView) flEditImage.findViewById(R.id.ivBLeft);
final ImageView ivBRight = (ImageView) flEditImage.findViewById(R.id.ivBRight);
iv.setImageResource(R.mipmap.shaosiming);
flEditImage.setTag(iv); ViewTreeObserver vto = flEditImage.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() { //监听一次马上结束 if (Build.VERSION.SDK_INT < 16) {
flEditImage.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
flEditImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
int[] location = new int[2];
flEditImage.getLocationOnScreen(location);//获取在整个屏幕内的绝对坐标
centerX0 = location[0] + flEditImage.getWidth() / 2;
centerY0 = location[1] + flEditImage.getHeight() / 2;
}
} ); //平移
flEditImage.setOnTouchListener(new OnTouchListener() { private boolean isMove;
private int downY;
private int downX; private ImageView iv; @Override
public boolean onTouch(View view, MotionEvent event) {
FrameLayout flview = (FrameLayout) view;
iv = (ImageView) view.getTag();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
downX = (int) event.getRawX();
downY = (int) event.getRawY();
isMove = false;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
isMove = true;
int moveX = (int) event.getRawX();
int moveY = (int) event.getRawY();
// Log.d(TAG,"moveX "+moveX+" moveY "+moveY);
int dx = moveX - downX;
int dy = moveY - downY;
int i1 = dx + (int) ViewHelper.getTranslationX(flview);
int i2 = dy + (int) ViewHelper.getTranslationY(flview);
ViewHelper.setTranslationX(flview, i1);
ViewHelper.setTranslationY(flview, i2);
downX = moveX;
downY = moveY;
Log.i("rex", "ACTION_MOVE");
flRoot.setClipChildren(false);//是否限制子View在其范围内*(注意这个属性必须放在拖动布局的爷爷布局才能生效) }
if (event.getAction() == MotionEvent.ACTION_UP) {
Log.i("rex", "ACTION_UP");
centerX = centerX0 + flEditImage.getTranslationX();
centerY = centerY0 + flEditImage.getTranslationY();
flRoot.setClipChildren(true);
flRoot.invalidate(); if (CheckIsOut(iv, flview)) {
ViewGroup parent = (ViewGroup) flview.getParent();
parent.removeView(flview); // flZone.removeView(flview);
Toast.makeText(mContext, "超出范围已移除", Toast.LENGTH_SHORT).show();
} if (!isMove) {
// Log.i("rex", "i 点击事件 ");
}
// centerX = (flEditImage.getLeft() + flEditImage.getWidth()) / 2;
// centerY = (flEditImage.getTop() + flEditImage.getHeight()) / 2; } return true;
}
}); //删除
ivLeft.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) { flZone.removeView(flEditImage);
}
}); //镜像
ivBLeft.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) { Bitmap bitmap = Bitmap.createBitmap(iv.getWidth(), iv.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
iv.draw(canvas);
iv.setImageBitmap(flip(bitmap, false)); }
});
//旋转
ivRight.setOnTouchListener(new OnTouchListener() { private float mDownY;
private float mDownX; //
// float centerX = flEditImage.getWidth() / 2;
// float centerY = flEditImage.getHeight() / 2; @Override
public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: mDownX = event.getRawX();
mDownY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getRawX();
float moveY = event.getRawY();
float rotate = flEditImage.getRotation();
float angle = getDegress(moveX, moveY) - getDegress(mDownX, mDownY);
// float angle = getDegress(mDownX, mDownY) - getDegress(moveX, moveY); Log.d("name", "degress -> " + (angle + rotate));
flEditImage.setRotation((rotate + angle) % 360); mDownX = moveX;
mDownY = moveY;
break;
case MotionEvent.ACTION_UP: break;
}
return true; } private float getDegress(float newX, float newY) {
float x = newX - centerX;
float y = newY - centerY;
return (float) Math.toDegrees(Math.atan2(y, x));
}
}); //缩放
ivBRight.setOnTouchListener(new OnTouchListener() { @Override
public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// mDownX = event.getRawX();
// mDownY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getRawX();
float moveY = event.getRawY(); float bili = getBili(moveX, moveY);
Log.d("rex", "bili -> " + bili);
flEditImage.setScaleX(bili);
flEditImage.setScaleY(bili);
correct4ButtonSize(1 / bili); Log.d("rex", " ivBRight.getScaleX(); " + ivBRight.getScaleX());
// mDownX = moveX;
// mDownY = moveY;
break;
case MotionEvent.ACTION_UP: break;
}
return true; } /**
*
* @param bili
*/
private void correct4ButtonSize(float bili) { ivLeft.setScaleX(bili);
ivLeft.setScaleY(bili); ivRight.setScaleX(bili);
ivRight.setScaleY(bili); ivBLeft.setScaleX(bili);
ivBLeft.setScaleY(bili); ivBRight.setScaleX(bili);
ivBRight.setScaleY(bili);
;
} private float getBili(float moveX, float moveY) { int[] location = new int[2];
// flEditImage.getLocationInWindow(location); //获取在当前窗口内的绝对坐标
flEditImage.getLocationOnScreen(location);//获取在整个屏幕内的绝对坐标
float radius = ivBLeft.getWidth();//不精确 float oldLine = (float) Math.sqrt(flEditImage.getWidth() * flEditImage.getWidth() + flEditImage.getHeight() * flEditImage.getHeight());
float newLine = (float) Math.sqrt((moveX - location[0] + radius) * (moveX - location[0] + radius) + (moveY - location[1] + radius) * (moveY - location[1] + radius)); float scale = newLine / oldLine;//此处有微量误差
return scale;
}
}); }
/**
* 判断是否超出
* @param iv
* @param fl
* @return
*/
private boolean CheckIsOut(ImageView iv, FrameLayout fl) {
int maxX = flZone.getWidth();
int maxY = flZone.getHeight(); int a = fl.getWidth();
int a2 = (int) fl.getX();
int a3 = iv.getLeft();
int a4 = iv.getRight();
int x1 = (int) (fl.getX() + iv.getLeft());
int x2 = (int) (fl.getX() + fl.getWidth() - iv.getLeft());
int y1 = (int) (fl.getY() + iv.getTop());
int y2 = (int) (fl.getY() + fl.getHeight() - iv.getTop()); if (x1 > maxX || x2 < 0 || y1 > maxY || y2 < 0) {
return true;
}
return false;
} public static Bitmap flip(Bitmap bitmap, boolean isVer) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Matrix matrix = new Matrix();
if (isVer)
matrix.postScale(1, -1);//镜像垂直翻转
else
matrix.postScale(-1, 1); //镜像水平翻转
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
bitmap.recycle();
return newBitmap;
}

转自:Android DIY之路 (一) 指定区域多图片合成 放大 缩小 镜像 旋转 等

Android DIY之路 (一) 指定区域多图片合成 放大 缩小 镜像 旋转 等(转)的更多相关文章

  1. Android多点触控技术,实现对图片的放大缩小平移,惯性滑动等功能

    首先推荐一下鸿洋大大的打造个性的图片预览与多点触控视频教程,这套教程教我们一步一步实现了多点触控实现对图片的平移和缩放的功能.这篇文章我将在鸿洋大大的基础之上做了一些扩展功能: 1.图片的惯性滑动 2 ...

  2. Android 本地&sol;网路下载图片实现放大缩小

     Android 本地加载/网路下载图片实现放大缩小拖拉效果,自定义控件. package com.example.ImageViewCustom; import android.app.Activi ...

  3. Android 图片的放大缩小拖拉

    package com.example.ImageView; import android.annotation.SuppressLint; import android.content.Contex ...

  4. scrollView截取指定区域的图片

    把scrollView放到一个容器里面,再截图就可以了 scrollview放到容器: UIView *lunboCarrier = [[UIView alloc] initWithFrame:CGR ...

  5. Android 自定义View可拖动移动位置及边缘拉伸放大缩小

    一.首先说一下定义这样一个View有什么用?在一些app中,需要设置头像,而用户选择的图片可能是使用摄像头拍摄,也可能是选择的相册里面的图片,总之,这样的图片大小不一,就比如在使用某个聊天软件的时候, ...

  6. android Graphics(三):区域(Range)

    前言:最近几天对画图的研究有些缓慢,项目开始写代码了,只能在晚上空闲的时候捯饬一下自己的东西,今天给大家讲讲区域的相关知识,已经想好后面两篇的内容了,这几天有时间赶紧写出来给大家.有关界面开发的东东内 ...

  7. Android学习之路——简易版微信为例(三)

    最近好久没有更新博文,一则是因为公司最近比较忙,另外自己在Android学习过程和简易版微信的开发过程中碰到了一些绊脚石,所以最近一直在学习充电中.下面来列举一下自己所走过的弯路: (1)本来打算前端 ...

  8. Android学习之路——简易版微信为例(二)

    1 概述 从这篇博文开始,正式进入简易版微信的开发.深入学习前,想谈谈个人对Android程序开发一些理解,不一定正确,只是自己的一点想法.Android程序开发不像我们在大学时候写C控制台程序那样, ...

  9. 小猪的Android入门之路 Day 4 - part 1

    小猪的Android入门之路 Day 4 - part 1 Android事件处理机制之--基于监听的事件处理机制 本节引言: 在開始本个章节前,我们先回想下,如今我们已经知道了android的一些相 ...

随机推荐

  1. smartjs - DataManager 场景示例分析 - 数据懒加载

    发一张policy的参数图设置图: 场景1 - 数据的懒加载/延迟加载 在很多时候,为了提高网页的加载速度,减少不必要的开销,会将页面的数据拆分成几个部分,首先加载呈现可视区域内的数据,然后剩下来的会 ...

  2. Spring 作用域 scope

    spring的作用域将对Bean的生命周期和创建方式产生影响.  主要分为五种类型的作用域 singleton (默认)在spring IOC容器中仅存在一个Bean实例,Bean以单实例的方式存在. ...

  3. 在Tomcat下配置Solr 4&period;x 版本

    solr是一款非常优秀的全文检索服务器,最新版本在配置和前台页面上都做了较大的改动, 所以对用惯了老版本的朋友们来说,再重新配置新版本的solr,无疑又是一件痛苦的事情. 配置环境:windows   ...

  4. 一个简单WPF登陆界面,包含记住密码,自动登录等功能,简洁美观

    简介:这是一个自己以前用WPF设计的登陆界面,属于一个实验性的界面窗体,如果用于产品还很有不足.但也是有一点学习价值.后台代码略有复杂,但基本上都有注释 分类,略有代码经验的一般都能看懂. 登陆界面外 ...

  5. C&num; 限制Text只能输入数字

    private void InputNumber(KeyPressEventArgs e) { //如果输入的不是数字键,也不是回车键.Backspace键,则取消该输入 && e.K ...

  6. atitit&period;404错误调查过程汇总

    atitit.404错误调查过程汇总 #----------jsp  head  errorPage="" del zeu ok le. #------resin server. ...

  7. hashlib 模块:加密

    import hashlib # 基本使用 cipher = hashlib.md5('需要加密的数据的二进制形式'.encode('utf-8')) print(cipher.hexdigest() ...

  8. &OpenCurlyDoubleQuote;放到桌面”的Servlet实现

    复习下Servlet下载文件, 当response把ContentType设置成application/xxxx的时候呢,浏览器会默认启动下载,而不是试图打开. 通过给httpHeader里面加入内容 ...

  9. ecmall 学习记录2

    1.ecmall 自带的写入日志方法:do_log4php("函数名","类名",$param);   $param是参数 在类里调用写入之日的方法 需要先加载 ...

  10. PyCharm 2019 最新激活方式总结(最新最全最有效&excl;&excl;&excl;

    host 注册码 http://idea.lanyus.com/           https://www.cnblogs.com/yjd_hycf_space/p/9110550.html     ...