2016.04.03   |   Android

Viewのキャプチャを画像化してインテントでシェアする

AndroidのViewを画像として保存してシェアしたい時の実装方法。
割とよく使うのにあまりまとめられてないのでまとめておきます。
Kotlinのコードで紹介するので、Javaの方は適宜読み替えてください。

手順

  • Viewをキャプチャする
  • キャプチャしたものを画像に保存する
  • 保存した画像をシェアIntentに渡す

Viewをキャプチャする

view.isDrawingCacheEnabled = true
view.destroyDrawingCache()
val bmp = view.drawingCache

キャプチャしたいViewに対して上記を行うだけでbitmapとして取得できます。簡単ですね。キャプチャできるのは実行したViewとその子Viewの描画内容のみです。FrameLayoutでたまたま重なっているViewや、背景にあるViewは取得できません。Backgroundが存在しないViewは背景が透明色となります。

キャプチャしたものを画像に保存する

画像をシェアするためにはBitmapを一旦保存する必要があります。Intentにバイナリをそのまま渡すことは出来ません。
また画像は他のアプリからも見える位置に置く必要があります。
アプリのキャッシュ領域等ではダメでした。
なので画像の保存先は必然的にメディアフォルダやSD上に書き出す必要がある(=相応のPermissionが必要)…と思っていたのですが、この前解決策を見つけました。

http://stackoverflow.com/questions/9049143/android-share-intent-for-a-bitmap-is-it-possible-not-to-save-it-prior-sharing

ContentProviderを使って画像を提供してあげればよいみたいです。言われてみれば納得ですが、シェアする目的でContentProviderを使うのは考えが及ばなかったです。
これを使うことで変なパーミッションを使う事無く画像のシェアが可能になります。

val cachePath = File(context.cacheDir, "images")
cachePath.mkdirs()
val filePath = File(cachePath, "Hogehoge.png")
val fos = FileOutputStream(filePath.absolutePath)
bitmap.compress(Bitmap.CompressFormat.PNG, 95, fos)
fos.close()

val contentUri = FileProvider.getUriForFile(context, "$packageName.fileprovider", filepath)

CacheDirにimages/という階層を作って、その中にPNGで画像を保存します。
そして保存したパスからContentProviderのURIを生成します。
このURIをIntentに渡せばシェアが出来ます。

# Manifest.xml
<provider
     android:name="android.support.v4.content.FileProvider"
     android:authorities="${applicationId}.fileprovider"
     android:grantUriPermissions="true"
     android:exported="false">
     <meta-data
           android:name="android.support.FILE_PROVIDER_PATHS"
           android:resource="@xml/filepaths" />
</provider>

ManifestにProviderを使う宣言と、

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

filepathを定義するxmlも忘れずに用意しておきましょう。

保存した画像をシェアIntentに渡す

val shareIntent = Intent()
shareIntent.action = Intent.ACTION_SEND
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
shareIntent.setDataAndType(contentUri, contentResolver.getType(contentUri))
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
shareIntent.putExtra(Intent.EXTRA_TEXT, "Viewをシェアするテスト!")
startActivity(Intent.createChooser(shareIntent, "アプリを選ぶ")))

先ほどのContentUriをExtraにセットしてIntentを起動してあげれば無事、他のアプリに画像をシェアすることが出来ます。