我尝试使用 https://developer.android.com/guide/topics/media/mediarecorder 和 MVVM 和数据绑定制作一个录音机应用程序。我在视图模型中有 AudioRecordTest 类,但不知道如何从视图中使用它来将它连接到 xml 文件中的按钮
RecordViewModel.kt
package com.example.thedictaphone.viewmodels
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.media.MediaPlayer
import android.media.MediaRecorder
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import java.io.IOException
private const val LOG_TAG = "AudioRecordTest"
private const val REQUEST_RECORD_AUDIO_PERMISSION = 200
class RecordViewModel: ViewModel() {
//private val _name = MutableLiveData("Ada")
//val name: LiveData<String> = _name
}
class AudioRecordTest : AppCompatActivity() {
private var fileName: String = ""
private var recordButton: RecordButton? = null
private var recorder: MediaRecorder? = null
private var playButton: PlayButton? = null
private var player: MediaPlayer? = null
// Requesting permission to RECORD_AUDIO
private var permissionToRecordAccepted = false
private var permissions: Array<String> = arrayOf(Manifest.permission.RECORD_AUDIO)
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
permissionToRecordAccepted = if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
grantResults[0] == PackageManager.PERMISSION_GRANTED
} else {
false
}
if (!permissionToRecordAccepted) finish()
}
fun onRecord(start: Boolean) = if (start) {
startRecording()
} else {
stopRecording()
}
private fun onPlay(start: Boolean) = if (start) {
startPlaying()
} else {
stopPlaying()
}
fun startPlaying() {
player = MediaPlayer().apply {
try {
setDataSource(fileName)
prepare()
start()
} catch (e: IOException) {
Log.e(LOG_TAG, "prepare() failed")
}
}
}
private fun stopPlaying() {
player?.release()
player = null
}
private fun startRecording() {
recorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
setOutputFile(fileName)
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
try {
prepare()
} catch (e: IOException) {
Log.e(LOG_TAG, "prepare() failed")
}
start()
}
}
private fun stopRecording() {
recorder?.apply {
stop()
release()
}
recorder = null
}
internal inner class RecordButton(ctx: Context) : androidx.appcompat.widget.AppCompatButton(ctx) {
var mStartRecording = true
var clicker: OnClickListener = OnClickListener {
onRecord(mStartRecording)
text = when (mStartRecording) {
true -> "Stop recording"
false -> "Start recording"
}
mStartRecording = !mStartRecording
}
init {
text = "Start recording"
setOnClickListener(clicker)
}
}
internal inner class PlayButton(ctx: Context) : androidx.appcompat.widget.AppCompatButton(ctx) {
var mStartPlaying = true
var clicker: OnClickListener = OnClickListener {
onPlay(mStartPlaying)
text = when (mStartPlaying) {
true -> "Stop playing"
false -> "Start playing"
}
mStartPlaying = !mStartPlaying
}
init {
text = "Start playing"
setOnClickListener(clicker)
}
}
override fun onCreate(icicle: Bundle?) {
super.onCreate(icicle)
// Record to the external cache directory for visibility
fileName = "${externalCacheDir?.absolutePath}/audiorecordtest.3gp"
ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION)
recordButton = RecordButton(this)
playButton = PlayButton(this)
val ll = LinearLayout(this).apply {
addView(recordButton,
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
0f))
addView(playButton,
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
0f))
}
setContentView(ll)
}
override fun onStop() {
super.onStop()
recorder?.release()
recorder = null
player?.release()
player = null
}
}
FragmentRecord.kt
package com.example.thedictaphone.views
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.media.MediaPlayer
import android.media.MediaRecorder
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.PackageManagerCompat.LOG_TAG
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.thedictaphone.R
import com.example.thedictaphone.databinding.FragmentRecordBinding
import com.example.thedictaphone.viewmodels.AudioRecordTest
import com.example.thedictaphone.viewmodels.ListRecordsViewModel
import com.example.thedictaphone.viewmodels.RecordViewModel
import java.io.IOException
class FragmentRecord : Fragment() {
private var _binding: FragmentRecordBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
val viewModel by lazy { ViewModelProvider(this).get(RecordViewModel::class.java)}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentRecordBinding.inflate(inflater, container, false)
binding.viewModel= viewModel
binding.lifecycleOwner=this
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.startRecord.setOnClickListener {
}
binding.pauseRecord.setOnClickListener {
}
binding.stopRecord.setOnClickListener {
}
}
override fun onResume(){
super.onResume()
val mainActivity: MainActivity?=activity as? MainActivity
mainActivity!!.binding.fab.hide()//make binding public
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
片段记录.xml
<?xml version="1.0" encoding="utf-8"?>
<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="viewModel"
type="com.example.thedictaphone.viewmodels.RecordViewModel"/>
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".views.FragmentRecord">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<Button
android:id="@+id/start_record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Record"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.052"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!--android:onClick="@{() -> viewModel.RecordButton}"-->
<Button
android:id="@+id/pause_record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="22dp"
android:layout_marginTop="4dp"
android:text="Pause"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toEndOf="@+id/start_record"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/stop_record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Play"
android:onClick="PlayButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.912"
app:layout_constraintStart_toEndOf="@+id/pause_record"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>
我试着用这个:
android:onClick="@{() -> viewModel.RecordButton}"
将按钮绑定到我的班级,但它说“找不到标识符‘RecordButton’”