http://blog.csdn.net/xww810319/article/details/17607749

Android截屏浅析

链接:http://blog.sina.com.cn/s/blog_69a04cf4010173fz.html

Android的调试工具DDMS提供截屏功能,很多同步软件例如豌豆荚也都提供截屏功能,经分析Android截屏原理大致如下:

DDMS是通过adb调用设备端的adbd(ADBdaemon)提供的framebufferservice进行截屏(源码在system/core/adb/framebuffer_service.c),在较早版本的Android中,framebuffer service通过直接读framebuffer 设备(/dev/graphics/fb0)来截屏,在较新版本的Android中,framebuffer service则调用截屏工具screencap来截屏。那些同步软件也是调用screencap实现截屏的。

screencap是Android原生自带的工具,是一个C写的可执行文件,在设备上的/system/bin/下面可以找到它,screencap截屏后可保存为PNG格式文件或RGBRAW文件。

screencap的源码在frameworks/base/cmds/screencap/,它调用SurfaceFlinger提供的截屏接口ScreenshotClient,其源码在frameworks/native/libs/gui/SurfaceComposerClient.cpp(该路径在不同版本的Android源码中可能略有差别),ScreenshotClient通过进程间通信调用SurfaceFlinger service的截屏功能,源码在frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中的函数SurfaceFlinger::captureScreen。

在各种截屏方法中,读framebuffer设备(/dev/graphics/fb0)的方式在某些使用硬件overlay显示的设备上可能无法截取到某些画面(例如video playback和camera preview画面),但是SurfaceFlinger提供的上述截屏接口则可以完美截取任何屏幕画面,因此相对来说是Android上最正规最完善的截屏方法,使用起来也非常简单。但需注意,ScreenshotClient等接口在不同Android版本上可能存在差异,例如在JellyBean的最新版本上ScreenshotClient的成员函数update增加了一个参数display(应该是为了支持多个显示设备),导致与之前版本不兼容。

此外Android还自带另一个截屏工具screenshot,源码在frameworks/base/cmds/screenshot/,它通过直接读framebuffer 设备(/dev/graphics/fb0)来截屏,保存为PNG格式文件。screenshot貌似没有用处。

原文二:(用自己的方式实现截屏)

Android代码截屏

链接:http://myhpu2008.iteye.com/blog/999779

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;

public class ScreenShot {
    // 获取指定Activity的截屏,保存到png文件
    private static Bitmap takeScreenShot(Activity activity) {
        // View是你需要截图的View
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap b1 = view.getDrawingCache();

// 获取状态栏高度
        Rect frame = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;
        Log.i("TAG", "" + statusBarHeight);

// 获取屏幕长和高
        int width = activity.getWindowManager().getDefaultDisplay().getWidth();
        int height = activity.getWindowManager().getDefaultDisplay()
                .getHeight();
        // 去掉标题栏
        // Bitmap b = Bitmap.createBitmap(b1, 0, 25, 320, 455);
        Bitmap b = Bitmap.createBitmap(b1, 0, statusBarHeight, width, height
                - statusBarHeight);
        view.destroyDrawingCache();
        return b;
    }

// 保存到sdcard
    private static void savePic(Bitmap b, String strFileName) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(strFileName);
            if (null != fos) {
                b.compress(Bitmap.CompressFormat.PNG, 90, fos);
                fos.flush();
                fos.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

// 程序入口
    public static void shoot(Activity a) {
        ScreenShot.savePic(ScreenShot.takeScreenShot(a), "sdcard/xx.png");
    }
}

需要注意的是,shoot方法只能在view已经被加载后方可调用。

或者在    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        // TODO Auto-generated method stub
        super.onWindowFocusChanged(hasFocus);
        ScreenShot.shoot(this);
    }中调用

原文三:

浅谈android截屏问题

链接:http://my.oschina.net/JumpLong/blog/75556

做了几个月的截屏开发,稍微了解了一下这方面的知识,于是拿来分享一下,也许对你有一些帮助吧。

我是基于android2.3.3系统之上的,想必大家应该知道在android源码下面有个文件叫做screencap吧,位于frameworks\base\services\surfaceflinger\tests\screencap\screencap.cpp,你直接在linux下编译(就是去screencap的文件mm,然后保存在 /system/bin/test-screencap),然后push到手机上再通过电脑去敲命令./test-screencap /mnt/sdcard/scapxx.png就可以实现截屏(不要导入到/mntsdcard,反正我是导入到system目录里面才可以执行)。

  1. /*
  2. * Copyright (C) 2010 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *      http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <utils/Log.h>
  17. #include <binder/IPCThreadState.h>
  18. #include <binder/ProcessState.h>
  19. #include <binder/IServiceManager.h>
  20. #include <binder/IMemory.h>
  21. #include <surfaceflinger/ISurfaceComposer.h>
  22. #include <SkImageEncoder.h>
  23. #include <SkBitmap.h>
  24. using namespace android;
  25. int main(int argc, char** argv)
  26. {
  27. if (argc != 2) {
  28. printf("usage: %s path\n", argv[0]);
  29. exit(0);
  30. }
  31. const String16 name("SurfaceFlinger");
  32. sp<ISurfaceComposer> composer;
  33. getService(name, &composer);
  34. sp<IMemoryHeap> heap;
  35. uint32_t w, h;
  36. PixelFormat f;
  37. status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
  38. if (err != NO_ERROR) {
  39. fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
  40. exit(0);
  41. }
  42. printf("screen capture success: w=%u, h=%u, pixels=%p\n",
  43. w, h, heap->getBase());
  44. printf("saving file as PNG in %s ...\n", argv[1]);
  45. SkBitmap b;
  46. b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
  47. b.setPixels(heap->getBase());
  48. SkImageEncoder::EncodeFile(argv[1], b,
  49. SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
  50. return 0;
  51. }

其实这个程序真正用到的就是一个叫做capturescreen的函数,而capturescreen会调用captureScreenImplLocked这个函数

下面是代码:

  1. status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
  2. sp<IMemoryHeap>* heap,
  3. uint32_t* w, uint32_t* h, PixelFormat* f,
  4. uint32_t sw, uint32_t sh)
  5. {
  6. LOGI("captureScreenImplLocked");
  7. status_t result = PERMISSION_DENIED;
  8. // only one display supported for now
  9. if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
  10. return BAD_VALUE;
  11. if (!GLExtensions::getInstance().haveFramebufferObject())
  12. return INVALID_OPERATION;
  13. // get screen geometry
  14. const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
  15. const uint32_t hw_w = hw.getWidth();
  16. const uint32_t hw_h = hw.getHeight();
  17. if ((sw > hw_w) || (sh > hw_h))
  18. return BAD_VALUE;
  19. sw = (!sw) ? hw_w : sw;
  20. sh = (!sh) ? hw_h : sh;
  21. const size_t size = sw * sh * 4;
  22. // make sure to clear all GL error flags
  23. while ( glGetError() != GL_NO_ERROR ) ;
  24. // create a FBO
  25. GLuint name, tname;
  26. glGenRenderbuffersOES(1, &tname);
  27. glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
  28. glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
  29. glGenFramebuffersOES(1, &name);
  30. glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
  31. glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
  32. GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
  33. GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
  34. if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
  35. // invert everything, b/c glReadPixel() below will invert the FB
  36. glViewport(0, 0, sw, sh);
  37. glScissor(0, 0, sw, sh);
  38. glMatrixMode(GL_PROJECTION);
  39. glPushMatrix();
  40. glLoadIdentity();
  41. glOrthof(0, hw_w, 0, hw_h, 0, 1);
  42. glMatrixMode(GL_MODELVIEW);
  43. // redraw the screen entirely...
  44. glClearColor(0,0,0,1);
  45. glClear(GL_COLOR_BUFFER_BIT);
  46. const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
  47. const size_t count = layers.size();
  48. for (size_t i=0 ; i<count ; ++i) {
  49. const sp<LayerBase>& layer(layers[i]);
  50. layer->drawForSreenShot();
  51. }
  52. // XXX: this is needed on tegra
  53. glScissor(0, 0, sw, sh);
  54. // check for errors and return screen capture
  55. if (glGetError() != GL_NO_ERROR) {
  56. // error while rendering
  57. result = INVALID_OPERATION;
  58. } else {
  59. // allocate shared memory large enough to hold the
  60. // screen capture
  61. sp<MemoryHeapBase> base(
  62. new MemoryHeapBase(size, 0, "screen-capture") );
  63. void* const ptr = base->getBase();
  64. if (ptr) {
  65. // capture the screen with glReadPixels()
  66. glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
  67. if (glGetError() == GL_NO_ERROR) {
  68. *heap = base;
  69. *w = sw;
  70. *h = sh;
  71. *f = PIXEL_FORMAT_RGBA_8888;
  72. result = NO_ERROR;
  73. }
  74. } else {
  75. result = NO_MEMORY;
  76. }
  77. }
  78. glEnable(GL_SCISSOR_TEST);
  79. glViewport(0, 0, hw_w, hw_h);
  80. glMatrixMode(GL_PROJECTION);
  81. glPopMatrix();
  82. glMatrixMode(GL_MODELVIEW);
  83. } else {
  84. result = BAD_VALUE;
  85. }
  86. // release FBO resources
  87. glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
  88. glDeleteRenderbuffersOES(1, &tname);
  89. glDeleteFramebuffersOES(1, &name);
  90. hw.compositionComplete();
  91. return result;
  92. }
  93. status_t SurfaceFlinger::captureScreen(DisplayID dpy,
  94. sp<IMemoryHeap>* heap,
  95. uint32_t* width, uint32_t* height, PixelFormat* format,
  96. uint32_t sw, uint32_t sh)
  97. {
  98. LOGI("into captureScreen");
  99. // only one display supported for now
  100. if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
  101. return BAD_VALUE;
  102. if (!GLExtensions::getInstance().haveFramebufferObject())
  103. return INVALID_OPERATION;
  104. class MessageCaptureScreen : public MessageBase {
  105. SurfaceFlinger* flinger;
  106. DisplayID dpy;
  107. sp<IMemoryHeap>* heap;
  108. uint32_t* w;
  109. uint32_t* h;
  110. PixelFormat* f;
  111. uint32_t sw;
  112. uint32_t sh;
  113. status_t result;
  114. public:
  115. MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
  116. sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
  117. uint32_t sw, uint32_t sh)
  118. : flinger(flinger), dpy(dpy),
  119. heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED)
  120. {
  121. }
  122. status_t getResult() const {
  123. LOGI("getResult()");
  124. return result;
  125. }
  126. virtual bool handler() {
  127. LOGI("handler()");
  128. Mutex::Autolock _l(flinger->mStateLock);
  129. // if we have secure windows, never allow the screen capture
  130. if (flinger->mSecureFrameBuffer)
  131. return true;
  132. result = flinger->captureScreenImplLocked(dpy,
  133. heap, w, h, f, sw, sh);
  134. return true;
  135. }
  136. };
  137. LOGI("before messagecapturescreen");
  138. sp<MessageBase> msg = new MessageCaptureScreen(this,
  139. dpy, heap, width, height, format, sw, sh);
  140. status_t res = postMessageSync(msg);
  141. if (res == NO_ERROR) {
  142. res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
  143. }
  144. return res;
  145. }

而这个函数关键又使用了opengl的几个函数去获得图片,然而opengl又去read framebuffer(这是我的理解)。如果你去用jni调用so的方法去截屏的话,就可以把screencap这个文件稍微修改一下然后做成so文件,方法可以参考这篇博客:http://blog.csdn.net/zx19899891/article/details/7072291

主要是补充一下怎么去存放文件与编译吧,当然我说的方法只是我做的方法不代表是很好用的。

存放:在eclipse新建一个android工程,保存后找到这个工程(如screencap)的存放位置 然后把这个文件放到android源代码的development文件里面,然后在你的那个工程文件里面新建一个文件夹,名字叫做jni(这个文件夹平行于src文件夹,screencap/jni),把上面博客提到的那个C++跟mk(screencap/jni/com_android_ScreenCap_ScreenCapNative.cpp和screencap/jni/Android.mk)文件放进去,最后在把编译的mk文件放在screencap目录下(screencap/Android.mk);

编译:编译是个很伟大的工程,需要你花大量的时间与精力。直接在终端进入工程存放的所在位置,我的是Administrator/Android.2.3.3/development,然后mm(Builds all of the modules in the current directory),如果成功,那么你运气比较好,在终端回提示你APK保存的位置。push进手机试一试。但是往往是不成功的。你可能会遇到一些问题,比如android.permission.ACCESS_SURFACE_FLINGER ,android.permission.READ_FRAME_BUFFER(因为capturescrren这个函数是surfaceflinger里面的函数,然而surfaceflinger里面的opengl截屏函数会去读取framebuffer),相关源代码是:

  1. status_t SurfaceFlinger::onTransact(
  2. uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
  3. {
  4. switch (code) {
  5. case CREATE_CONNECTION:
  6. case OPEN_GLOBAL_TRANSACTION:
  7. case CLOSE_GLOBAL_TRANSACTION:
  8. case SET_ORIENTATION:
  9. case FREEZE_DISPLAY:
  10. case UNFREEZE_DISPLAY:
  11. case BOOT_FINISHED:
  12. case TURN_ELECTRON_BEAM_OFF:
  13. case TURN_ELECTRON_BEAM_ON:
  14. {
  15. // codes that require permission check
  16. IPCThreadState* ipc = IPCThreadState::self();
  17. const int pid = ipc->getCallingPid();
  18. const int uid = ipc->getCallingUid();
  19. if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) {
  20. LOGE("Permission Denial: "
  21. "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
  22. return PERMISSION_DENIED;
  23. }
  24. break;
  25. }
  26. case CAPTURE_SCREEN:
  27. {
  28. // codes that require permission check
  29. IPCThreadState* ipc = IPCThreadState::self();
  30. const int pid = ipc->getCallingPid();
  31. const int uid = ipc->getCallingUid();
  32. if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
  33. LOGE("Permission Denial: "
  34. "can't read framebuffer pid=%d, uid=%d", pid, uid);
  35. return PERMISSION_DENIED;
  36. }
  37. break;
  38. }
  39. }
  40. status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
  41. if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
  42. CHECK_INTERFACE(ISurfaceComposer, data, reply);
  43. if (UNLIKELY(!mHardwareTest.checkCalling())) {
  44. IPCThreadState* ipc = IPCThreadState::self();
  45. const int pid = ipc->getCallingPid();
  46. const int uid = ipc->getCallingUid();
  47. LOGI("err");
  48. LOGE("Permission Denial: "
  49. "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
  50. return PERMISSION_DENIED;
  51. }
  52. int n;
  53. switch (code) {
  54. case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
  55. case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
  56. return NO_ERROR;
  57. case 1002:  // SHOW_UPDATES
  58. n = data.readInt32();
  59. mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
  60. return NO_ERROR;
  61. case 1003:  // SHOW_BACKGROUND
  62. n = data.readInt32();
  63. mDebugBackground = n ? 1 : 0;
  64. return NO_ERROR;
  65. case 1004:{ // repaint everything
  66. Mutex::Autolock _l(mStateLock);
  67. const DisplayHardware& hw(graphicPlane(0).displayHardware());
  68. mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe
  69. signalEvent();
  70. return NO_ERROR;
  71. }
  72. case 1005:{ // force transaction
  73. setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
  74. return NO_ERROR;
  75. }
  76. case 1006:{ // enable/disable GraphicLog
  77. int enabled = data.readInt32();
  78. GraphicLog::getInstance().setEnabled(enabled);
  79. return NO_ERROR;
  80. }
  81. case 1007: // set mFreezeCount
  82. mFreezeCount = data.readInt32();
  83. mFreezeDisplayTime = 0;
  84. return NO_ERROR;
  85. case 1010:  // interrogate.
  86. reply->writeInt32(0);
  87. reply->writeInt32(0);
  88. reply->writeInt32(mDebugRegion);
  89. reply->writeInt32(mDebugBackground);
  90. return NO_ERROR;
  91. case 1013: {
  92. Mutex::Autolock _l(mStateLock);
  93. const DisplayHardware& hw(graphicPlane(0).displayHardware());
  94. reply->writeInt32(hw.getPageFlipCount());
  95. }
  96. return NO_ERROR;
  97. }
  98. }
  99. return err;
  100. }

这个仅仅只是开始!  你会发现你即使在xml里面添加相应的权限仍然会有这个问题出现,为什么呢?在packageManger文件里面发现相关代码:

  1. int checkSignaturesLP(Signature[] s1, Signature[] s2) {
  2. if (s1 == null) {
  3. return s2 == null
  4. ? PackageManager.SIGNATURE_NEITHER_SIGNED
  5. : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
  6. }
  7. if (s2 == null) {
  8. return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
  9. }
  10. HashSet<Signature> set1 = new HashSet<Signature>();
  11. for (Signature sig : s1) {
  12. set1.add(sig);
  13. }
  14. HashSet<Signature> set2 = new HashSet<Signature>();
  15. for (Signature sig : s2) {
  16. set2.add(sig);
  17. }
  18. // Make sure s2 contains all signatures in s1.
  19. if (set1.equals(set2)) {
  20. return PackageManager.SIGNATURE_MATCH;
  21. }
  22. return PackageManager.SIGNATURE_NO_MATCH;
  23. }
  24. Check for shared user signatures
  25. if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
  26. if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
  27. pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
  28. Slog.e(TAG, "Package " + pkg.packageName
  29. + " has no signatures that match those in shared user "
  30. + pkgSetting.sharedUser.name + "; ignoring!");
  31. mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
  32. return false;
  33. }
  34. }
  35. return true;
  36. private boolean verifySignaturesLP(PackageSetting pkgSetting,
  37. PackageParser.Package pkg) {
  38. // Check for shared user signatures
  39. if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
  40. (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
  41. pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
  42. log.e(TAG, "Package " + pkg.packageName
  43. + " has no signatures that match those in shared user "
  44. + pkgSetting.sharedUser.name + "; ignoring!");
  45. mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
  46. return false;
  47. }
  48. }
  49. return true;
  50. }

你在终端输入adb logcat | grep PackageManager 你会发现这两个权限根本没有赋予给你的apk,我的理解是,程序需要权限,然后apk仍然需要权限。那怎么样给apk赋予权限呢,两个方法,一个是在我上面说的screencap/Android.mk里面添加platform一行,然后在回到mm。还有一个方法就是通过sign。这两个方法都是给apk赋予system权限,但是我试过这两种方法,都有问题,就是在adb install的时候会显示签名不兼容,查看源代码会发现uid跟gid不匹配。这些是我这段时间发现的问题,大家有问题可以交流交流。

再说说几个简单的应用层截屏吧,很简单,就是几个函数调用而已

  1. View view = getWindow().getDecorView();
  2. Display display = this.getWindowManager().getDefaultDisplay();
  3. view.layout(0, 0, display.getWidth(), display.getHeight());
  4. view.setDrawingCacheEnabled(true);//允许当前窗口保存缓存信息,这样  getDrawingCache()方法才会返回一个Bitmap
  5. Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache());

我对这个程序的理解就是,它仅仅只能截取当前的activity,也就是说如果你运行这个程序后它就截取你这个程序的当前屏幕的信息。我们假设你做成一个按钮,在点击按钮后sleep5秒再调用这个方法(假设你的activity叫做screen)。当你点击按钮以后,然后你再点击home或者返回按钮,等到5秒后你那个程序就会截取到你当前屏幕?不是!它只会截取那个运行于后台的screen这个activity。

还有个截屏的方法就是用OPENGL。

这里有我的opengl截屏的程序,方便的话可以下载后去看看。

http://www.oschina.net/code/snippet_729469_19233

这些只是我的一点小小的总结,而且肯定有不对的地方,希望大家一起来解决截屏的问题!

【转】Android截屏的更多相关文章

  1. Android截屏的几种实现

    Android截屏的几种实现 微信公众号:CodingAndroid CSDN:http://blog.csdn.net/xinpengfei521 最近我们的APP要求需要截屏功能,网上看了看大致有 ...

  2. android截屏

    截屏是一个常用的操作,经常会有这种需求. 截屏的工具类 package com.fxb.screenshot; import android.app.Activity; import android. ...

  3. Android 截屏与 WebView 长图分享经验总结

    最近在做新业务需求的同时,我们在 Android 上遇到了一些之前没有碰到过的问题,截屏分享. WebView 生成长图以及长图在各个分享渠道分享时图片模糊甚至分享失败等问题,在这过程中踩了很多坑,到 ...

  4. Android 截屏的各种骚操作

    本文公众号「AndroidTraveler」首发. 背景 在实际的应用场景中,Android 手机的截屏其实是很普遍的. 比如说 PPT 演示,比如说技术博客图文并茂讲解. 因此懂得 Android ...

  5. android截屏:保存一个view的内容为图片并存放到SD卡

    项目中偶尔会用到截屏分享,于是就有了下面这个截屏的方法~ 下面得saveImage()方法就是保存当前Activity对应的屏幕所有内容的截屏保存. private void saveImage() ...

  6. Android 截屏检测

    最近项目中新接到一个需求,对手机截屏进行检测并进行后续操作,类似于Snapchat,iOS具有先天优势,因iOS系统提供了相关API!Google无果之后原作者决定再次造轮子,为了持续表达对Rx的敬意 ...

  7. 快速简化Android截屏工作

    1.安装Notepad++v6.9 2.插件管理器里Plugin Manager安装AndroidLogger 3.AndroidLogger里的capture功能抓取Android的当前屏幕截图到w ...

  8. Android手机截屏

    刚开始打算做一个简单的截屏程序时,以为很轻松就能搞定. 在Activity上放一个按钮,点击完成截屏操作,并将数据以图片形式保存在手机中. 动手之前,自然是看书和网上各种查资料.结果发现了解的知识越多 ...

  9. Android开发笔记:安卓程序截屏方法

    1,基于Android SDK的截屏方法 (1)主要就是利用SDK提供的View.getDrawingCache()方法.网上已经有很多的实例了.首先创建一个android project,然后进行L ...

随机推荐

  1. 连接云MariaDB实例

    新建实例 MariaDB CVM 内网连接 CVM远程登录测试(同一个VPC下): CVM安装客户端: yum install mysql 连接: mysql -h MariaDB内网ip -u 数据 ...

  2. 使用Flask开发简单接口(5)--数据加密处理

    前言 在之前开发的接口中,我们设计把用户信息存储到数据库时,没有对数据进行加密处理,为了提高下安全性,我们今天就学习下,如何对用户数据进行加密加盐处理. MD5加密加盐 MD5加密 MD5是常用的一种 ...

  3. Pytest单元测试框架-allure测试报告

    Allure Test Report 对于不同的编程语言,有很多很酷的测试框架.不幸的是,它们中只有少数能够提供测试执行输出的良好表示.Qameta软件测试团队正在致力于Allure--一个开源框架, ...

  4. OAuth2.0-4整合网关

    .antMatchers("/**").access("#oauth2.hasScope('scope1')")上面这行代码,只是控制大范围的访问权限,具体到方 ...

  5. 比PS还好用!Python 20行代码批量抠图

    你是否曾经想将某张照片中的人物抠出来,然后拼接到其他图片上去,从而可以即使你在天涯海角,我也可以到此一游? 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在 ...

  6. 有用的20个Python代码段

    Python是一种非BS编程语言.设计简单和易读性是它广受欢迎的两大原因.正如Python的宗旨:美丽胜于丑陋,显式胜于隐式. 记住一些帮助提高编码设计的常用小诀窍是有用的.在必要时刻,这些小诀窍能够 ...

  7. C语言学习笔记之一个程序弄清&&、||、i++、++i

     由此程序可以看出, ++a是先执行自加,再把值赋值给c,所以c就是a+1=10+1=11 b++是先做赋值运算,也就是先d=b,再b自加,所以d=b(原先)=5 a和b都执行自加,所以a=11,b= ...

  8. JS DOM笔记

    js的组成     ECMAScript:JS的语法     DOM:页面文档对象模型     BOM:浏览器对象模型     web APIs     是浏览器提供的一套操作浏览器功能和页面元素的A ...

  9. 10、Java 数组的定义和使用

    1.数组的定义 首先举一个小例自:如果你现在需要定义100个int类型的变量,那么按照前俩节的做法为: int a = 1, b=2 , c=3.......; 可以发现我们的代码特别的冗余,而且不美 ...

  10. java 序列化流与反序列化流

    一 对象序列化流ObjectOutputStream ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream.可以使用 ObjectInputStr ...