【转】Android仿QQ截图应用测试
使用过QQ的同学应该都用过QQ截图,Ctrl+Alt+A进入截图操作,通过拉伸,移动高亮区域的框体可以快速截取我们需要的图片。在android应用中,我们也经常需要截图操作,以下实现了一个类似QQ截图的应用。先贴图看看效果:
实现原理:
自定义CaptureView,在CaptureView上绘制具有一个可拉伸,移动的高亮矩形框,通过FrameLayout布局将这个CaptureView覆盖到需要截图的图片显示控件ImageView上,当点击截图按钮后,计算CaptureView矩形框的坐标值及宽和高读取图片相映区域的像素,并将这些像素通过画布重新绘制成图片。
首先先上布局文件:main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFFFF"
android:orientation="vertical" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" >
<!-- 显示图片 -->
<ImageView
android:id="@+id/iv_image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitXY" />
<!-- 自定义的截图View -->
<gwn.test.capture.CaptureView
android:id="@+id/capture"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<!-- 截图显示 -->
<ImageView
android:id="@+id/iv_corp"
android:layout_width="100dip"
android:layout_height="100dip"
android:layout_gravity="right"
android:background="#50000000"
android:scaleType="centerInside" />
</FrameLayout>
<Button
android:id="@+id/btn_crop"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="截图" />
</LinearLayout>
布局文件很简单,接下来主角就要登场了,当然这就是CaptureViewr的实现。CaptureView需要绘制的部分有三个,分别为整个View可视范围viewRect,矩形框体captureRect,拉伸时的显示箭头。CaptureView的触摸事件类型有三种:无操作、移动、拉伸,代码定义如下:
private enum ActionMode { // 枚举动作类型:无、移动、拉伸
None, Move, Grow
}
首先先计算viewRect,captureView的大小,我们在系统给View指派大小的地方初始化这两个区域,即在onLayout()方法中实现。代码如下:
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
// 初始化可视范围及框体大小
viewRect = new Rect(left, top, right, bottom);
int viewWidth = right - left;
int viewHeight = bottom - top;
int captureWidth = Math.min(viewWidth, viewHeight) * 3 / 5;
int captureHeight = viewHeight * 2 / 5;
// 将框体绘制在可视范围中间位置
int captureX = (viewWidth - captureWidth) / 2;
int captureY = (viewHeight - captureHeight) / 2;
captureRect = new Rect(captureX, captureY, captureX + captureWidth,
captureY + captureHeight);
}
接下来重写ondraw(Canvas canvas),将可视范围、框体区域,箭头绘制上去:
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.save();
Path path = new Path();
path.addRect(new RectF(captureRect), Path.Direction.CW);// 顺时针闭合框体
canvas.clipPath(path, Region.Op.DIFFERENCE);
canvas.drawRect(viewRect, outsideCapturePaint); // 绘制框体外围区域
canvas.drawPath(path, lineCapturePaint); // 绘制框体
canvas.restore();
if (mMode == ActionMode.Grow) { // 拉伸操作时,绘制框体箭头
int xMiddle = captureRect.left + captureRect.width() / 2; // 框体中间X坐标
int yMiddle = captureRect.top + captureRect.height() / 2; // 框体中间Y坐标
// 框体左边的箭头
horStretchArrows.setBounds(captureRect.left
- horStretchArrowsHalfWidth, yMiddle
- horStretchArrowsHalfHeigth, captureRect.left
+ horStretchArrowsHalfWidth, yMiddle
+ horStretchArrowsHalfHeigth);
horStretchArrows.draw(canvas);
// 框体右边的箭头
horStretchArrows.setBounds(captureRect.right
- horStretchArrowsHalfWidth, yMiddle
- horStretchArrowsHalfHeigth, captureRect.right
+ horStretchArrowsHalfWidth, yMiddle
+ horStretchArrowsHalfHeigth);
horStretchArrows.draw(canvas);
// 框体上方的箭头
verStretchArrows.setBounds(xMiddle - verStretchArrowsHalfWidth,
captureRect.top - verStretchArrowsHalfHeigth, xMiddle
+ verStretchArrowsHalfWidth, captureRect.top
+ verStretchArrowsHalfHeigth);
verStretchArrows.draw(canvas);
// 框体下方的箭头
verStretchArrows.setBounds(xMiddle - verStretchArrowsHalfWidth,
captureRect.bottom - verStretchArrowsHalfHeigth, xMiddle
+ verStretchArrowsHalfWidth, captureRect.bottom
+ verStretchArrowsHalfHeigth);
verStretchArrows.draw(canvas);
}
}
重头戏来了,CaptureView的事件监听。首先定义触摸位置及动作,代码:
// 触摸位置及动作
public static final int GROW_NONE = (1 << 0);//框体外部
public static final int GROW_LEFT_EDGE = (1 << 1);//框体左边缘
public static final int GROW_RIGHT_EDGE = (1 << 2);//框体右边缘
public static final int GROW_TOP_EDGE = (1 << 3);//框体上边缘
public static final int GROW_BOTTOM_EDGE = (1 << 4);//框体下边缘
public static final int GROW_MOVE = (1 << 5);//框体移动
// 确定触摸位置及动作,分别为触摸框体外围和框体上、下、左、右边缘以及框体内部。
private int getGrow(float x, float y) {
final float effectiveRange = 20F; // 触摸的有效范围大小
int grow = GROW_NONE;
int left = captureRect.left;
int top = captureRect.top;
int right = captureRect.right;
int bottom = captureRect.bottom;
boolean verticalCheck = (y >= top - effectiveRange)
&& (y < bottom + effectiveRange);
boolean horizCheck = (x >= left - effectiveRange)
&& (x < right + effectiveRange);
// 触摸了框体左边缘
if ((Math.abs(left - x) < effectiveRange) && verticalCheck) {
grow |= GROW_LEFT_EDGE;
}
// 触摸了框体右边缘
if ((Math.abs(right - x) < effectiveRange) && verticalCheck) {
grow |= GROW_RIGHT_EDGE;
}
// 触摸了框体上边缘
if ((Math.abs(top - y) < effectiveRange) && horizCheck) {
grow |= GROW_TOP_EDGE;
}
// 触摸了框体下边缘
if ((Math.abs(bottom - y) < effectiveRange) && horizCheck) {
grow |= GROW_BOTTOM_EDGE;
}
// 触摸框体内部
if (grow == GROW_NONE && captureRect.contains((int) x, (int) y)) {
grow = GROW_MOVE;
}
return grow;
}
如果grow的值不为GROW_NONE,也即用户触摸位置在框体边缘或框体内部,那么就锁定用户本次触摸,直到用户放开触摸释放。判断用户的移动事件是伸缩框体还是移动框体,如果是伸缩框体,则调用growBy()方法拉伸框体,否则调用moveBy()移动框体代码如下:
private void handleMotion(int grow, float dx, float dy) {
if (grow == GROW_NONE) {
return;
} else if (grow == GROW_MOVE) {
moveBy(dx, dy); // 移动框体
} else {
if (((GROW_LEFT_EDGE | GROW_RIGHT_EDGE) & grow) == 0) {
dx = 0; // 水平不伸缩
}
if (((GROW_TOP_EDGE | GROW_BOTTOM_EDGE) & grow) == 0) {
dy = 0; // 垂直不伸缩
}
growBy((((grow & GROW_LEFT_EDGE) != 0) ? -1 : 1) * dx,
(((grow & GROW_TOP_EDGE) != 0) ? -1 : 1) * dy);
}
}
下面是贴上这两个方法,有关说明见注释
private void moveBy(float dx, float dy) {
Rect invalRect = new Rect(captureRect);
captureRect.offset((int) dx, (int) dy);
captureRect.offset(Math.max(0, viewRect.left - captureRect.left),
Math.max(0, viewRect.top - captureRect.top));
captureRect.offset(Math.min(0, viewRect.right - captureRect.right),
Math.min(0, viewRect.bottom - captureRect.bottom));
//清除移动滞留的痕迹
invalRect.union(captureRect);//更新围绕本身区域和指定的区域,
invalRect.inset(-100, -100);
invalidate(invalRect); // 重绘指定区域
}
private void growBy(float dx, float dy) {
float widthCap = 50F; //captureRect最小宽度
float heightCap = 50F; //captureRect最小高度
RectF r = new RectF(captureRect);
//当captureRect拉伸到宽度 = viewRect的宽度时,则调整dx的值为 0
if (dx > 0F && r.width() + 2 * dx >= viewRect.width()) {
dx = 0F;
}
//同上
if (dy > 0F && r.height() + 2 * dy >= viewRect.height()) {
dy = 0F;
}
r.inset(-dx, -dy); // 框体边缘外移
//当captureRect缩小到宽度 = widthCap时
if (r.width() <= widthCap) {
r.inset(-(widthCap - r.width()) / 2F, 0F);
}
//同上
if (r.height() <= heightCap) {
r.inset(0F, -(heightCap - r.height()) / 2F);
}
if (r.left < viewRect.left) {
r.offset(viewRect.left - r.left, 0F);
} else if (r.right > viewRect.right) {
r.offset(-(r.right - viewRect.right), 0);
}
if (r.top < viewRect.top) {
r.offset(0F, viewRect.top - r.top);
} else if (r.bottom > viewRect.bottom) {
r.offset(0F, -(r.bottom - viewRect.bottom));
}
captureRect.set((int) r.left, (int) r.top, (int) r.right,
(int) r.bottom);
invalidate();
}
接下来看下截图操作,由于ImageView显示的图片跟原始图片有比例上的区别,因此,先取得调整比例的图片,代码说明:
// ImageView中的图像是跟实际的图片有比例缩放,因此需要调整图片比例
private Bitmap regulationBitmap(Bitmap bitmap) {
int ivWidth = ivImage.getWidth();
int ivHeight = ivImage.getHeight();
int bmpWidth = bitmap.getWidth();
int bmpHeight = bitmap.getHeight();
// 宽和高的比例
float scaleWidth = (float) ivWidth / bmpWidth;
float scaleHeight = (float) ivHeight / bmpHeight;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bmpWidth,
bmpHeight, matrix, true);
return resizeBmp;
}
截图代码:
private Bitmap cropImage(){
Rect cropRect = mCaptureView.getCaptureRect();
int width = cropRect.width();
int height = cropRect.height();
Bitmap croppedImage = Bitmap.createBitmap(width,
height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(croppedImage);
Rect dstRect = new Rect(0, 0, width, height);
// 调整图片显示比例
mBitmap = regulationBitmap(mBitmap);
canvas.drawBitmap(mBitmap, cropRect, dstRect, null);
return croppedImage;
}
分析完毕!
【转】Android仿QQ截图应用测试的更多相关文章
- WPF C#截图功能 仿qq截图
原文:WPF C#截图功能 仿qq截图 先上效果图 源码下载地址:http://download.csdn.net/detail/candyvoice/9788099 描述:启动程序,点击窗口butt ...
- Android仿QQ ios dialog,仿QQ退出向上菜单
Android仿QQ ios dialog,仿QQ退出向上菜单 EasyDialog两种模式 仿QQ退出向上菜单,自己定义向上菜单 github地址:https://gith ...
- Android 仿QQ首页的消息和电话的切换,首页的头部(完全用布局控制)
Android 仿QQ首页的消息和电话的切换,首页的头部(完全用布局控制) 首先贴上七个控制布局代码 1.title_text_sel.xml 字体颜色的切换 放到color文件夹下面 <?xm ...
- android 仿QQ手机版
千人2群开启,欢迎大家围观打酱油,群号145667827 您当前位置 : JavaApk-安卓应用游戏源码服务专家 » QQ » Android项目源码界面超级华丽的仿QQ最新版本 Andro ...
- Android仿QQ登录下拉历史列表
demo中包含了Sqlite数据库增删改查,对存储的账号进行按照最新的时间排序,限制了最多存储5条数据. 效果图: 1.首先创建MyHelper建表: public class MyHelper ex ...
- Android仿QQ窗口的抖动的动画效果
就是仿照QQ窗口的抖动效果,在项目的res下创建anim文件夹,再创建两个xml文件:cycle.xml . myanim.xml cycle.xml : <?xml version ...
- android仿QQ的SlideMenu
这其实很简单就可以实现,只需要自定义一个View继承自HorizontalScrollView 1,新建一个项目,再新建一个MySlideMenu继承HorizontalScrollView publ ...
- android仿qq空间、微信朋友圈图片展示
废话不多说,先上效果图 由于近期须要做朋友圈功能,所以在此记录一下,事实上非常多人不明确的一点应该是在图片的排列上面吧,不规则的排列,事实上非常easy的.就是一个GridView.然而你xml光光写 ...
- Android仿qq聊天记录长按删除功能效果
最近项目在做IM即时通讯开发,在删除聊天列表的时候跟删除聊天详细信息的时候,产品经理想要跟ios一样,在当前选中行上方弹出一个删除窗口.于是先从网上找demo,找了一个发现是Dialog做的,我感觉没 ...
随机推荐
- VMware网络选项分析
摘自资料:VMware网卡选项分析.zip 很多朋友都曾问到关于Guest和Host互联,其实这并不是一件困难的事情,只要能够理解VMware的网络模型即可,今天结合着我的虚拟机,来详细介绍一下VMw ...
- 将yyyyMMdd HH:mm:ss格式的时间转换成时间类型
DateTime.ParseExact(gmt_withdrawal, "yyyyMMddHHmmss", System.Globalization.CultureInfo.Cur ...
- 写一个TT模板自动生成spring.net下面的配置文件。
这个是目标. 然后想着就怎么开始 1.
- Windows server2008 搭建ASP接口访问连接oracle数据库全过程记录--备用
真的是太不容易了,以前的时候在window server 2003上面搭建了一套asp+oracle的接口系统,就费了好大的劲儿,其实那会迷迷瞪瞪的也不知道怎么的就弄好了,也懒得管了.OK,从昨天到今 ...
- C++引用作为函数的参数
引用也可以作为一个函数的参数,如:我们定义交换两个数的函数swap,将函数的参数定义成引用的形式: void swap(int &p1, int &p2) //此处函数的形参都是引用 ...
- java 容器类大集结
这个世界是程序员的世界,归根到底是数据的世界,要统治这个世界,首先要学会征服数据. 没有最好的,只有最合适的,如何在不同的环境先选择最优的存储的结构呢?且看下文分解: 以下内容部分来自网络,参考: h ...
- SQL2005 安装时 “性能监视器计数器要求(错误)” 解决方案
转自SQL2005 安装时 "性能监视器计数器要求(错误)" 解决方案 出现此类问题一般都是在非法卸载sql2005出现的 在 "开始" --> &qu ...
- POJ 2750 Potted Flower(线段树的区间合并)
点我看题目链接 题意 : 很多花盆组成的圆圈,每个花盆都有一个值,给你两个数a,b代表a位置原来的数换成b,然后让你从圈里找出连续的各花盆之和,要求最大的. 思路 :这个题比较那啥,差不多可以用DP的 ...
- 【BZOJ 2005】[Noi2010]能量采集 (容斥原理| 欧拉筛+ 分块)
能量采集 Description 栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量.在这些植物采集能量后,栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起. 栋栋 ...
- windows进程中的内存结构(好多API,而且VC最聪明)
在阅读本文之前,如果你连堆栈是什么多不知道的话,请先阅读文章后面的基础知识. 接触过编程的人都知道,高级语言都能通过变量名来访问内存中的数据.那么这些变量在内存中是如何存放的呢?程序又是如何使用这 ...