Panoramica di ViewModel (visualizzazioni)

Concetti e implementazione di Jetpack Compose

La classe ViewModel è un contenitore di stato a livello di logica di business o schermata. Espone lo stato all'interfaccia utente e incapsula la logica di business correlata. Il suo vantaggio principale è che memorizza nella cache lo stato e lo mantiene durante le modifiche alla configurazione. Ciò significa che la tua UI non deve recuperare nuovamente i dati quando passi da un'attività all'altra o quando segui le modifiche alla configurazione, ad esempio quando ruoti lo schermo.

Vantaggi di ViewModel

L'alternativa a un ViewModel è una semplice classe che contiene i dati visualizzati nella UI. Questo può diventare un problema quando si passa da un'attività all'altra o alle destinazioni di navigazione. In questo modo i dati vengono eliminati se non li memorizzi utilizzando il meccanismo di stato dell'istanza salvato. ViewModel fornisce un'API comoda per la persistenza dei dati che risolve questo problema.

I vantaggi principali della classe ViewModel sono essenzialmente due:

  • Consente di rendere persistente lo stato dell'interfaccia utente.
  • Fornisce l'accesso alla logica di business.

Ambito

Quando crei un'istanza di un ViewModel, gli passi un oggetto che implementa l'interfaccia ViewModelStoreOwner. Può trattarsi di una destinazione di navigazione, di un grafico di navigazione, di un'attività, di un frammento o di qualsiasi altro tipo che implementi l'interfaccia. La ViewModel viene quindi limitata all'Lifecycle di ViewModelStoreOwner. Rimane in memoria finché il suo ViewModelStoreOwner non scompare definitivamente.

Una serie di classi sono sottoclassi dirette o indirette dell'interfaccia ViewModelStoreOwner. Le sottoclassi dirette sono ComponentActivity, Fragment e NavBackStackEntry. Per un elenco completo delle sottoclassi indirette, consulta il riferimento ViewModelStoreOwner.

Implementare un ViewModel

Di seguito è riportato un esempio di implementazione di un ViewModel per una schermata che consente all'utente di tirare i dadi.

Kotlin

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    // Expose screen UI state
    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // Handle business logic
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
                firstDieValue = Random.nextInt(from = 1, until = 7),
                secondDieValue = Random.nextInt(from = 1, until = 7),
                numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

Java

public class DiceUiState {
    private final Integer firstDieValue;
    private final Integer secondDieValue;
    private final int numberOfRolls;

    // ...
}

public class DiceRollViewModel extends ViewModel {

    private final MutableLiveData<DiceUiState> uiState =
        new MutableLiveData(new DiceUiState(null, null, 0));
    public LiveData<DiceUiState> getUiState() {
        return uiState;
    }

    public void rollDice() {
        Random random = new Random();
        uiState.setValue(
            new DiceUiState(
                random.nextInt(7) + 1,
                random.nextInt(7) + 1,
                uiState.getValue().getNumberOfRolls() + 1
            )
        );
    }
}

Puoi quindi accedere al ViewModel da un'attività nel seguente modo:

Kotlin

import androidx.activity.viewModels

class DiceRollActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same DiceRollViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val viewModel: DiceRollViewModel by viewModels()
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

Java

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
        model.getUiState().observe(this, uiState -> {
            // update UI
        });
    }
}