FragmentViewModel создается в MainActivity, а не в самом Fragment - PullRequest
0 голосов
/ 23 апреля 2020

Это мой фрагмент_дома. xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.home.HomeFragment" >

    <TextView
        android:id="@+id/bluetooth_connection_status_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/title_bluetooth_connection_status"
        android:layout_marginStart="10dp"
        android:layout_marginTop="10dp"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/bluetooth_connection_current_status"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.1"
        app:layout_constraintStart_toEndOf="@+id/bluetooth_connection_status_label"
        app:layout_constraintTop_toTopOf="@+id/bluetooth_connection_status_label" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/toggle_advertising_floating_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:contentDescription="@string/button"
        android:src="@drawable/ic_bluetooth_searching_white_24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.9" />

</androidx.constraintlayout.widget.ConstraintLayout>

Файл HomeViewModel.kt:

class HomeViewModel : ViewModel() {

    private val connectionStatus: MutableLiveData<BleConnectionStatus> by lazy { MutableLiveData<BleConnectionStatus>()}
    fun setConnectionStatus(value: BleConnectionStatus){
        connectionStatus.value = value
        Log.d(HomeViewModel::class.java.name, "setConnectionStatus(${connectionStatus.value})")
    }

    fun getConnectionStatusValue(): MutableLiveData<BleConnectionStatus>{
        Log.d(HomeViewModel::class.java.name, "getConnectionStatusValue() :${connectionStatus.value}")
        return connectionStatus
    }
}

Мой файл HomeViewFragment.kt

class HomeFragment : Fragment() {

    private lateinit var homeViewModel: HomeViewModel

    private lateinit var connectionStatusTextView: TextView
    private lateinit var advertisingFloatingActionButton: FloatingActionButton

    private var activityCallback: IToggleAdvertising? = null

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)

        connectionStatusTextView = root.findViewById(R.id.bluetooth_connection_current_status)
        advertisingFloatingActionButton = root.findViewById(R.id.toggle_advertising_floating_button)
        return root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        createObserver()
        initializeActivityCallback()
        advertisingFloatingActionButton.setOnClickListener { toggleAdvertising() }
    }

    private fun createObserver() {
        val connectionStatusObserver = Observer<BleConnectionStatus> {
            Log.d(HomeFragment::class.java.name, "Observer updated with $it")
            connectionStatusTextView.text = it.toString()
            if (it == BleConnectionStatus.SLEEPING) {
                advertisingFloatingActionButton.isEnabled = true
                advertisingFloatingActionButton.visibility = View.VISIBLE
            } else if (it == BleConnectionStatus.ADVERTISING) {
                advertisingFloatingActionButton.isEnabled = false
                advertisingFloatingActionButton.visibility = View.VISIBLE
            } else
                advertisingFloatingActionButton.visibility = View.GONE

        }
//                homeViewModel.getConnectionStatusValue().observe(this, connectionStatusObserver)
        homeViewModel.getConnectionStatusValue().observe(viewLifecycleOwner, connectionStatusObserver)
    }

    private fun initializeActivityCallback() {
        try {
            activityCallback = context as IToggleAdvertising
        } catch (e: ClassCastException) {
            e.toString()
        }
    }

    private fun toggleAdvertising(){
        Log.d(HomeFragment::class.java.name, "toggleAdvertising() && ${homeViewModel.getConnectionStatusValue().value}")
        if(homeViewModel.getConnectionStatusValue().value == BleConnectionStatus.SLEEPING)
            activityCallback?.onStartAdvertising()
        else if(homeViewModel.getConnectionStatusValue().value == BleConnectionStatus.ADVERTISING)
            activityCallback?.onStopAdvertising()
    }

    override fun onResume() {
        super.onResume()
        if(homeViewModel.getConnectionStatusValue().value == null)
            homeViewModel.setConnectionStatus(BleConnectionStatus.SLEEPING)
    }
}

И это мой вывод logcat

2020-04-23 16:09:27.401 17290-17290/? D/MainActivity: checkPermissions()
2020-04-23 16:09:27.446 17290-17290/? D/HomeViewModel: getConnectionStatusValue() :null
2020-04-23 16:09:27.457 17290-17290/? D/MainActivity: initializeBluetooth()
2020-04-23 16:09:27.500 17290-17290/? D/HomeViewModel: getConnectionStatusValue() :null 

2020-04-23 16:09:27.500 17290-17290/? D/HomeFragment: Observer updated with Sleeping

2020-04-23 16:09:27.501 17290-17290/? D/HomeViewModel: setConnectionStatus(Sleeping)
2020-04-23 16:09:32.379 17290-17290/  D/HomeViewModel: getConnectionStatusValue() :Sleeping
2020-04-23 16:09:32.379 17290-17290/  D/HomeFragment: toggleAdvertising() && Sleeping
2020-04-23 16:09:32.379 17290-17290/  D/HomeViewModel: getConnectionStatusValue() :Sleeping
2020-04-23 16:09:32.379 17290-17290/  D/MainActivity: onStartAdvertising()
2020-04-23 16:09:32.421 17290-17290/  D/MainActivity: updateBluetoothStatusString(Advertising)
2020-04-23 16:09:32.422 17290-17290/  D/HomeViewModel: setConnectionStatus(Advertising)
2020-04-23 16:12:01.310 17290-17290/  D/HomeViewModel: getConnectionStatusValue() :Sleeping
2020-04-23 16:12:01.310 17290-17290/  D/HomeFragment: toggleAdvertising() && Sleeping
2020-04-23 16:12:01.310 17290-17290/  D/HomeViewModel: getConnectionStatusValue() :Sleeping
2020-04-23 16:12:01.310 17290-17290/  D/MainActivity: onStartAdvertising()
2020-04-23 16:12:01.324 17290-17290/  D/MainActivity: updateBluetoothStatusString(Error)
2020-04-23 16:12:01.324 17290-17290/  D/HomeViewModel: setConnectionStatus(Error)

Я следую инструкциям официального Android сайта и книги "Android Studio 3.2 Основы разработки - Kotlin Edition", но я не обновляю TextView.

Кто-нибудь знает, что я делаю не так? Спасибо заранее.

РЕДАКТИРОВАТЬ: Я обнаружил проблему, Я дважды создал экземпляр класса HomeViewModel : один раз в HomeFragment и один в HomeActivity:

class MainActivity : AppCompatActivity(), IBluetoothStatusUpdater, IToggleAdvertising {

    private val TAG = MainActivity::class.java.name
    private val REQUEST_ENABLE_BT = 1
    private val PERMISSION_REQUEST_FINE_LOCATION = 1
    private val PERMISSION_REQUEST_COARSE_LOCATION = 2

    private lateinit var mBlePeripheral: BlePeripheral
    private lateinit var homeViewModel: HomeViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initializeNavigationComponents()

        homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)

        checkPermissions()
    }
...
}

Итак, я хочу обновить GUI HomeFragment и сохранить его статус в HomeViewModel. Обновления MutableLiveData в HomeViewModel поступают из службы Bluetooth, которая должна быть инициализирована в MainActivity.

Итак, мой новый вопрос: должен ли экземпляр HomeViewModel из MainActivity и удалить экземпляр в HomeFragment?

Если нет, какие у меня есть альтернативы? Я рассматриваю репозиторий, но Bluetooth должен быть инициализирован в MainActivity, поэтому я не знаю, как сейчас приступить к дизайну.

...