捕获 Google Map Android API V2 的屏幕截图

Posted

技术标签:

【中文标题】捕获 Google Map Android API V2 的屏幕截图【英文标题】:Capture screen shot of GoogleMap Android API V2 【发布时间】:2012-11-26 06:07:44 【问题描述】:

最终更新

Google 已满足功能请求。请看this answer below.

原始问题

使用旧版本的 Google Maps android API,我能够截取 google 地图的屏幕截图以通过社交媒体分享。我使用以下代码捕获屏幕截图并将图像保存到文件中,效果很好:

public String captureScreen()

    String storageState = Environment.getExternalStorageState();
    Log.d("StorageState", "Storage state is: " + storageState);

    // image naming and path  to include sd card  appending name you choose for file
    String mPath = this.getFilesDir().getAbsolutePath();

    // create bitmap screen capture
    Bitmap bitmap;
    View v1 = this.mapView.getRootView();
    v1.setDrawingCacheEnabled(true);
    bitmap = Bitmap.createBitmap(v1.getDrawingCache());
    v1.setDrawingCacheEnabled(false);

    OutputStream fout = null;

    String filePath = System.currentTimeMillis() + ".jpeg";

    try 
    
        fout = openFileOutput(filePath,
                MODE_WORLD_READABLE);

        // Write the string to the file
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
        fout.flush();
        fout.close();
     
    catch (FileNotFoundException e) 
    
        // TODO Auto-generated catch block
        Log.d("ImageCapture", "FileNotFoundException");
        Log.d("ImageCapture", e.getMessage());
        filePath = "";
     
    catch (IOException e) 
    
        // TODO Auto-generated catch block
        Log.d("ImageCapture", "IOException");
        Log.d("ImageCapture", e.getMessage());
        filePath = "";
    

    return filePath;

但是,API V2 使用的新 GoogleMap 对象没有 MapView 那样的“getRootView()”方法。

我尝试过这样做:

    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.basicMap);

    View v1 = mapFragment.getView();

但我得到的截图没有任何地图内容,看起来像这样:

有人知道如何截取新的 Google Maps Android API V2 的屏幕截图吗?

更新

我也试过这样获取rootView:

View v1 = getWindow().getDecorView().getRootView();

这会生成一个屏幕截图,其中包含屏幕顶部的操作栏,但地图仍然是空白的,就像我附上的屏幕截图一样。

更新

功能请求已提交给 Google。如果您希望 Google 将来添加此功能请求,请为功能请求添加星标:Add screenshot ability to Google Maps API V2

【问题讨论】:

现在他们使用向量是我在某些地方读到的。不知道该怎么做 【参考方案1】:

更新 - Google 添加了快照方法**!:

对获取 Android Google Map API V2 OpenGL 层屏幕截图的方法的功能请求已完成。

要截屏,只需实现以下接口:

public abstract void onSnapshotReady (Bitmap snapshot)

然后调用:

public final void snapshot (GoogleMap.SnapshotReadyCallback callback)

截取屏幕截图,然后显示标准“图像共享”选项的示例:

public void captureScreen()
    
        SnapshotReadyCallback callback = new SnapshotReadyCallback() 
        

            @Override
            public void onSnapshotReady(Bitmap snapshot) 
            
                // TODO Auto-generated method stub
                bitmap = snapshot;

                OutputStream fout = null;

                String filePath = System.currentTimeMillis() + ".jpeg";

                try 
                
                    fout = openFileOutput(filePath,
                            MODE_WORLD_READABLE);

                    // Write the string to the file
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
                    fout.flush();
                    fout.close();
                 
                catch (FileNotFoundException e) 
                
                    // TODO Auto-generated catch block
                    Log.d("ImageCapture", "FileNotFoundException");
                    Log.d("ImageCapture", e.getMessage());
                    filePath = "";
                 
                catch (IOException e) 
                
                    // TODO Auto-generated catch block
                    Log.d("ImageCapture", "IOException");
                    Log.d("ImageCapture", e.getMessage());
                    filePath = "";
                

                openShareImageDialog(filePath);
            
        ;

        mMap.snapshot(callback);
    

图像捕获完成后,它将触发标准的“共享图像”对话框,以便用户选择他们想要共享的方式:

public void openShareImageDialog(String filePath) 

File file = this.getFileStreamPath(filePath);

if(!filePath.equals(""))

    final ContentValues values = new ContentValues(2);
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
    final Uri contentUriFile = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

    final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
    intent.setType("image/jpeg");
    intent.putExtra(android.content.Intent.EXTRA_STREAM, contentUriFile);
    startActivity(Intent.createChooser(intent, "Share Image"));

else

            //This is a custom class I use to show dialogs...simply replace this with whatever you want to show an error message, Toast, etc.
    DialogUtilities.showOkDialogWithText(this, R.string.shareImageFailed);


文档是here

【讨论】:

你能举个例子吗! @David - 提供示例。 嗨@DiscDev,这段代码非常有用。但我想要一个解决方案:假设有一个包含地图和一些 UI 组件的活动。见:i57.tinypic.com/iglyef.png 现在我需要一张截图。这个怎么做 ?提前致谢 @Vrajesh - 我看不到你的图片,但我认为你不能捕捉地图中没有的 UI 元素......我认为你必须做的是捕获地图图像,然后分别捕获整个屏幕(整个屏幕捕获时地图将是空白的),然后稍后通过一些代码将 2 个图像重新组合在一起。 @Disc 开发。谢谢你的答复...!你说的对。我必须做两个单独的图像来截屏。一个用于整个屏幕,第二个仅显示地图。然后合并它们。这里有点混乱..!我怎样才能做到这一点 ?你有这样做的一些想法或参考..?更新图片位置 :: s13.postimg.org/5r99pibx3/test.png【参考方案2】:

以下是通过示例捕获 Google Map V2 屏幕截图的步骤

第一步打开Android Sdk Manager (Window > Android Sdk Manager)然后Expand Extras现在update/install Google Play Services to Revision 10如果已经installed忽略这一步

在此处阅读注释https://developers.google.com/maps/documentation/android/releases#august_2013

第 2 步。 Restart Eclipse

第 3 步。 import com.google.android.gms.maps.GoogleMap.SnapshotReadyCallback;

步骤 4. 制作方法来捕获/存储地图的屏幕/图像,如下所示

public void CaptureMapScreen() 

SnapshotReadyCallback callback = new SnapshotReadyCallback() 
            Bitmap bitmap;

            @Override
            public void onSnapshotReady(Bitmap snapshot) 
                // TODO Auto-generated method stub
                bitmap = snapshot;
                try 
                    FileOutputStream out = new FileOutputStream("/mnt/sdcard/"
                        + "MyMapScreen" + System.currentTimeMillis()
                        + ".png");

                    // above "/mnt ..... png" => is a storage path (where image will be stored) + name of image you can customize as per your Requirement

                    bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
                 catch (Exception e) 
                    e.printStackTrace();
                
            
        ;

        myMap.snapshot(callback);

        // myMap is object of GoogleMap +> GoogleMap myMap;
        // which is initialized in onCreate() => 
        // myMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map_pass_home_call)).getMap();

第 5 步。 现在在您要捕获图像的位置调用此 CaptureMapScreen() 方法

在我的情况下,我是 calling this method on Button click in my onCreate(),它工作正常

喜欢:

Button btnCap = (Button) findViewById(R.id.btnTakeScreenshot);
    btnCap.setOnClickListener(new OnClickListener() 

        @Override
        public void onClick(View v) 
            // TODO Auto-generated method stub
            try 
                CaptureMapScreen();
             catch (Exception e) 
                // TODO: handle exception
                e.printStackTrace();
            

        
    );

查看文档here和here

【讨论】:

我知道这是一个很老的帖子,但我有一个类似的问题,想看看你是否可以帮助解决它。我正在像这样初始化 myMap: myMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();我收到以下错误:txs.io/JBS,这是我拥有的 map_pass_home_call.xml 文件:txs.io/KBS。我不确定我哪里出错了。我总是收到空指针异常。【参考方案3】:

我截取了地图截图。会有所帮助

  private GoogleMap map;
 private static LatLng latLong;

`

public void onMapReady(GoogleMap googleMap) 
           map = googleMap;
           setMap(this.map);
           animateCamera();
            map.moveCamera (CameraUpdateFactory.newLatLng (latLong));
            map.setOnMapLoadedCallback (new GoogleMap.OnMapLoadedCallback () 
                @Override
                public void onMapLoaded() 
                    snapShot();
                
            );
        

`

snapShot() 方法截取地图

 public void snapShot()
    GoogleMap.SnapshotReadyCallback callback=new GoogleMap.SnapshotReadyCallback () 
        Bitmap bitmap;
        @Override
        public void onSnapshotReady(Bitmap snapshot) 
            bitmap=snapshot;

            try
                file=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"map.png");
                FileOutputStream fout=new FileOutputStream (file);
                bitmap.compress (Bitmap.CompressFormat.PNG,90,fout);
                Toast.makeText (PastValuations.this, "Capture", Toast.LENGTH_SHORT).show ();

            catch (Exception e)
                e.printStackTrace ();
                Toast.makeText (PastValuations.this, "Not Capture", Toast.LENGTH_SHORT).show ();
            


        
    ;map.snapshot (callback);

我的输出低于

【讨论】:

这很好,它正在将图片保存在图片文件夹中..【参考方案4】:

编辑:此答案不再有效 - Google Maps Android API V2 上的屏幕截图功能请求已得到满足。见this answer for an example。

接受的原始答案

由于新的 Android API v2 地图使用 OpenGL 显示,因此无法创建屏幕截图。

【讨论】:

你能引用这个答案的来源吗? 我主要从官方API docs得到这个,另外还有this post 我见过这样的问题:***.com/questions/3310990/… 不过,这似乎表明可以截取 OpenGL 层的屏幕截图......虽然我对 OpenGL 几乎一无所知,所以这就是为什么我我正在寻求帮助... 问题是用于显示地图的OpenGL SurfaceView通过MapView/MapFragment被黑盒化掉了... 您可以为以下功能请求投票/加注星标,以便 Google 在 API 中添加屏幕截图支持:code.google.com/p/gmaps-api-issues/issues/detail?id=4898【参考方案5】:

由于投票最多的答案不适用于地图片段顶部的折线和其他叠加层(我在寻找什么),我想分享这个解决方案。

public void captureScreen()
        
            GoogleMap.SnapshotReadyCallback callback = new GoogleMap.SnapshotReadyCallback()
            


                @Override
                public void onSnapshotReady(Bitmap snapshot) 
                    try 
                        getWindow().getDecorView().findViewById(android.R.id.content).setDrawingCacheEnabled(true);
                        Bitmap backBitmap = getWindow().getDecorView().findViewById(android.R.id.content).getDrawingCache();
                        Bitmap bmOverlay = Bitmap.createBitmap(
                                backBitmap.getWidth(), backBitmap.getHeight(),
                                backBitmap.getConfig());
                        Canvas canvas = new Canvas(bmOverlay);
                        canvas.drawBitmap(snapshot, new Matrix(), null);
                        canvas.drawBitmap(backBitmap, 0, 0, null);

                        OutputStream fout = null;

                        String filePath = System.currentTimeMillis() + ".jpeg";

                        try
                        
                            fout = openFileOutput(filePath,
                                    MODE_WORLD_READABLE);

                            // Write the string to the file
                            bmOverlay.compress(Bitmap.CompressFormat.JPEG, 90, fout);
                            fout.flush();
                            fout.close();
                        
                        catch (FileNotFoundException e)
                        
                            // TODO Auto-generated catch block
                            Log.d("ImageCapture", "FileNotFoundException");
                            Log.d("ImageCapture", e.getMessage());
                            filePath = "";
                        
                        catch (IOException e)
                        
                            // TODO Auto-generated catch block
                            Log.d("ImageCapture", "IOException");
                            Log.d("ImageCapture", e.getMessage());
                            filePath = "";
                        

                        openShareImageDialog(filePath);


                     catch (Exception e) 
                        e.printStackTrace();
                    
                
            ;

           ;


            map.snapshot(callback);
        

【讨论】:

公认的答案绝对适用于折线和叠加层——这就是共享地图图像的全部意义所在,因为我画了一堆折线、标记和叠加层,并想捕捉它。您将折线添加到地图的方式可能存在问题?【参考方案6】:
private GoogleMap mMap;
SupportMapFragment mapFragment;
LinearLayout linearLayout;
String jobId="1";

文件文件;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate (savedInstanceState);
    setContentView (R.layout.activity_maps);

    linearLayout=(LinearLayout)findViewById (R.id.linearlayout);
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
     mapFragment = (SupportMapFragment)getSupportFragmentManager ()
            .findFragmentById (R.id.map);
    mapFragment.getMapAsync (this);
    //Taking Snapshot of Google Map






/**
 * Manipulates the map once available.
 * This callback is triggered when the map is ready to be used.
 * This is where we can add markers or lines, add listeners or move the camera. In this case,
 * we just add a marker near Sydney, Australia.
 * If Google Play services is not installed on the device, the user will be prompted to install
 * it inside the SupportMapFragment. This method will only be triggered once the user has
 * installed Google Play services and returned to the app.
 */
@Override
public void onMapReady(GoogleMap googleMap) 
    mMap = googleMap;

    // Add a marker in Sydney and move the camera
    LatLng sydney = new LatLng (-26.888033, 75.802754);
    mMap.addMarker (new MarkerOptions ().position (sydney).title ("Kailash Tower"));
    mMap.moveCamera (CameraUpdateFactory.newLatLng (sydney));
    mMap.setOnMapLoadedCallback (new GoogleMap.OnMapLoadedCallback () 
        @Override
        public void onMapLoaded() 
            snapShot();
        
    );


// Initializing Snapshot Method
public void snapShot()
    GoogleMap.SnapshotReadyCallback callback=new GoogleMap.SnapshotReadyCallback () 
        Bitmap bitmap;
        @Override
        public void onSnapshotReady(Bitmap snapshot) 
            bitmap=snapshot;
            bitmap=getBitmapFromView(linearLayout);
            try
               file=new File (getExternalCacheDir (),"map.png");
                FileOutputStream fout=new FileOutputStream (file);
                bitmap.compress (Bitmap.CompressFormat.PNG,90,fout);
                Toast.makeText (MapsActivity.this, "Capture", Toast.LENGTH_SHORT).show ();
                sendSceenShot (file);
            catch (Exception e)
                e.printStackTrace ();
                Toast.makeText (MapsActivity.this, "Not Capture", Toast.LENGTH_SHORT).show ();
            


        
    ;mMap.snapshot (callback);

private Bitmap getBitmapFromView(View view) 
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas (returnedBitmap);
    Drawable bgDrawable =view.getBackground();
    if (bgDrawable!=null) 
        //has background drawable, then draw it on the canvas
        bgDrawable.draw(canvas);
       else
        //does not have background drawable, then draw white background on the canvas
        canvas.drawColor(Color.WHITE);
    
    view.draw(canvas);
    return returnedBitmap;


//Implementing Api using Retrofit
private void sendSceenShot(File file) 
    RequestBody job=null;

    Gson gson = new GsonBuilder ()
            .setLenient ()
            .create ();

    Retrofit retrofit = new Retrofit.Builder ()
            .baseUrl (BaseUrl.url)
            .addConverterFactory (GsonConverterFactory.create (gson))
            .build ();

    final RequestBody requestBody = RequestBody.create (MediaType.parse ("image/*"),file);
    job=RequestBody.create (MediaType.parse ("text"),jobId);


    MultipartBody.Part  fileToUpload = MultipartBody.Part.createFormData ("name",file.getName (), requestBody);

    API service = retrofit.create (API.class);
    Call<ScreenCapture_Pojo> call=service.sendScreen (job,fileToUpload);
    call.enqueue (new Callback<ScreenCapture_Pojo> () 
        @Override
        public void onResponse(Call <ScreenCapture_Pojo> call, Response<ScreenCapture_Pojo> response) 
            if (response.body ().getMessage ().equalsIgnoreCase ("Success"))
                Toast.makeText (MapsActivity.this, "success", Toast.LENGTH_SHORT).show ();
            
        

        @Override
        public void onFailure(Call <ScreenCapture_Pojo> call, Throwable t) 

        
    );


【讨论】:

你好,成功的代码。 我希望这将有助于捕获地图的屏幕截图,并且您可以使用改造在服务器上发送而无需保存到外部存储中。请发表评论。 截图示例,然后呈现标准的“图像发送。这个完整的源代码。请订阅我的页面。 如果特定视图包含 ImageView、TextView 和 mapView(都带有值),我会截屏吗?【参考方案7】:

我希望这将有助于捕获您的地图的屏幕截图

方法调用:

gmap.setOnMapLoadedCallback(mapLoadedCallback);

方法声明:

final SnapshotReadyCallback snapReadyCallback = new SnapshotReadyCallback() 
        Bitmap bitmap;
        @Override
        public void onSnapshotReady(Bitmap snapshot) 
            bitmap = snapshot;

            try 

                //do something with your snapshot

                imageview.setImageBitmap(bitmap);

             catch (Exception e) 
                e.printStackTrace();
            


        
    ;

GoogleMap.OnMapLoadedCallback mapLoadedCallback = new GoogleMap.OnMapLoadedCallback() 
        @Override
        public void onMapLoaded() 
            gmap.snapshot(snapReadyCallback);
        
;

【讨论】:

【参考方案8】:

使用mapcalback获取地图截图

 GoogleMap.SnapshotReadyCallback callback = new GoogleMap.SnapshotReadyCallback() 
                    Bitmap bitmap=null;
                    @Override
                    public void onSnapshotReady(Bitmap snapshot) 
                        // TODO Auto-generated method stub
                        bitmap = snapshot;
                        try 
                            saveImage(bitmap);
                            Toast.makeText(getActivity().getApplicationContext(), "Successful.Screenshot Saved.", Toast.LENGTH_LONG).show();
                         catch (Exception e) 
                            e.printStackTrace();
                        
                    

                    private void saveImage(Bitmap bitmap) throws IOException 
                        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);

                        Date date = new Date();
                        CharSequence format = android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", date);
                        String dirpath = Environment.getExternalStorageDirectory() + "";
                        File file = new File(dirpath);
                        if (!file.exists()) 
                            boolean mkdir = file.mkdir();
                        
                        String path1 = dirpath + "/Documents/SCREENSHOT/";
                        File imageurl1 = new File(path1);
                        imageurl1.mkdirs();
                        File f = new File(path1 +"/"+ format + ".png");
                        f.createNewFile();
                        FileOutputStream fo = new FileOutputStream(f);
                        fo.write(bytes.toByteArray());
                        fo.close();
                    
                ;
                mMap.snapshot(callback);

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案9】:

即使是谷歌地图 V2,Eclipse DDMS 也可以捕获屏幕。

如果您有“root”权限,请尝试调用 /system/bin/screencap 或 /system/bin/screenshot。我从How Eclipse android DDMS implement "screen capture"了解到这一点

【讨论】:

是的,我知道这一点,但我希望能够在非根设备上以编程方式(即在代码中)截取屏幕截图。这在 Maps API for Android 的第一个版本中是可能的,但不是第二个版本(由于接受的答案中提到的原因)。 是的,这也是我的问题。但是如果谷歌地图V2似乎不可能。 DDMS 使用通过具有必要权限的进程(adb pull)提供的screencap 或screensnap android。但是普通的android应用程序没有权限(我昨天从***找到)。希望下个版本的谷歌地图能加强它。 是的,令人沮丧的是他们没有提供 OpenGL 层,但我想这与版权或安全有关。

以上是关于捕获 Google Map Android API V2 的屏幕截图的主要内容,如果未能解决你的问题,请参考以下文章

打开eclipse后应该怎样安装 Google map API ?在Android sDK Manager列表中没有找到Google map API 这应该

如何在 android 上使用 google map api 获取 google 驾驶指令?

如何在现有的旧版Android项目中使用最新的API添加Google Map?

如何在现有较旧的 android 项目中使用最新 API 添加 Google Map?

如何在 Google Map Api V2 Android 中搜索?

使用google map direction api android计算两个位置之间的距离