Android获取离屏页面截图

19

我正在开发一款Android应用程序。我有一个填满整个屏幕的活动(Activity A),其中包含一些视图。当在A中点击按钮时,我想启动另一个活动(Activity B),它也有一些视图和控件。我希望Activity B处于屏幕外,并且想从A中获取B的截图。这是否可行?

注意:我已成功地通过将绘制缓存保存到位图中来获取页面A的截图,但是无法获取屏幕外页面的截图。

4个回答

14

是的,这是可能的...你应该在Activity 'A'中扩展ActivityGroup。 然后在你的按钮点击事件中执行以下操作...

 View view =getLocalActivityManager().startActivity("B",new Intent(this,B.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();

然后将该视图转换为位图...

我认为这对您有帮助...


谢谢John。我的A类已经继承了Activity,而Java不允许多重继承。那么我该如何让A类继承ActivityGroup呢? - Krishnabhadra
我无法截取到这个DecorView的屏幕截图。如何在这个decorView中获取主布局。对于我的onScreen活动,我截取了mainLayout的屏幕截图。如何从这个decorView中获取我的offScreen的mainLayout。 - Krishnabhadra
我该如何获取decorView()的位图..getDrawingCache()方法总是返回null。 - Krishnabhadra
最终我没有使用你所描述的方法。不过,我从你的答案开始,并最终得到了我想要的结果。但我认为有更好的方法(更直接)。如果你看到这条消息,请给出一个你会如何做到这一点的例子。 - Krishnabhadra
@Kantha,我正在取消你的答案勾选,因为我决定添加自己的答案,完整描述步骤(我应该早些时候就这样做,而不是编辑问题并在那里添加答案。当时我还是SO的新手)。你的答案确实帮助了我。谢谢。 - Krishnabhadra
显示剩余3条评论

5

好的,我已经达到了我的目标。这是我所采用的步骤。

  1. Start activity B with startActivityForResult() instead of startActivity().

    Intent bIntent = new Intent(A.this,B.class);
    startActivityForResult(bIntent,B_REQUEST_CODE);
    
  2. In onCreate of activity B take mainLayout of B and enable its drawing cache

    final LinearLayout layout = (LinearLayout)
                         findViewById(R.id.app_parent_layout);
    layout.setDrawingCacheEnabled(true);
    layout.setDrawingCacheQuality(LinearLayout.DRAWING_CACHE_QUALITY_HIGH);
    
  3. In activity B wait till its layout is drawn completely(This is very important). For that in onCreate() of activity B, get mainLayout of B, use ViewTreeObserver to get a call back when its layout is drawn completely.

    ViewTreeObserver vto = layout.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
     @Override
     public void onGlobalLayout() {
      layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      getDrawingBitmap();
     }
    });
    
  4. Now getDrawingBitmap() gets called when layout is drawn completely. There take your screenshot.

    public void getDrawingBitmap(){
        LinearLayout mainLayout = (LinearLayout)
                            findViewById(R.id.app_parent_layout);
        Bitmap b = mainLayout.getDrawingCache();
    
        File file = saveBitmapAsFile(b);
    
        Intent resultIntent = new Intent();
        resultIntent.putExtra("FullFileName",file.getAbsolutePath());
        setResult(Activity.RESULT_OK,resultIntent);
        finish();
    }
    

    Here I am taking bitmap and saving it as a file and returning the filename as a result code. I am sure there are better method to send the bitmap to parent activity than saving as a file and sending filename. But I have a requirement anyway to save bitmap for sometime. So For me this is the easier method. After setting result finish activity.

  5. Now on Activity A onActivityResult() retrieve the filename.

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      if(requestCode == B_REQUEST_CODE){
        if (resultCode == Activity.RESULT_OK) { 
          String newText = data.getStringExtra("FullFileName");
         File dir = new File (newText);
        } 
      }
    }
    

这里的问题是,活动B如何仍然出现在屏幕外?你没有处理它的任何代码吗? - Peterdk
@Peterdk 这里的问题是,为什么活动B仍然出现在屏幕外?实际上,活动B根本没有出现过。在它出现在屏幕上之前,它就已经结束了(请参见getDrawingBitmap方法中的finish()调用)。 - Krishnabhadra
好的,我现在明白了。然而,当我自己尝试时,我看到新活动的短暂闪烁,所以这并不是一个真正的解决方案。 - Peterdk
@Peterdk 不,我知道。这不是最好的解决方案。但是当我使用它时,没有闪光灯。你能否检查你的“getDrawingBitmap()”方法是否在“onResume()”调用之前被调用?我认为在 android 中没有其他调用屏幕外活动的方法(如果有人纠正我,我会很高兴)。 - Krishnabhadra
也许更好的将屏幕截图传回活动的方法是将其序列化为字节数组(例如压缩序列化为值为90的PNG格式),然后将其设置为Bundle extras。在其他活动中,从字节数组中读取此位图。 - Igor Čordaš

1

我认为这很难做到,因为我不知道如何控制这些视图的绘制方法,而且安卓操作系统也不会绘制它们,除非它们可见。

然而,可能有可能将其他视图置于前景,仅在短时间内填充其绘图缓存。也许可以在它变得可见之前再次将其放回背景,因为为了流畅的体验,操作系统似乎会在实际组合到前景之前绘制屏幕外的视图。

另一个技巧是将前景视图作为子视图附加到背景上,并给它一个非常轻微的透明度。例如,WebView 可以通过这种方式很好地叠加,实际上强制 WebView 及其背景都被绘制。也许这对其他视图也有效。


"除非可见,否则Android操作系统也不会绘制它们" 你确定吗?如果是的话,那就完了。 - Krishnabhadra
“visible”并不是一个确切的术语。例如,如果由于小容器视图的包含,只有一个像素的视图可见,则需要调用其onDraw(Canvas)方法,因为操作系统无法知道onDraw实际绘制在Canvas上的位置。因此,具有一些onDraw方法的用户定义的视图将进行绘制。但是,具有UI元素的布局视图不必这样做:每个UI元素本身都是一个视图,并且如果系统未假定其可见,则可以将其排除在外。 - dronus
也许你可以通过在调用invalidate()后使用自己的Canvas来绘制视图,从而自己绘制视图。 - dronus
“visible”并不是一个确切的术语。例如,如果由于小容器视图的包含,只有一个像素的视图可见,则需要调用其onDraw(Canvas)方法。 -> 好的,你说得对。让我试一下这个。 - Krishnabhadra

0

我正在使用这种方法,它运行得很好,但是有一个问题。第一次我只拍摄一个布局的快照,当我拍摄快照时,它给我正确的图片,但是在进行了一些更改后再次拍摄时,它会给我以前的快照。

RelativeLayout layout = (RelativeLayout)findViewById(R.id.mainview);
    layout.setDrawingCacheEnabled(true);
    layout.setDrawingCacheQuality(LinearLayout.DRAWING_CACHE_QUALITY_HIGH);
    layout.buildDrawingCache();
    Bitmap bitmap = layout.getDrawingCache();

          File file =  new File(Environment.getExternalStorageDirectory().toString() + "/sPenimg.png");
          FileOutputStream ostream;
          try {
              ostream = new FileOutputStream(file);
              bitmap.compress(CompressFormat.PNG, 100, ostream);
              ostream.flush();
              ostream.close();      
          } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }

这个问题可以通过添加 ((RelativeLayout)findViewById(R.id.mainview)).destroyDrawingCache(); 来解决。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接