Android 手机截屏程序

一:程序运行的大致思路

1、运行程序,显示一个Activity界面,点击按钮,显示一个浮窗。这里用到一个显示浮窗的小技术。

2、在显示浮窗的同时,会启动一个server服务,这个服务很重要,因为在这里会建立java端和android底层(即c语言端)的通信机制。这个地方比较抽象。以后再来解释,你就记住它是一个通信机制,相当于客户端和服务器端的关系

3、点击一下浮窗,就会向android底层发送消息,开始截屏,这里的向android底层发送消息,采用了android源代码里面的通信机制,我直接就把android源码拿来用了。

4、开始截屏,这里的截屏程序也是用的android自带的截屏程序,也是android源码,但是我在这里做了很多的工作。因为截屏出来的图片,我需要保存为png格式。

5、在保存png格式的图片的时候,我又使用了第三方的一个png库。

6、最后的运行效果相当于点击浮窗,开始截屏,再点击浮窗,停止截屏,图片会自动保存到/sdcard/DCIM/这个目录下面。

二:改程序在做的过程中使用到的一些技术

1、要用到android的应用程序开发的基本知识,这里就不多说了

2、用到了ndk开发技术

3、既然使用ndk开发,那C/C++的技术就不得不用了

4、因为做ndk开发,我觉的就相当于在Linux系统下面做c语言的开发,那Linux开发中使用的一些东西也就需要了。

5、最后一点,你的手机需要root。因为截屏的基本思路就读取屏幕像素在内存中的映射,所有需要直接读取内存中的内容,root是必须的。

三:程序开发的具体过程

1、从现在起,我会一步一步的把这个程序再重新做一遍,目的就是希望能把之前学习到的东西再回顾一遍,温故而知新。

2、先建立一个截屏项目工程,名字随便吧,我的叫screenshot如图

3、在Activity_main.xml文件中添加一个Button控件,如图

在MainActivity.java这个类里面做一些简单的初始化,为Button按钮添加点击响应事件,显示浮窗。因为显示浮窗,程序会直接跳到手机的桌面,所以这里有一个小技术,从应用程序直接跳到手机桌面,代码如下。

/**

* 返回到主桌面 类似按下Home按钮

*

* @param context

*/

public static void backToHomePage(Context context) {

Intent i = new Intent(Intent.ACTION_MAIN);

i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

i.addCategory(Intent.CATEGORY_HOME);

context.startActivity(i);

}

4、要新建一个ScreenCaptureServer类,这个类就非常重要了,它主要有这几个作用,首先要创建一个浮窗,其次就是要建立与android底层的连接,然后要维护与android底层的通信。最后也是最重要的,建立截屏程序的独立线程。首先做一个浮窗,很简单,这里就不多说了,具体可以看我的另一篇博客-----android之浮窗篇。

5、实现android的java端与底层的c端通信。因为这里是调用的系统的通信代码,好多我也没有搞清楚。不过现在到是能用,在该程序里面,java端充当的是服务器端,它随service的启动开始运行,不停的监听来自客户端(c端的连接)。下面是服务器线程的具体代码。

/**

* 服务器端的线程

*

* @author jeck

*

*/

private class ServerSocketThread extends Thread {

/**

* 线程运行的标示

*/

private boolean keeprunning = true;

/**

* 本地服务socket

*/

private LocalServerSocket localServerSocket;

@Override

public void run() {

try {

// 创建一个本地socket

localServerSocket = new LocalServerSocket("screen_shot_socket");

} catch (Exception e) {

e.printStackTrace();

keeprunning = false;

}

// 通过while循环, 轮训从客户端发过来的连接请求

while (keeprunning) {

Log.d(TAG, "Waitting for client to connect !! ");

try {

// 监听客户端的连接

LocalSocket interactClientSocket = localServerSocket

.accept();

// 因为有可能在等待客户端连接的时候,accept阻塞了。Activity被finish掉,所以有必要在检测一次

if (keeprunning) {

Log.d(TAG, "now client coming !!!");

// 开始为客户端服务

new InteractClientSocketThread(interactClientSocket)

.start();

}

} catch (Exception e) {

e.printStackTrace();

keeprunning = false;

}

}

// 如果断开连接,那就关闭服务

if (localServerSocket != null) {

try {

localServerSocket.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

// /**

// * 停止线程

// */

// public void stopRunning() {

//

// keeprunning = false;

// }

}

6、接下来的是service里面的第三个功能,就是维护与客户端(c端)的通信了。这里无非就是监听连接,连接到了就发送消息。因为要配合我的主要功能,截屏,所有我这里的逻辑是启动service,开启服务器监听,如果有客户端连接,会向客户端发送一个数字2,如果客户端收到2,会向服务器端发送2222表示连接成功,然后如果你点击了我们的小浮窗,会向客户端发送数字1,表示开始截屏,客户端收到消息会想服务器端发送1111表示我客户端已经收到消息了。然后你再点击一次小浮窗,服务器端会发送0,表示停止截屏,那客户端收到0会停止截屏,并向服务器端反馈0000.这就是我的通信机制。下面我把代码粘出来。

/**

* 服务器与客户端直接的通信线程

*

* @author jeck

*

*/

private class InteractClientSocketThread extends Thread {

/**

* 本地socket

*/

private LocalSocket interactClientSocket;

/**

* 输入流

*/

private InputStream inputStream = null;

/**

* 输出流

*/

private OutputStream outputStream = null;

/**

* 从客户端发来的消息

*/

private StringBuilder receiveFromClientString = new StringBuilder();

/**

* 输入缓存区

*/

private char[] readBuffer = new char[4096];

/**

* 输入字节数

*/

private int readBytes = -1;

/**

* 构造函数

*/

public InteractClientSocketThread(LocalSocket interactClientSocket) {

this.interactClientSocket = interactClientSocket;

}

/**

* 从客户端读取数据

*

* @return

*/

private boolean readDataFromClient() {

boolean readResult = false;

// 从本地连接中获取输入流

try {

inputStream = interactClientSocket.getInputStream();

// 读数据

InputStreamReader inputStreamReader = new InputStreamReader(

inputStream);

// 从输入流中读取数据

while ((readBytes = inputStreamReader.read(readBuffer)) != -1) {

String tmpStr = new String(readBuffer, 0, readBytes);

receiveFromClientString.append(tmpStr).append("\n");

}

if (receiveFromClientString.toString() != null) {

if (receiveFromClientString.toString().startsWith("0000")

|| receiveFromClientString.toString().startsWith(

"1111")) {

captureState = 2;

}

// 显示client发送的消息

Log.d(TAG, receiveFromClientString.toString());

// 读取时间成功

readResult = true;

}

} catch (IOException e) {

e.printStackTrace();

}

return readResult;

}

/**

* 向客户端写数据

*

* @return

*/

private boolean writeDataToClient(String writeContent) {

boolean writeResult = false;

try {

outputStream = interactClientSocket.getOutputStream();

// 如果点击了开始录屏,则发送消息

if (writeContent != null && !"".equals(writeContent)) {

outputStream.write(writeContent.getBytes());

}

writeResult = true;

} catch (IOException e) {

e.printStackTrace();

writeResult = false;

}

return writeResult;

}

@Override

public void run() {

try {

switch (captureState) {

case 0:

// 停止截屏

writeDataToClient(STOP_CAPTURE_SCREEN);

break;

case 1:

// 开始录屏

writeDataToClient(START_CAPTURE_SCREEN);

break;

case 2:

// 等待连接

writeDataToClient(KEEP_CONNECTION);

break;

}

readDataFromClient();

} catch (Exception e) {

e.printStackTrace();

Log.d(TAG, "receive data failed !!");

} finally {

if (outputStream != null) {

try {

outputStream.close();

} catch (Exception e) {

e.printStackTrace();

}

}

if (inputStream != null) {

try {

inputStream.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

}

7、接下来是最关键的部分,呵呵,其实那个地方都很关键,缺少了任何一个地方,这个程序也跑不起来。废话不多说了,现在简绍截屏进程了。这进程的工作原理是这样的。1需要手机root权限,在获取手机root权限之后,通过ndk的混合编译器,编译一个exe文件,在root权限下面,通过代码执行这个exe程序。换句话说就是相当于在java端,执行exe文件。因为android系统是基于Linux操作系统的,所以你的exe想要直接执行,必须获取一点的权限,就算你在Linux系统下面直接写代码,那你执行./xxxx 程序的时候,也是需要权限的。2就是要创建一个进程了(process)。你执行了exe程序,就相当于你创建了一个进程,所以要把这个进程获取出来,以便操作。最后这些东西都是在一个单独的线程中运行的,来代码。

/**

* 响应点击截屏按钮

*/

private boolean screenCapture() {

boolean result = false;

try {

// 创建log对象

screenLog = new StringBuilder();

// 创建一个进程

logcatProcess = RuntimeHelper.getLogcatProcess(this);

// 创建一个缓冲

bufferedReader = new BufferedReader(new InputStreamReader(

logcatProcess.getInputStream()), 8192);

String line;

while ((line = bufferedReader.readLine()) != null) {

Log.d(TAG, line);

screenLog.append(line).append("\n");

if (line.startsWith("Success")) {

result = true;

}

}

} catch (Exception e) {

e.printStackTrace();

result = false;

}

return result;

}

/**

* 使用异步线程执行截屏操作

*

* @author jeck

*

*/

private class ScreenCaptureTask extends AsyncTask<Void, Void, Boolean> {

@Override

protected Boolean doInBackground(Void... params) {

return screenCapture();

}

@Override

protected void onPostExecute(Boolean result) {

if (result) {

// 将日志保存在SD卡上

// try {

// // Utils.saveCaptureLog(screenLog.toString());

// } catch (Exception e) {

// e.printStackTrace();

// }

Toast.makeText(getApplicationContext(), "截屏成功",

Toast.LENGTH_LONG).show();

}

}

}

8、以上就是我们这个截屏程序的java端的所有代码了,这只是一个好的开始。

在java端的功能就是显示一个浮窗,然后点击浮窗,会开始和C端进行交互。

C端涉及的东西就都是C语言的了,这里面都是ndk的知识了,这里首先要编写一个.cpp文件,就是我们的通信程序,因为这里用的是android源代码,所以这个程序目前只适合android4.1系统的。其他版本暂时没有测试。下面这是我的exe程序的主要代码。

#include <stdio.h>

#include <sys/system_properties.h>

#include <dlfcn.h>

#include <android/log.h>

#include <sys/socket.h>

#include <cutils/sockets.h>

#include <sys/un.h>

#include <unistd.h>

#include <stddef.h>

#include <pthread.h>

#include <string.h>

#include "screen_capture_image.h"

#define TAG "--screen_capture-->"

int socketID;

/*

*获取当前的系统版本

*/

int getCurrentSDKVersion() {

int sdk;

char c[PROP_VALUE_MAX];

if (__system_property_get("ro.build.version.sdk", c) > 0) {

sscanf(c, "%d", &sdk);

} else {

sdk = 8;

}

return sdk;

}

/*

* Class:     com_rdtd_jni_SendMessageFromClientJNI

* Method:    startHeartBead

* Signature: ()I

*/

int connection_to_server() {

char path[] = "screen_shot_socket";

socketID = socket_local_client(path, ANDROID_SOCKET_NAMESPACE_ABSTRACT,

SOCK_STREAM);

//如果连接失败

if (socketID < 0) {

return socketID;

} else {

return 1;

}

}

void close_connection() {

if (socketID) {

close(socketID);

}

}

int read_data_from_server() {

//0表示停止录屏,1 表示开始录屏, 2 表示连接中, -1 表示读取数据出错

int result;

//读取的字数

int read_result;

char readBuffer[2];

memset(readBuffer, 0, 2);

read_result = read(socketID, readBuffer, 2);

if (read_result) {

result = atoi(readBuffer);

//printf("---->read data success :%d\n", result);

} else {

result = -1;

}

return result;

}

int write_data_to_server(const char *str) {

int write_result;

write_result = write(socketID, str, strlen(str));

if (write_result) {

close_connection();

return 1;

} else {

printf("write data failed !\n");

close_connection();

return 0;

}

}

void* begin_capture(void*) {

long result = 1;

//设置截屏开始的标示

set_capture_flag_png(1);

//截屏并保存成png图片,现在在联想的机器上是不行的。一直报的是找不到libpng。so文件

screen_capture_png();

return (void*) result;

}

/*

*截屏程序的入口,截屏参数都放在这里面。

*argv[1]表示宽度, argv[2]表示高度

*默认帧率15帧每秒,宽度和高度是手机屏幕的宽高

*/

int main(int argc, char *argv[]) {

int count = 0;

pthread_t thread_id;

void *thread_result;

//循环读写数据

while (connection_to_server()) {

//先读取数据

int read_result = read_data_from_server();

printf("---->receive message = %d\n", read_result);

__android_log_print(ANDROID_LOG_DEBUG, TAG, "receive message = %d",

read_result);

switch (read_result) {

case 0:

//停止录制

write_data_to_server("0000---------->");

set_capture_flag_png(0);

if (pthread_join(thread_id, &thread_result) == -1) {

printf("waiting thread failed !\n");

} else {

if ((long) thread_result == 0) {

printf("screen_cap return failed \n");

} else {

printf("Success------------------>\n");

//这个地方必须返回,否则保存的图片都是黑屏图片,不知道是为什么,呵呵,应该是没有关闭文件,只有函数返回了,系统自动关闭文件

exit(0);

}

}

break;

case 1:

//开始录制

write_data_to_server("1111---------->");

if (pthread_create(&thread_id, NULL, begin_capture, NULL) == -1) {

printf("create thread failed !\n");

}

break;

case 2:

//连接中

write_data_to_server("2222---------->");

break;

case -1:

//读取数据出错

printf("---->read data failed !\n");

goto exit;

}

sleep(3);

}

exit: close_connection();

return 0;

}

这个cpp文件就会被ndk编译成我们在上面提到的exe文件,就是Linux系统下面的可执行文件。只有这个文件,我们能做的就是和java端发个信息而已,还是不能截屏的,需要截屏的程序在下面,也是用的android源代码,不过基本让我改的没有源代码的味道了。

在这里,我使用了第三方的png库,我把它编译成静态库,然后链接到我的cpp文件中,最后把cpp文件编译成exe文件,再在代码中执行exe文件。下面是截屏的主要代码。

/*

* Copyright (C) 2010 The Android Open Source Project

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

*      http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

#include <errno.h>

#include <unistd.h>

#include <stdio.h>

#include <fcntl.h>

#include <ui/PixelFormat.h>

#include <zlib.h>

#include <libpng/png.h>

#include <time.h>

#include <stdlib.h>

#include <malloc.h>

#include <pthread.h>

#include <linux/fb.h>

#include <sys/ioctl.h>

#include <sys/mman.h>

#include <gui/SurfaceComposerClient.h>

#include "screen_capture_image.h"

#include <android/log.h>

//#ifdef ANDROID_KK //4.4

//#include <binder/ProcessState.h>

//#include <gui/ISurfaceComposer.h>

//#else

#include <binder/IMemory.h>

//#endif

using namespace android;

#define TAG "--screen_capture-->"

//截屏标识

static int capture_flag = 0;

//录音标识

static int record_flag = 0;

int PushTime = 0;

//获取当前时间(microsecond)

int64_t getCurrentTime() {

struct timeval tv;

gettimeofday(&tv, NULL);

return (1000000LL * tv.tv_sec) + tv.tv_usec;

}

//#ifdef ANDROID_KK

//static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;

//#endif

//显示错误信息

void error(const char* msg) {

fprintf(stderr, "%s: %s:\n", msg, strerror(errno));

exit(1);

}

typedef struct param {

void *fb_in;

FILE * fb_out;

int width;

int height;

int64_t usedTime;

size_t buffer_size;

param * next;

} image_info;

// 获取当前日期, 以秒为单位,现在我一秒之内可以截取3张图片,他的名字当然一样了

char* getLocalTime() {

char currentTime[128];

memset(currentTime, 0, 128);

int64_t time = getCurrentTime();

sprintf(currentTime, "%lld", time);

return currentTime;

}

//创建视频路径

void create_video_path(char* path) {

memset(path, 0, 256);

strcpy(path, "/sdcard/DCIM/Record_");

strcat(path, getLocalTime());

strcat(path, ".mp4");

printf("---->path = %s\n", path);

}

//创建视频路径

void create_image_path(char* path) {

memset(path, 0, 256);

strcpy(path, "/sdcard/DCIM/capture_");

strcat(path, getLocalTime());

strcat(path, ".png");

printf("---->path = %s\n", path);

}

//创建截屏图片信息的节点

image_info* create_image_info_node(const void * in, FILE* out, int w, int h,

size_t size, int64_t ut) {

image_info *newInfo = (image_info*) malloc(sizeof(image_info));

//分配内存

newInfo->fb_in = malloc(size);

//拷贝内存

memcpy(newInfo->fb_in, in, size);

newInfo->fb_out = out;

newInfo->buffer_size = size;

newInfo->width = w;

newInfo->height = h;

newInfo->usedTime = ut;

newInfo->next = NULL;

printf("---->create a new image node \n");

return newInfo;

}

//释放内存

void release_image_node(image_info* node) {

if (node != NULL) {

free(node->fb_in);

node->fb_in = NULL;

free(node);

node = NULL;

printf("---->success to release a image node \n");

}

}

/*

* fb_base 屏幕左上角的第一个像素的内存地址

* fb_out 生成的png保存的地址

* w 屏幕的宽度

* h 屏幕的高度

* f png的文件格式

*/

//void take_screenshot(char *fb_base, FILE* fb_out, int w, int h, int f) {

int take_screenshot(image_info *argu) {

long result = 0;

//printf("take_screenshot is running! \n");

//image_info *argu = (image_info*) param;

//png结构

png_structp png;

//png info

png_infop info;

struct fb_var_screeninfo vinfo;

// 因为是一行一行的扫描屏幕,r是行数

unsigned int r;

//每一行的长度,是右屏幕的宽度(像素) * 每一个像素的大小(32位 4个字节)

unsigned int rowlen;

//每一个像素所占的大小,4个字节

unsigned int bytespp = 4;

//创建一个png结构

png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

//printf("1---> png_create_write_struct is runed \n");

if (png == NULL) {

ALOGE("failed png_create_write_struct\n");

}

png_init_io(png, argu->fb_out);

//printf("2---> png_init_io is runed \n");

info = png_create_info_struct(png);

//printf("3---> png_create_info_struct is runed \n");

if (info == NULL) {

ALOGE("failed png_create_info_struct\n");

png_destroy_write_struct(&png, NULL);

}

if (setjmp(png_jmpbuf(png))) {

ALOGE("failed png setjmp\n");

png_destroy_write_struct(&png, NULL);

}

//设置png的各种信息

png_set_IHDR(png, info, argu->width, argu->height, 8,

PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,

PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

//printf("4---> png_set_IHDR is runed \n");

png_write_info(png, info);

//printf("5---> png_write_info is runed \n");

//计算每一行的长度

rowlen = argu->width * bytespp;

//新建一个临时变量,保存图片的内存块地址

png_bytep temp = (png_bytep) argu->fb_in;

//循环扫描屏幕,一行一行的读取数据

for (r = 0; r < argu->height; r++) {

//将第r行写到png结构中

png_write_row(png, temp);

//计算下一行的起始位置

temp += rowlen;

}

//写png的信息

png_write_end(png, info);

//printf("6---> png_write_end is runed \n");

png_destroy_write_struct(&png, NULL);

//printf("7---> png_destroy_write_struct is runed \n");

//保存完图片,就把文件关闭掉

if (fclose(argu->fb_out) == -1) {

error("close file failed !");

} else {

//printf("---->success to save a image \n");

temp = NULL;

result = 1;

}

return result;

}

//保存图片,返回保存成功的图片数量,如果不等于24, 则说明保存失败, 成功返回1, 失败,返回0

void* save_image(void *start) {

long result = 0;

image_info * p = (image_info*) start;

image_info * next = NULL;

while (p != NULL) {

//先将下一个节点的指针保存在next里面

next = p->next;

//直接保存,如果成功,则释放该节点所占的内存

if (take_screenshot(p) == 1) {

release_image_node(p);

} else {

error("save image failed");

}

//最后再将next节点复制给p,继续操作

p = next;

}

result = 1;

return (void*) result;

}

/*截屏程序*/

image_info* screen_shot() {

static int64_t base_time = getCurrentTime();

//截屏的开始时间和结束时间

int64_t beginTime = 0, usedTime = 0;

const void* base;

size_t size;

uint32_t width, height;

FILE *fb_out = NULL;

//#ifdef ANDROID_KK

//ProcessState::self()->startThreadPool();

//#endif

//

//#ifdef ANDROID_KK

//int32_t displayId = DEFAULT_DISPLAY_ID;

//#endif

ScreenshotClient screenshot;

beginTime = getCurrentTime();

//#ifdef ANDROID_KK

//sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);

//if (display != NULL && screenshot.update(display) == NO_ERROR) {

//#else

if (screenshot.update() == NO_ERROR) {

//#endif

base = screenshot.getPixels();

size = screenshot.getSize();

width = screenshot.getWidth();

height = screenshot.getHeight();

//计算截图时间

usedTime = getCurrentTime() - beginTime;

printf("---->Image: width = %d, height = %d, used time = % lld\n",

screenshot.getWidth(), screenshot.getHeight(), usedTime);

char image_path[256];

create_image_path(image_path);

fb_out = fopen(image_path, "w");

return create_image_info_node(base, fb_out, width, height, size,

usedTime);

}

return NULL;

}

//截屏并生成视频链表

void* capture_and_link(void *) {

image_info* start = NULL;

image_info* newNode = NULL;

image_info* tail = NULL;

//开始截屏并生成截屏链表

while (capture_flag) {

//保存视频流

newNode = screen_shot();

if (newNode != NULL) {

if (start == NULL) {

start = newNode;

}

if (tail != NULL) {

tail->next = newNode;

}

tail = newNode;

} else {

printf("---->screen_shot return NULL !\n");

return NULL;

}

//睡3秒,不然录制的太快

sleep(3);

}

return (void*) start;

}

#ifdef __cplusplus

extern "C" {

#endif

//设置截屏标志

void set_capture_flag_png(int flag) {

capture_flag = flag;

record_flag = flag;

}

//截屏并保存为图片

void screen_capture_png() {

//截屏线程,只负责截屏

pthread_t capture_thread;

if (pthread_create(&capture_thread, NULL, capture_and_link, NULL) == -1) {

error("create capture_thread  failed !");

}

//截屏之后返回的结果,是一个包含截屏图片信息的链表,这个链表保存的东西关系到整个截屏程序的成败

void* capture_result;

if (pthread_join(capture_thread, &capture_result) == -1) {

error("waiting capture_thread failed !");

}

//截屏完了,开始保存图片

pthread_t save_thread;

if (pthread_create(&save_thread, NULL, save_image, capture_result) == -1) {

error("create save_thread failed !");

}

//等待保存图片的线程

void* save_result;

if (pthread_join(save_thread, &save_result) == -1) {

error("waiting save_thread failed !");

}

if ((long) save_result == 1) {

printf("---->success to save image !\n");

}

}

#ifdef __cplusplus

}

#endif

到此为止,这个截屏程序的主要代码是都将完了,虽然说将的很简单,但是做起来一点都不简单,里面涉及的东西还是很多的。这需要你对ndk编程很熟悉才可能把这个程序顺利的运行起来

android之手机截屏小程序相关推荐

  1. android 实现手机录屏功能,基于MediaProjection实现Android移动手机截屏和录屏功能

    Android软件应用经常要求实现截屏和录屏的功能,那么如何实现Android软件截屏和录屏功能呢?本文将介绍基于MediaProjection实现Android移动手机截屏和录屏功能. MediaP ...

  2. Android下手机截屏

    Android开发过程中,难免会需要对手机进行截屏.以前截屏时一直到处找截屏软件,操作复杂. 今天刚发现,原来ADT是自带截屏功能的,而且操作简单. 打开DDMS视图,在Device框的右上角有一个照 ...

  3. Android之MediaProjectionManager实现手机截屏总结

    比较好的文章: Android中使用代码截图的各种方法总结 http://blog.csdn.net/woshinia/article/details/11520403 手机截屏: http://ww ...

  4. Android手机截屏

    对于android手机截屏,据我所知,现在主要有三种方法. 第一种,通过DDMS. 把手机连接上电脑,运行DDMS,选中你的手机设备,然后点击菜单"设备"->"Sc ...

  5. Android手机截屏并上传到电脑

    前一段时间受不了某流氓手机助手,给卸载了,现在手机截屏 是个问题,虽说有些开发工具可以截屏,但是不方便,就萌发了写个脚本截屏的想法. 脚本使用window echo命令编写.所以只可运行在window ...

  6. android区域截图app,截图 + 录屏,这个 App 满足你对手机截屏的所有需求 - Screenshot touch #Android...

    作者:周杰腾现在用手机截屏基本都要用「关机 + 音量键」这种组合按钮,又或者用状态栏上的快捷方式.在大屏手机当道的现在,单手操作略显麻烦.最近 AppSo(微信公众号 AppSo)就发现了 Scree ...

  7. android华为怎么截屏快捷键,华为手机怎么截图?华为手机截图怎么截?截屏快捷键是什么?...

    感动的瞬间有很多,或许是与亲朋好友视频时的搞怪表情,或许是与爱人在微信的一句表白,又或者是公布比赛结果的那一瞬间.一瞬间的感动,一瞬间的开心,一瞬间的惊喜,不管这一瞬间发生了什么,它们都是很容易消逝的 ...

  8. android app应用后台休眠,安卓手机锁屏后程序自动关闭,怎么设置手机app允许锁屏后台运行...

    原标题:安卓手机锁屏后程序自动关闭,怎么设置手机app允许锁屏后台运行 安卓手机锁屏后,很多程序就会自动关闭,实际上,这是安卓手机的一种保护机制.为了使系统能够流畅稳定的运行以及更加省电,它都会在手机 ...

  9. 一种实现(无须root)手机截屏方案

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/53966818 前言:一年半多以前 ...

最新文章

  1. IDEA打包出现Unable to find main class
  2. 张勇:做一个透明经济体的CEO
  3. LINQ To DataSet 几个常用示例
  4. python对象复制_Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
  5. 蚂蚁金服11.11:支付宝和蚂蚁花呗的技术架构及实践
  6. strtotime()加半个小时_炖羊肉,必须加这4种料,缺一味就不香!很多人做错,难怪不好吃...
  7. Apache Derby-02通过IJ简单操作DERBY
  8. 重新绑定ItemsSource先设置ItemsSource = null;的原因
  9. android+图标+i_explore+无背景,Android Studio中Android Device Monitor中的File Explore不显示文...
  10. 对象转型(casting); 多态; static(静态);单例模式
  11. IS-IS详解(八)——深入探究IS-IS DIS选举机制
  12. 【礼仪大赛策划方案手段】 问穿正装的礼节是什么?到底何为正装?休闲正装?
  13. 关于购买二手macbook的建议,看这一篇文章就够了!
  14. 案例研究 | 瓜子二手车战略升级三部曲
  15. 计算机毕业设计android的云南旅游,美食攻略,产品预订app(源码+系统+mysql数据库+Lw文档)
  16. 智能制造系统解决方案和智能工厂发展趋势
  17. 逻辑备份数据导入原分区表遇到的那些事儿
  18. 中图分类号——计算机软件类
  19. 未来几年GPT/大模型如何影响软件研发?
  20. 聊一聊 vscode 的代码格式化

热门文章

  1. 云天视界传媒浅谈无人机航拍技巧
  2. 学游戏设计要什么学历_学历低学游戏动作设计好就业吗
  3. ipa包的简单校验方法
  4. 你所不了解的手机程序数据外泄
  5. 【转载】浅谈蓝牙 Mesh 组网技术
  6. 网易邮箱显示附件上传不成功,请重试!(错误码:FR_INVALID_REQUEST) 的解决方法
  7. html时间轴横向自动播放,利用jQuery实现日期时间轴自动播放代码
  8. 根据起始日期、起始时间、终止日期、终止时间计算年数
  9. 智能合约升级原理01---起源
  10. 七种PDF转Excel的转换方法,分分钟提高你的工作效率