问题描述
我在这里遵循代码,但是无法捕获我的ArFragment。请帮我。 https://codelabs.developers.google.com/codelabs/sceneform-intro/index.html?index=..%2F..io2018#15
该项目的链接:https://github.com/DSparda/Breakable-Cam
始终获取IOException:“无法将位图保存到磁盘”
我的WritingArFragment:
class FaceArFragment : ArFragment() {
override fun getSessionConfiguration(session: Session?): Config {
return Config(session).apply { augmentedFaceMode = Config.AugmentedFaceMode.MESH3D }
}
override fun getSessionFeatures(): MutableSet<Session.Feature> {
return EnumSet.of(Session.Feature.FRONT_CAMERA)
}
override fun getAdditionalPermissions(): Array<String?>? {
val additionalPermissions = super.getAdditionalPermissions()
val permissionLength =
additionalPermissions?.size ?: 0
val permissions =
arrayOfNulls<String>(permissionLength + 1)
permissions[0] = Manifest.permission.WRITE_EXTERNAL_STORAGE
if (permissionLength > 0) {
System.arraycopy(
additionalPermissions,permissions,1,additionalPermissions!!.size
)
}
return permissions
}
/**
* Override to turn off planediscoveryController. Plane trackables are not supported with the
* front camera.
*/
override fun onCreateView(
inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
): View {
val layout = super.onCreateView(inflater,container,savedInstanceState) as FrameLayout
planediscoveryController.apply {
hide()
setInstructionView(null)
}
return layout
}
}
我的活动XML:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="takePictureviewmodel"
type="com.example.breakablecam.screens.takingPicture.TakePictureviewmodel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/face_fragment_cointanier"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/takePhotoView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<fragment
android:id="@+id/face_fragment"
android:name="com.example.breakablecam.screens.takingPicture.FaceArFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.fragment.app.FragmentContainerView>
<ImageView
android:id="@+id/backArrow"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/stickerView"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginBottom="16dp"
android:onClick="@{() -> takePictureviewmodel.tapSticker()}"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/takePhotoView"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/stickerView1"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:visibility="gone"
android:onClick="@{() -> takePictureviewmodel.tapSticker1()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/takePhotoView"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginBottom="16dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/makeupView"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/stickerView" />
<ImageView
android:id="@+id/makeupView"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginBottom="16dp"
android:onClick="@{() -> takePictureviewmodel.tapMakeupView()}"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/takePhotoView" />
<ImageView
android:id="@+id/makeupView1a"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:onClick="@{() -> takePictureviewmodel.tapMakeup1aView()}"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
<ImageView
android:id="@+id/makeupView1"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginStart="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/makeupView1a"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
我的活动:
class TakePictureActivity : AppCompatActivity() {
private lateinit var viewmodel: TakePictureviewmodel
private lateinit var arFragment: FaceArFragment
private var modelRenderable: ModelRenderable? = null
private var meshTexture: Texture? = null
private val faceNodeMap = HashMap<AugmentedFace,AugmentedFaceNode?>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityTakePictureBinding =
DataBindingUtil.setContentView(this,R.layout.activity_take_picture)
viewmodel = viewmodelProvider(this).get(TakePictureviewmodel::class.java)
binding.apply {
viewmodel.apply {
setStickerViewSource(stickerView)
setMakeupViewSource(makeupView)
setTakePhotoViewSource(takePhotoView)
setMakeup1ViewSource(makeupView1)
setMakeup1aViewSource(makeupView1a)
setSticker1ViewSource(stickerView1)
setBackArrowSource(backArrow)
}
}
binding.takePictureviewmodel = viewmodel
binding.lifecycleOwner = this
viewmodel.makeupTap.observe(this,Observer { check ->
if (check == 1) {
binding.apply {
makeupView.visibility = GONE
takePhotoView.visibility = GONE
stickerView.visibility = GONE
makeupView1.visibility = VISIBLE
makeupView1a.visibility = VISIBLE
backArrow.visibility = VISIBLE
val params =
faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
params.bottomToTop = R.id.makeupView1
}
viewmodel.doneTapMakeup()
}
})
viewmodel.stickerTap.observe(this,Observer { check ->
if (check == 1) {
binding.apply {
makeupView.visibility = GONE
takePhotoView.visibility = GONE
stickerView.visibility = GONE
stickerView1.visibility = VISIBLE
backArrow.visibility = VISIBLE
val params =
faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
params.bottomToTop = R.id.stickerView1
}
viewmodel.doneTapSticker()
}
})
binding.apply {
backArrow.setonClickListener {
makeupView.visibility = VISIBLE
takePhotoView.visibility = VISIBLE
stickerView.visibility = VISIBLE
makeupView1.visibility = GONE
makeupView1a.visibility = GONE
backArrow.visibility = GONE
stickerView1.visibility = GONE
backArrow.visibility = GONE
val params =
faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
params.bottomToTop = R.id.takePhotoView
}
}
arFragment = supportFragmentManager.findFragmentById(R.id.face_fragment) as FaceArFragment
val sceneView = arFragment.arSceneView
sceneView.cameraStreamRenderPriority = Renderable.RENDER_PRIORITY_FirsT
val scene = sceneView.scene
loadTexture(R.drawable.makeup1a)
loadModel(R.raw.fox_face)
viewmodel.makeupTap1a.observe(this,Observer { check ->
when (check) {
1 -> {
scene.addOnUpdateListener {
val collection: Collection<AugmentedFace>? =
sceneView.session?.getAllTrackables(AugmentedFace::class.java)
collection?.forEach { face ->
if (!faceNodeMap.containsKey(face)) {
val faceNode = AugmentedFaceNode(face)
faceNode.apply {
setParent(scene)
faceMeshTexture = meshTexture
}
faceNodeMap[face] = faceNode
}
}
val iterator = faceNodeMap.entries.iterator()
while (iterator.hasNext()) {
val entry = iterator.next()
val face = entry.key
if (face.trackingState == TrackingState.STOPPED) {
val faceNode = entry.value
faceNode!!.setParent(null)
iterator.remove()
}
}
}
}
2 -> {
val children: List<Node> =
ArrayList(arFragment.arSceneView.scene.children)
for (node in children) {
if (node is AnchorNode) {
if (node.anchor != null) {
node.anchor?.detach()
}
}
if (node !is Camera && node !is Sun) {
node.setParent(null)
}
}
}
}
})
viewmodel.sticker1Tap.observe(this,Observer { check ->
when (check) {
1 -> {
scene.addOnUpdateListener {
val collection: Collection<AugmentedFace>? =
sceneView.session?.getAllTrackables(AugmentedFace::class.java)
collection?.forEach { face ->
if (!faceNodeMap.containsKey(face)) {
val faceNode = AugmentedFaceNode(face)
faceNode.apply {
setParent(scene)
faceRegionsRenderable = modelRenderable
}
faceNodeMap[face] = faceNode
}
}
val iterator = faceNodeMap.entries.iterator()
while (iterator.hasNext()) {
val entry = iterator.next()
val face = entry.key
if (face.trackingState == TrackingState.STOPPED) {
val faceNode = entry.value
faceNode!!.setParent(null)
iterator.remove()
}
}
}
}
2 -> {
val children: List<Node> =
ArrayList(arFragment.arSceneView.scene.children)
for (node in children) {
if (node is AnchorNode) {
if (node.anchor != null) {
node.anchor?.detach()
}
}
if (node !is Camera && node !is Sun) {
node.setParent(null)
}
}
}
}
})
binding.takePhotoView.setonClickListener {
takePhoto()
}
}
private fun loadTexture(tex: Int) {
Texture.builder()
.setSource(this,tex)
.build()
.thenAccept { texture -> meshTexture = texture }
}
private fun loadModel(mod: Int) {
ModelRenderable.builder()
.setSource(this,mod)
.build()
.thenAccept { model ->
model.apply {
isShadowCaster = false // optional
isShadowReceiver = false
}
modelRenderable = model
}
}
private fun generateFilename(): String? {
val date =
SimpleDateFormat("yyyyMMddHHmmss",Locale.getDefault())
.format(Date())
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
).toString() + File.separator + "SceneForm/" + date + "_screenshot.jpg"
}
@Throws(IOException::class)
private fun saveBitmapTodisk(bitmap: Bitmap,filename: String) {
val out = File(filename)
if (!out.parentFile.exists()) {
out.parentFile.mkdirs()
}
try {
FileOutputStream(filename).use { outputStream ->
ByteArrayOutputStream().use { outputData ->
bitmap.compress(Bitmap.CompressFormat.PNG,100,outputData)
outputData.writeto(outputStream)
outputStream.flush()
outputStream.close()
}
}
} catch (ex: IOException) {
throw IOException("Failed to save bitmap to disk",ex)
}
}
private fun takePhoto() {
val filename = generateFilename()
val view: ArSceneView = arFragment.getArSceneView()
// Create a bitmap the size of the scene view.
val bitmap = Bitmap.createBitmap(
view.width,view.height,Bitmap.Config.ARGB_8888
)
// Create a handler thread to offload the processing of the image.
val handlerThread = HandlerThread("Pixelcopier")
handlerThread.start()
// Make the request to copy.
Pixelcopy.request(view,bitmap,{ copyResult ->
if (copyResult === Pixelcopy.SUCCESS) {
try {
saveBitmapTodisk(bitmap,filename!!)
} catch (e: IOException) {
val toast = Toast.makeText(
this,e.toString(),Toast.LENGTH_LONG
)
toast.show()
return@request
}
val snackbar = Snackbar.make(
findViewById(android.R.id.content),"Photo saved",Snackbar.LENGTH_LONG
)
snackbar.setAction(
"Open in Photos"
) { v: View? ->
val photoFile = File(filename)
val photoURI = FileProvider.getUriForFile(
this,this.getPackageName()
.toString() + ".ar.codelab.name.provider",photoFile
)
val intent = Intent(Intent.ACTION_VIEW,photoURI)
intent.setDataAndType(photoURI,"image/*")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
}
snackbar.show()
} else {
val toast = Toast.makeText(
this,"Failed to copyPixels: $copyResult",Toast.LENGTH_LONG
)
toast.show()
}
handlerThread.quitSafely()
},Handler(handlerThread.looper))
}
}
非常感谢大家。
解决方法
尝试添加
<application android:requestLegacyExternalStorage="true" ... > ... </application>
显示在清单上。
我认为这与Android 11中即将实施的范围存储实施有关。
,您必须在代码实验室(权限和文件提供者)中指定的AndroidManifest.xml中添加标签。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.ar.codelab.name.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths"/>
</provider>
然后,您必须从使用File迁移到使用Uris和ContentResolvers,因为Android 10弃用了File功能,并且您必须使用MediaStore。
https://developer.android.com/training/data-storage/shared/media