【问题标题】:Take a screenshot of a whole View [duplicate]截取整个视图 [重复]
【发布时间】:2012-04-05 05:02:29
【问题描述】:

我已经建立了一个基本上由HorizontalScrollViewScrollView 中完成的表。我让用户可以编辑字段。

现在我想将表格保存在屏幕上,jpg、png、pdf 或其他任何格式。

问题是 - 桌子几乎总是比屏幕大。

有没有办法截取整个ScrollView 布局?如果不是,您认为什么可以胜任这项工作?

【问题讨论】:

标签: android screenshot


【解决方案1】:

其实我找到了答案:

public static Bitmap loadBitmapFromView(View v) {
    Bitmap b = Bitmap.createBitmap(v.getWidth() , v.getHeight(), Bitmap.Config.ARGB_8888);                
    Canvas c = new Canvas(b);
    v.draw(c);
    return b;
}

【讨论】:

  • 这里的问题是平均布局没有真正的高度/宽度值,并且通常设置为 match_parent / fill_parent。就我而言,我需要调查一个具有可访问实际尺寸的父母。
  • Leon,你总是可以调用 view.getWidth() 和 view.getHeight 来获取航班上的视图尺寸
  • 这段代码对我来说工作正常,但我想在一秒钟内连续截取 24-25 个截屏,我每秒可以截取 14-15 个截屏,但不能截取 24- 25张截图。你能告诉我该怎么做吗?
  • @FarrakhJaved,我和你有同样的问题,你有更好的解决方案吗?谢谢
  • 很好,但是背景是黑色的。如何设置背景颜色?
【解决方案2】:
  ScrollView iv = (ScrollView) findViewById(R.id.scrollView);
  Bitmap bitmap = Bitmap.createBitmap(
        iv.getChildAt(0).getWidth(), 
        iv.getChildAt(0).getHeight(), 
        Bitmap.Config.ARGB_8888);
  Canvas c = new Canvas(bitmap);
  iv.getChildAt(0).draw(c);

  // Do whatever you want with your bitmap
  saveBitmap(bitmap);

【讨论】:

  • 它有效,但背景为黑色。我怎样才能改变它?
  • 我明白了。需要更改第一个子元素的背景颜色。
【解决方案3】:

在我的视图被重绘并变得完全变白的情况下,使用@softwaresupply 答案会导致问题。有一个更简单的解决方案来获取屏幕截图,您甚至不必提供宽度和高度作为参数。 使用绘图缓存

public static Bitmap loadBitmapFromView(View v) {
    Bitmap bitmap;
    v.setDrawingCacheEnabled(true);
    bitmap = Bitmap.createBitmap(v.getDrawingCache());
    v.setDrawingCacheEnabled(false);
    return bitmap;
}

【讨论】:

  • 这种方法不太安全,绘图缓存有限制,当您的视图很大时(如可滚动视图),getDrawingCache 将返回 null。查看Android SDK for View.java 和方法buildDrawingCacheImpl,日志中也有警告"not displayed because it is too large to fit into a software layer (or drawing cache), needs..."
【解决方案4】:

无法对尚未渲染的内容(如 ScrollView 的屏幕外部分)进行屏幕截图。但是,您可以制作多个屏幕截图,在每个镜头之间滚动内容,然后加入图像。这是一个可以为您自动执行此操作的工具:https://github.com/PGSSoft/scrollscreenshot

免责声明:我是这个工具的作者,它由my employer 发布。欢迎提出功能请求。

【讨论】:

  • 谢谢!我困惑了一会儿,因为我想“但是我需要在设备中运行的部分是什么?” - 没想到它会像那样工作。
  • stackoverflow.com/a/54779181/7074112 检查此答案以获得可能的解决方案。该库使用与上述相同的过程,但使用更通用的形式。 (不是优化的技术,但肯定是可用的)
【解决方案5】:

从这里下载源代码 (Take screenshot of scrollview in android programmatically)

activity_main.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#efefef"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_screenshot"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_margin="10dp"
        android:text="Take ScreenShot"/>

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="10dp"
        android:background="#ffffff">

        <LinearLayout
            android:id="@+id/ll_linear"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:orientation="vertical">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image2"/>

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image3"/>

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image5"/>

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image6"/>

        </LinearLayout>
    </ScrollView>
</LinearLayout>

MainActivity.xml

package deepshikha.com.screenshot;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;

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

public class MainActivity extends AppCompatActivity {

Button btn_screenshot;
ScrollView scrollView;
LinearLayout ll_linear;
public static int REQUEST_PERMISSIONS = 1;
boolean boolean_permission;
boolean boolean_save;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    init();
    fn_permission();
}

private void init() {
    btn_screenshot = findViewById(R.id.btn_screenshot);
    scrollView = findViewById(R.id.scrollView);
    ll_linear = findViewById(R.id.ll_linear);

    btn_screenshot.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (boolean_save) {
                Intent intent = new Intent(getApplicationContext(), Screenshot.class);
                startActivity(intent);

            } else {
                if (boolean_permission) {
                    Bitmap bitmap1 = loadBitmapFromView(ll_linear, ll_linear.getWidth(), ll_linear.getHeight());
                    saveBitmap(bitmap1);
                } else {

                }
            }

        }
    });
}

public void saveBitmap(Bitmap bitmap) {
    File imagePath = new File("/sdcard/screenshotdemo.jpg");
    FileOutputStream fos;
    try {
        fos = new FileOutputStream(imagePath);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
        Toast.makeText(getApplicationContext(), imagePath.getAbsolutePath() + "", Toast.LENGTH_SHORT).show();
        boolean_save = true;

        btn_screenshot.setText("Check image");

        Log.e("ImageSave", "Saveimage");
    } catch (IOException e) {
        Log.e("GREC", e.getMessage(), e);
    }
}

public static Bitmap loadBitmapFromView(View v, int width, int height) {
    Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    v.draw(c);

    return b;
}

private void fn_permission() {
    if ((ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) ||
            (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
        if ((!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE))) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    REQUEST_PERMISSIONS);
        }

        if ((!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE))) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSIONS);
        }
    } else {
        boolean_permission = true;
    }
}


@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == REQUEST_PERMISSIONS) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            boolean_permission = true;

        } else {
            Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();

        }
    }
}
}

谢谢!

【讨论】:

    【解决方案6】:

    您可以向视图传递一个基于位图对象的 Canvas 的新实例。

    试试

    Bitmap b = Bitmap.createBitmap(targetView.getWidth(), 
                                   targetView.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    targetView.draw(c);
    BitmapDrawable d = new BitmapDrawable(getResources(), b);
    canvasView.setBackgroundDrawable(d);`
    

    它确实为我完成了这项工作。

    【讨论】:

      【解决方案7】:

      这对我有用,希望对你也有帮助。

      public static Bitmap getBitmapByView(ScrollView scrollView) {
          int h = 0;
          Bitmap bitmap = null;
          //get the actual height of scrollview
          for (int i = 0; i < scrollView.getChildCount(); i++) {
              h += scrollView.getChildAt(i).getHeight();
              scrollView.getChildAt(i).setBackgroundResource(R.color.white);
          }
          // create bitmap with target size
          bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
                  Bitmap.Config.ARGB_8888);
          final Canvas canvas = new Canvas(bitmap);
          scrollView.draw(canvas);
          FileOutputStream out = null;
          try {
              out = new FileOutputStream("/sdcard/screen_test.png");
          } catch (FileNotFoundException e) {
              e.printStackTrace();
          }
          try {
              if (null != out) {
                  bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
                  out.flush();
                  out.close();
              }
          } catch (IOException e) {
              // TODO: handle exception
          }
          return bitmap;
      }
      

      【讨论】:

        【解决方案8】:

        我测试了很多代码,每次都点击NullPointerExeption。我发现当我们的视图没有父视图时,提供的宽度和高度(Xml 或 Java)会被忽略并设置为MATCH_PARENT

        最后我想出了这个解决方案:

        /**
         * Take screen shot of the View
         *
         * @param v the view
         * @param width_dp
         * @param height_dp
         *
         * @return screenshot of the view as bitmap
         */
        public static Bitmap takeScreenShotOfView(View v, int width_dp, int height_dp) {
        
            v.setDrawingCacheEnabled(true);
        
            // this is the important code :)
            v.measure(View.MeasureSpec.makeMeasureSpec(dpToPx(v.getContext(), width_dp), View.MeasureSpec.EXACTLY),
                    View.MeasureSpec.makeMeasureSpec(dpToPx(v.getContext(), height_dp), View.MeasureSpec.EXACTLY));
            v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
        
            v.buildDrawingCache(true);
        
            // creates immutable clone
            Bitmap b = Bitmap.createBitmap(v.getDrawingCache());
            v.setDrawingCacheEnabled(false); // clear drawing cache
            return b;
        }
        
        public static int dpToPx(Context context, int dp) {
            Resources r = context.getResources();
            return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
        }
        

        【讨论】:

          【解决方案9】:

          您也许可以使用视图的绘图缓存,但我不确定这是否会保存整个视图或只是呈现到屏幕上的内容。

          我建议你在 StackOverflow 上四处寻找类似的问题,以前很可能有人问过。

          【讨论】:

          • 其实我没有找到合适的解决方案。不幸的是,绘图缓存无法获取整个视图。
          • 您所追求的解决方案相当复杂。设备不会在屏幕之外渲染任何东西(因为它不是必须的),因此在请求之前不会渲染视图
          【解决方案10】:

          试试这个对我来说很好用

          TableLayout tabLayout = (TableLayout) findViewById(R.id.allview);
          
              if (tabLayout != null) {
          
                      Bitmap image = Bitmap.createBitmap(tabLayout.getWidth(),
                              tabLayout.getHeight(), Config.ARGB_8888);
                      Canvas b = new Canvas(image);
          
                      tabLayout.draw(b);
          }
          

          【讨论】:

            【解决方案11】:

            //设置按钮点击监听

                share = (Button)findViewById(R.id.share);
                share.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Bitmap bitmap = takeScreenshot();
                        saveBitmap(bitmap);
            
                    }
                });
            

            //那么你必须创建两个方法

                public Bitmap takeScreenshot() {
                View rootView = findViewById(android.R.id.content).getRootView();
                rootView.setDrawingCacheEnabled(true);
                return rootView.getDrawingCache();
                }
            
                public void saveBitmap(Bitmap bitmap) {
                File imagePath = new File(Environment.getExternalStorageDirectory() + "/screenshot.png");
                FileOutputStream fos;
                try {
                    fos = new FileOutputStream(imagePath);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
                    fos.flush();
                    fos.close();
                } catch (FileNotFoundException e) {
                    Log.e("GREC", e.getMessage(), e);
                } catch (IOException e) {
                    Log.e("GREC", e.getMessage(), e);
                }
               }
            

            将此代码添加到您的应用程序后,运行该应用程序并检查您的本地存储,您已经创建了整个页面的屏幕截图。

            【讨论】:

              【解决方案12】:
              public static Bitmap loadBitmapFromView(ScrollView v) {
                  Bitmap b = Bitmap.createBitmap(v.getWidth() , v.getChildAt(0).getHeight(), Bitmap.Config.ARGB_8888);
                  Canvas c = new Canvas(b);
                  v.draw(c);
                  return b;
              }
              

              【讨论】:

              • 屏幕包含谷歌地图。它有效,但获取谷歌地图在屏幕截图中显示为黑色。
              【解决方案13】:

              截取一个视​​图,在参数中传递视图

              public static Bitmap getViewBitmap(View v) {
                  v.clearFocus();
                  v.setPressed(false);
              
                  boolean willNotCache = v.willNotCacheDrawing();
                  v.setWillNotCacheDrawing(false);
              
                  int color = v.getDrawingCacheBackgroundColor();
                  v.setDrawingCacheBackgroundColor(0);
              
                  if (color != 0) {
                      v.destroyDrawingCache();
                  }
                  v.buildDrawingCache();
                  Bitmap cacheBitmap = v.getDrawingCache();
                  if (cacheBitmap == null) {
                      return null;
                  }
              
                  Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
              
                  v.destroyDrawingCache();
                  v.setWillNotCacheDrawing(willNotCache);
                  v.setDrawingCacheBackgroundColor(color);
              
                  return bitmap;
              }
              

              【讨论】:

                【解决方案14】:
                fun View.getScreenShot():Bitmap{
                    return Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888).apply { 
                        draw(Canvas(this))
                    }
                }
                

                【讨论】:

                  【解决方案15】:

                  如提到here,你可以使用View::drawToBitmap函数:

                  val bitmap = myView.drawToBitmap(/*Optional:*/ Bitmap.Config.ARGB_8888)
                  

                  只要确保使用AndroidX Core库的-ktx版本:

                  implementation("androidx.core:core-ktx:1.6.0")
                  

                  注意:-ktx 版本的库与非 ktx 版本的库相同,只是它们包含有用的 Kotlin 扩展函数。

                  【讨论】:

                    猜你喜欢
                    • 2018-01-19
                    • 2019-10-31
                    • 2013-07-27
                    • 2018-04-02
                    • 2021-04-16
                    • 2016-08-31
                    • 2021-07-12
                    • 1970-01-01
                    • 2011-01-20
                    相关资源
                    最近更新 更多