android QQ截图 开发
2013-11-28 09:31
435 查看
使用过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;
}
实现原理:
自定义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截图 开发--------------growBy-----------
- Android开发技巧:像QQ一样输入表情图像
- Android开发打开指定QQ,申请加入QQ群,打开微信
- android开发技巧——仿新版QQ锁屏下弹窗
- Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果
- android开发技巧——仿新版QQ锁屏下弹窗(转)
- Android 开发过程中物理返回键的运用(仿qq退出应用,仍在后台执行,但不销毁)
- android开发之查询QQ是否在线
- android开发技巧——仿新版QQ锁屏下弹窗
- android开发之ExpandableListView的使用,实现类似QQ好友列表
- Android开发 获取当前activity的屏幕截图
- Android移动开发-在Android项目里实现仿QQ界面的 Material Design 设计开发
- [置顶] 【Android】开发干货-技术分享之高仿QQ换肤SkinEngine实现
- Android开发实现QQ三方登录 标签: android开发qq三方登录
- 【Android开发】经典范例2-仿QQ在状态栏显示登录状态的图标
- Android开发技巧——使用Dialog实现仿QQ的ActionSheet菜单
- Android开发之仿QQ侧滑删除实现(二)
- Android开发技巧:像QQ一样输入表情图像
- android开发之ExpandableListView的使用,实现类似QQ好友列表
- Android开发笔记(一百三十)截图和录屏