【问题标题】:Sharing Bitmap via Android Intent通过 Android Intent 共享位图
【发布时间】:2016-01-18 07:14:50
【问题描述】:

在我的 android 应用程序中,我有一个位图(比如 b)和一个按钮。现在,当我单击按钮时,我想共享位图。我正在使用onClick() 中的以下代码来实现这一点:-

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/png");
intent.putExtra(Intent.EXTRA_STREAM, b);
startActivity(Intent.createChooser(intent , "Share"));

我期待一份能够处理此意图的所有应用程序的列表,但我一无所获。 android studio 中没有应用列表,也没有任何错误。我的应用程序只是被挂起一段时间然后退出。

我检查了位图,它很好(它不为空)。

我哪里出错了?

【问题讨论】:

标签: android android-intent bitmap intentfilter


【解决方案1】:

我找到了解决方案的 2 个变体。两者都涉及将位图保存到存储中,但图像不会出现在图库中。

第一个变体:

保存到外部存储

  • 但到应用程序的私人文件夹。
  • 对于 API

1。设置权限

添加到AndroidManifest.xml标签之前

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18"/>

2。保存方法:

 /**
 * Saves the image as PNG to the app's private external storage folder.
 * @param image Bitmap to save.
 * @return Uri of the saved file or null
 */
private Uri saveImageExternal(Bitmap image) {
    //TODO - Should be processed in another thread
    Uri uri = null;
    try {
        File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "to-share.png");
        FileOutputStream stream = new FileOutputStream(file);
        image.compress(Bitmap.CompressFormat.PNG, 90, stream);
        stream.close();
        uri = Uri.fromFile(file);
    } catch (IOException e) {
        Log.d(TAG, "IOException while trying to write file for sharing: " + e.getMessage());
    }
    return uri;
}

3。检查存储可访问性

外部存储可能无法访问,因此您应该在尝试保存之前检查它:https://developer.android.com/training/data-storage/files

/**
 * Checks if the external storage is writable.
 * @return true if storage is writable, false otherwise
 */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

第二个变种

使用 FileProvider 保存到 cacheDir。它不需要任何权限。

1。在 AndroidManifest.xml 中设置 FileProvider

<manifest>
    ...
    <application>
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.mydomain.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths" />
        </provider>
        ...
    </application>
</manifest>

2。添加 res/xml/file_paths.xml 的路径

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
         <cache-path name="shared_images" path="images/"/>
    </paths>
</resources>

3。保存方法:

 /**
 * Saves the image as PNG to the app's cache directory.
 * @param image Bitmap to save.
 * @return Uri of the saved file or null
 */
private Uri saveImage(Bitmap image) {
    //TODO - Should be processed in another thread
    File imagesFolder = new File(getCacheDir(), "images");
    Uri uri = null;
    try {
        imagesFolder.mkdirs();
        File file = new File(imagesFolder, "shared_image.png");

        FileOutputStream stream = new FileOutputStream(file);
        image.compress(Bitmap.CompressFormat.PNG, 90, stream);
        stream.flush();
        stream.close();
        uri = FileProvider.getUriForFile(this, "com.mydomain.fileprovider", file);

    } catch (IOException e) {
        Log.d(TAG, "IOException while trying to write file for sharing: " + e.getMessage());
    }
    return uri;
}

有关文件提供程序的更多信息 - https://developer.android.com/reference/android/support/v4/content/FileProvider

压缩和保存可能很耗时,因此应该在不同的线程中完成

实际分享

/**
 * Shares the PNG image from Uri.
 * @param uri Uri of image to share.
 */
private void shareImageUri(Uri uri){
    Intent intent = new Intent(android.content.Intent.ACTION_SEND);
    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.setType("image/png");
    startActivity(intent);
}

【讨论】:

  • 这是我认为最好的解决方案,一步一步详细解释。如果考虑安全性,与存储在缓存中的图像共享会更好。我错过了添加 xml 和提供程序的步骤,因此它无法正常工作,但最终按照这些步骤操作,现在可以正常工作了。
  • 互联网上通过电子邮件共享的最佳解决方案。
  • 自 Android X 以来 FileProvider 的新包名称为:androidx.core.content.FileProvider
【解决方案2】:

正如 CommonsWare 所说,您需要获取位图的 URI 并将其作为您的 Extra 传递。

String bitmapPath = Images.Media.insertImage(getContentResolver(), bitmap,"title", null);
Uri bitmapUri = Uri.parse(bitmapPath);
...
intent.putExtra(Intent.EXTRA_STREAM, bitmapUri );

【讨论】:

  • 它可以工作,但也可以存储图像。因此,图像也显示在画廊中。我怎样才能避免保存图像或将其显示到画廊中。
  • @JaydipKalkani 我也需要这个,但也许我们不能。
【解决方案3】:

终于,我得到了解决方案:

第 1 步: 共享意图处理块。这将弹出您的窗口,其中包含您手机中的应用程序列表

public void share_bitMap_to_Apps() {
    Intent i = new Intent(Intent.ACTION_SEND);

    i.setType("image/*");
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    /*compress(Bitmap.CompressFormat.PNG, 100, stream);
    byte[] bytes = stream.toByteArray();*/

    i.putExtra(Intent.EXTRA_STREAM, getImageUri(mContext, getBitmapFromView(relative_me_other)));
    try {
        startActivity(Intent.createChooser(i, "My Profile ..."));
    } catch (android.content.ActivityNotFoundException ex) {
        ex.printStackTrace();
    }
}

第 2 步: 将视图转换为位图

public static Bitmap getBitmapFromView(View view) {
    // Define a bitmap with the same size as the view
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    // Bind a canvas to it
    Canvas canvas = new Canvas(returnedBitmap);
    // Get the view's background
    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);
    // draw the view on the canvas
    view.draw(canvas);
    // return the bitmap
    return returnedBitmap;
}

第 3 步: 从位图图像中获取 URI

public Uri getImageUri(Context inContext, Bitmap inImage) {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);

    String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
    return Uri.parse(path);
}

【讨论】:

  • 别忘了要求 android.permission.WRITE_EXTERNAL_STORAGE
  • 在不写外部存储的情况下有什么解决方案吗?
【解决方案4】:
ImageButton capture_share = (ImageButton) findViewById(R.id.share);
capture_share.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {

    String bitmapPath = MediaStore.Images.Media.insertImage(getContentResolver(), bitmap,"title", null);
    Uri bitmapUri = Uri.parse(bitmapPath);

        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("image/png");
        intent.putExtra(Intent.EXTRA_STREAM, bitmapUri);
        startActivity(Intent.createChooser(intent, "Share"));
  }
});

【讨论】:

  • 添加一些描述来回答,以便清楚理解。 @lalit Baghel
【解决方案5】:

引用the documentation:

内容:URI 保存与 Intent 关联的数据流,与 ACTION_SEND 一起使用以提供正在发送的数据。

因此,b 不应该是Bitmap,而是指向BitmapUri,由ContentProvider 提供服务。例如,您可以将Bitmap 写入文件,然后使用FileProvider 提供服务。

【讨论】:

    【解决方案6】:
    private void sharePalette(Bitmap bitmap) {
    
     String bitmapPath = MediaStore.Images.Media.insertImage(getContentResolver(), bitmap, "palette", "share palette");
        Uri bitmapUri = Uri.parse(bitmapPath);
    
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("image/png");
        intent.putExtra(Intent.EXTRA_STREAM, bitmapUri);
        startActivity(Intent.createChooser(intent, "Share"));
    }
    

    【讨论】:

      【解决方案7】:

      在图像文件名中带有时间戳的 Kotlin 解决方案:

      val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
      val imagePath = MediaStore.Images.Media.insertImage(
          context?.contentResolver,
          bitmap,
          "img_$timeStamp",
          null
      )
      val shareIntent = Intent(Intent.ACTION_SEND).apply {
          type = "image/*"
          putExtra(Intent.EXTRA_STREAM, Uri.parse(imagePath))
      }
      context?.startActivity(Intent.createChooser(shareIntent, null))
      

      【讨论】:

      • insertImage 在 API 级别 29 中已弃用。应使用 MediaColumns#IS_PENDING 执行图像的插入,它提供对生命周期的更丰富的控制。(也将很快弃用)大声笑)
      【解决方案8】:

      在花了很多时间之后:

      检查是否已授予权限。然后:

      第一步:在activity中创建你想要的图片的ImageView,然后转换成bitmap

      ImageView imageView = findViewById(R.id.image);
      Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
      
      //save the image now:
      saveImage(bitmap);
      //share it
      send();
      

      第 2 步:将图像存储在内部文件夹中:

      private static void saveImage(Bitmap finalBitmap) {
      
          String root = Environment.getExternalStorageDirectory().getAbsolutePath();
          File myDir = new File(root + "/saved_images");
          Log.i("Directory", "==" + myDir);
          myDir.mkdirs();
      
          String fname = "Image-test" + ".jpg";
          File file = new File(myDir, fname);
          if (file.exists()) file.delete();
          try {
              FileOutputStream out = new FileOutputStream(file);
              finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
              out.flush();
              out.close();
      
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      

      第 3 步:发送保存的图片:

      public void send() {
          try {
              File myFile = new File("/storage/emulated/0/saved_images/Image-test.jpg");
              MimeTypeMap mime = MimeTypeMap.getSingleton();
              String ext = myFile.getName().substring(myFile.getName().lastIndexOf(".") + 1);
              String type = mime.getMimeTypeFromExtension(ext);
              Intent sharingIntent = new Intent("android.intent.action.SEND");
              sharingIntent.setType(type);
              sharingIntent.putExtra("android.intent.extra.STREAM", Uri.fromFile(myFile));
              startActivity(Intent.createChooser(sharingIntent, "Share using"));
          } catch (Exception e) {
              Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
          }
      }
      

      现在发送后,如果您不希望保存的图像在您的存储中,您可以删除它。检查其他链接以执行此操作。

      【讨论】:

        【解决方案9】:

        由于Images.Media.insertImage弃用,最好使用FileProvider 作为Java 和Kotlin 中提到的@APA 和@kjs566 :(我检查到SDK 31)

         val imageName = "/image.jpg"
                try {
                    File(this.cacheDir, "images").deleteRecursively() 
                    val cachePath = File(this.cacheDir, "images")
                    cachePath.mkdirs() 
                    val stream = FileOutputStream("$cachePath$imageName")
                    bitmap.compress(
                        Bitmap.CompressFormat.JPEG,90,stream) 
                    stream.close()
                } catch (ex: Exception) {}
        
                // SHARE
                val imagePath = File(this.cacheDir, "images")
                val newFile = File(imagePath, imageName)
                val contentUri = FileProvider.getUriForFile(this, "com.src.noveinway.fileprovider", newFile)
                if (contentUri != null) {
                    val shareIntent = Intent()
                    shareIntent.action = Intent.ACTION_SEND
                    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 
                    shareIntent.type ="image/jpeg" 
                    shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
                    shareIntent.putExtra(Intent.EXTRA_TEXT, "This is a file for share")
                    startActivity(Intent.createChooser(shareIntent, "Choose app"))
                }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-10-06
          • 1970-01-01
          • 2014-08-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多