我实现了LiveData和ViewModel来模仿AsyncTaskLoader。
我从DCIM的摄影机目录中加载文件名,然后在删除文件(图片)时将fileObserver附加到Observe,然后回调告诉LiveData在发生delete事件时重新获取文件名>
问题:
以下代码应借助LiveData异步地从DCIM / Pictures中获取文件名,然后将FileObserver附加到目录(DCIM / Pictures),以监视何时删除文件并使用如代码所示,LiveData子类可以重新加载文件。
好的,它是第一次工作,即第一次加载文件,调用setValue()
并传递触发onChange的文件名,以在观察活动/片段中调用。但是,当删除文件时,回调函数将调用loadFiles()函数再次重新加载文件,但是这次调用setValue并传递FileNames不会触发观察活动/片段中的OnChange。
根据official documentation of LiveData
您必须调用setValue(T)方法来更新LiveData对象 从主线程开始。
我很想知道为什么LiveData在第一次调用后没有更新其值。
代码
MyLiveData
class MyLiveData() : MutableLiveData<MutableList<String>>(), PictureDelete {
override fun onPicDelete() {
loadFileNames()
}
val TAG = "MyLiveData"
val fileNamesList: MutableList<String> = ArrayList()
val fileWatcher : MyFileWatcher
init {
loadFileNames()
val pathToWatch = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Camera").getAbsolutePath()
fileWatcher = MyFileWatcher(pathToWatch, this)
fileWatcher.startWatching()
}
private fun loadFileNames() {
val fileDir: File
try {
fileDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Camera")
} catch (e: Exception) {
Log.e(TAG, e.message)
return
}
Log.d(TAG, "Actively Loading Files in Status LiveData")
val arrayOfFiles = fileDir.listFiles()
if (arrayOfFiles == null || arrayOfFiles.size < 1) return
Log.d(TAG, "Actively Loading Files. Size: ${arrayOfFiles.size}")
setValue(fileNamesList)
}
}
MyViewModel
class MyViewModel() : ViewModel() {
val myLiveData: MyLiveData
val TAG = "WhatsAppFragment-VModel"
init {
myLiveData = MyLiveData()
}
}
MyFragment
class MyFragment : Fragment() {
private val TAG = "MyFragment"
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_layout, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
viewModel.myLiveData.observe(this, androidx.lifecycle.Observer { fileNames ->
Log.d(TAG, "New Live Data Dispatch")
for ((index, name) in fileNames.withIndex()) {
Log.d(TAG, "the element at $index is $name")
}
})
}
}
MyFileObserver
class MyFileWatcher(pathToWatch: String, val picDelete: PictureDelete) : FileObserver(pathToWatch, DELETE) {
val TAG = "MyFileWatcher"
init {
Log.d(TAG, "Initialization")
}
override fun onEvent(event: Int, path: String?) {
if (event = FileObserver.DELETE) { // EventCode 512 == Delete
Log.d(TAG, "OnEvent. Event: $event Path: $path")
picDelete.onPicDelete()
}
}
}
PictureDelete界面
interface PictureDelete {
fun onPicDelete()
}
我的实施有什么问题?
答案 0 :(得分:0)
我这里有一个@Micklo_Nerd示例,但是它没有解决删除文件的问题,但是它为您提供了所需的操作思路。 在我的示例中,用户输入名称,然后单击按钮后,列表将更改。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:text="Add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/buttonAdd"
app:layout_constraintStart_toStartOf="@+id/filename"
app:layout_constraintEnd_toEndOf="@+id/filename"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@+id/filename"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:ems="10"
android:id="@+id/filename"
android:layout_marginStart="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/buttonAdd"/>
</android.support.constraint.ConstraintLayout>
MyRepository(在您的示例中是MyLiveData)
在这里,您必须完成获取文件夹中文件名的工作,并将其放入MutableLiveData中。
class MyRepository {
fun loadFileNames(liveData : MutableLiveData<MutableList<String>>, filename: String){
var fileList : MutableList<String>? = liveData.value
if(test == null)
fileList = MutableList(1){ filename }
else
fileList.add(filename)
liveData.value = fileList
}
}
MyViewModel
在这里,我有两种方法:一种是在单击按钮时更新列表,另一种是获取文件名列表。您可能只需要获取列表的那个
class MyViewModel : ViewModel() {
val repo: MyRepository
var mutableLiveData : MutableLiveData<MutableList<String>>
init {
repo = MyRepository()
mutableLiveData = MutableLiveData()
}
fun changeList(filename: String){
repo.loadFileNames(mutableLiveData, filename)
}
fun getFileList() : MutableLiveData<MutableList<String>>{
return mutableLiveData
}
}
MainActivity
在这里,您看到我正在观察返回文件名列表的方法,这是您需要做的,因为这将要更改。
class MainActivity : AppCompatActivity(), View.OnClickListener {
private val TAG = "MyFragment"
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
viewModel.getFileList().observe(this, Observer<MutableList<String>> { fileNames ->
Log.d(TAG, "New Live Data Dispatch")
textView.text = ""
for ((index, name) in fileNames!!.withIndex()) {
textView.append("the element at $index is $name\n")
}
})
buttonAdd.setOnClickListener(this)
}
override fun onClick(v: View?) {
when(v!!.id){
R.id.buttonAdd -> {
viewModel.changeList(filename.text.toString())
filename.text.clear()
}
}
}
}
希望这会有所帮助。