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. 最后通牒!8月1日开始ios中国区下架全部无版号游戏

      据媒体报道,苹果早于7月8日就给中国游戏开发者发送邮件,要求游戏开发者必须在7月31日前提交游戏版号及相关文件,否则付费游戏将不可以在中国AppStore供应,8月1日期全部正式下架. ​   需 ...

  2. 精讲RestTemplate第3篇-GET请求使用方法详解

    本文是精讲RestTemplate第3篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层H ...

  3. Python 创建用户界面之 PyQt5 的使用

    之前给大伙介绍了下 tkinter,有朋友希望小帅b对其它的 Python  GUI 框架也说道说道,那么今天就来说说 PyQt5 如何创建用户界面.   很多人学习python,不知道从何学起.很多 ...

  4. QUIC协议详解之Initial包的处理

    从服务器发起请求开始追踪,细说数据包在 QUIC 协议中经历的每一步.大量实例代码展示,简明易懂了解 QUIC. 前言 本文介绍了在 QUIC 服务器在收到 QUIC 客户端发起的第一个 UDP 请求 ...

  5. 使用halo搭建自己的博客并配置https域名访问

    首先进行java配置 # 1. 下载jdk [下载地址](https://www.oracle.com/cn/java/technologies/javase-downloads.html) - 一定 ...

  6. Golang Gtk+3教程:GtkBuilder使用XML构建UI

    在这节我将介绍GtkBuilder,其使我们可以从一个描述界面的xml文件构建UI.而这个文件我们可以使用Glade生成,这会极大的方便我们编辑用户界面.这节我们会使用到这么一个XML文件,名字为Bu ...

  7. 一个在交流群里讨论过两轮的问题,答案竟然跟一个 PEP 有关

    Python 中有没有办法通过类方法找到其所属的类? 这个问题看起来不容易理解,我可以给出一个例子: class Test: @xxx def foo(self): pass 现在有一个类和一个类方法 ...

  8. 涨见识!Java String转int还有这种写法

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个有颜值却假装靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题 ...

  9. 【算法•日更•第二十八期】图论:强连通+Tarjan算法(一)

    ▎前言 一直都想学习这个东西,以为很难,结果发现也不过如此. 只要会些图论的基础就可以了. ▎强连通 ☞『定义』 既然叫强连通,那么一定具有很强的连通性. 强连通:就是指在一个有向图中,两个顶点可以互 ...

  10. LeetCode 到底怎么刷?GitHub 上多位大厂程序员亲测的高效刷题方式

    作者:HelloGitHub-小鱼干 在众多的诸如阿里.腾讯等大厂之中,最看中面试者刷题技能的大概要数有"链表厂"之称的字节跳动了.作为一个新晋大厂,字节跳动以高薪.技术大佬云集吸 ...