Modelo de visualização
reter dados da IU
A atividade/fragmento exibe apenas dados e lida com a interação do usuário.
ViewModel contém dados da IU.
vida útil
A reconstrução da Activity não afetará o ciclo de vida do ViewModel.
Existe apenas uma função de ciclo de vida do ViewModel onCleared()
e essa função será chamada somente quando a página representada pela Activity for destruída.
Contexto de cotação do ViewModel
O ciclo de vida do ViewModel é maior que o do Activity, portanto você não deve manter uma referência ao Activity no ViewModel, caso contrário, causará um vazamento de memória.
O ViewModel não recomenda a introdução de Activity, mas e se o Context for necessário no ViewModel? Um dos seguintes métodos pode ser utilizado: 1.
Use 2. Use AndroidViewModel, que é uma subclasse de ViewModel, que recebe Application como Context internamente;Context.getApplicationContext()
Instanciação do ViewModel
XXXViewModel mXXXViewModel = new ViewModelProvider(this).get(XXXViewModel.class);
A diferença entre ViewModel e onSaveInstanceState()
-
Diferença de dados
onSaveInstanceState() pode salvar apenas uma pequena quantidade de dados da IU que podem ser serializados e não pode salvar grandes dados, como Bitmap.
ViewModel não tem tais restrições. -
A persistência de dados
onSaveInstanceState() pode salvar uma pequena quantidade de dados da UI nas duas situações a seguir:
① O processo do aplicativo é encerrado devido a limitações de memória quando está em segundo plano.
② Mudanças de configuração.
ViewModels só podem persistir dados em caso de destruição em alterações de configuração, e não em processos encerrados.
Dados ao vivo
usar
LiveData pode ser entendido como um contêiner de dados. Ele agrupa os dados para que eles se tornem um observador e, quando os dados mudarem, o observador possa ser notificado.
ViewModel contém dados da UI, e Activity/Fragment é responsável por exibir os dados. Se os dados da UI mudarem, LiveData notifica Activity/Fragment para atualizar os dados. Portanto, LiveData geralmente é usado em ViewModel.
uso básico
LiveData é uma classe abstrata e não pode ser usada diretamente. Normalmente usamos sua subclasse MutableLiveData.
Observe os dados agrupados pelo LiveData por meio do método LiveData.observe(). Por outro lado, quando queremos modificar os dados agrupados pelo LiveData, podemos fazê-lo através do método LiveData.postValue()/LiveData.setValue(). postValue() é chamado em thread não UI e setValue() é chamado em thread UI.
atualização de notificação
LiveData pode perceber o ciclo de vida da página, e somente quando a página estiver no estado ativo (Lifecycle.State.STARTED ou Lifecycle.State.RESUMED) a notificação do LiveData será recebida. Se a página for destruída (Lifecycle.State. DESTROYED), então o LiveData limpará automaticamente as associações com as páginas, evitando assim vazamentos de memória.
Normalmente, o LiveData só envia atualizações quando os dados mudam e apenas para observadores ativos. Uma exceção a esse comportamento é que os observadores também recebem atualizações quando passam de inativos para ativos. Além disso, se o observador mudar de inativo para ativo uma segunda vez, ele só receberá atualizações se o valor tiver mudado desde a última vez que ficou ativo.
LiveData.observeForever() é usado da mesma forma que observe, a diferença é que quando os dados mudam, não importa em que estado a página esteja, ela pode receber notificações. Portanto, você deve chamar removeObserver() após o uso para remover o observador e evitar vazamentos de memória.
ViewModel+LiveData implementa comunicação entre fragmentos
public class OneFragment extends Fragment {
public void onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/*
关键在于ViewModelProvider的构造函数传入的是getActivity()而不是Fragment.this,
这样才能保证每个Fragment得到的是同一个ViewModel,从而共享LiveData
*/
XXXViewModel mXXXViewModel = new ViewModelProvider(getActivity()).get(XXXViewModel.class);
}
}
//TwoFragment与OneFragment类似
resumo
A essência do LiveData é o modo observador + ciclo de vida de percepção.
Ligação de dados
fácil de usar
- Iniciar ligação de dados
android {
……
dataBinding {
enabled = true;
}
}
- Arquivo de layout de tags
Adicione tags fora da raiz do arquivo de layout<layout>
. O objetivo disso é instruir a biblioteca DataBinding para gerar a classe Binding correspondente ao arquivo de layout.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
/*
以下是实际布局
……
*/
</layout>
- Definir variáveis de layout
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name = "变量名"
type = "类全名"/>
/*
或者使用<import>标签引入类
<import type = "类全名"/>
<variable
name = "变量名"
type = "类名称"/>
*/
</data>
/*
以下是实际布局
……
*/
</layout>
- Obtenha a classe Binding
//该方法给Activity设置布局文件的同时,返回Binding类。
XXXBinding mXXXBinding = DataBindingUtil.setContentView(this, R.layout.xxx);
-
Atribuição de variáveis de layout
A vinculação fornece dois métodos de atribuição de valores a variáveis de layout:
①Método geral:XXXBinding.setVariable(BR.变量名, 变量);
②Método de atribuição para variáveis de layout específicas:XXXBinding.set变量名(变量)
-
Expressão de layout
O formato de uma expressão de layout:@{}
.
Por exemplo:@{布局变量.字段}
,@{方法调用的表达式}
<data>
<import type = "xxx.xxx.TestUtil"/>
<variable
name = "book"
type = "xxx.xxx.Book"/>
</data>
<!-- 在布局中引用静态类-->
<TextView
android:text="@{TestUtil.getText()}"/>
<TextView
android:text="@{book.name}"/>
As expressões de layout são muito mais do que esses usos. Para obter detalhes, consulte: Explicação detalhada da vinculação de dados (2) - Expressões de layout e vinculação
- Aparência final da atividade
public class TestActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
XXXBinding mXXXBinding = DataBindingUtil.setContentView(this, R.layout.xxx);
Book book = new Book();
book.name = "Jetpack应用指南";
mXXXBinding.setBook(book);
}
}
vinculação de evento
DataBinding suporta o uso de expressões de layout para lidar com respostas de eventos View.
Método específico: atribua uma expressão de layout ao atributo de evento de View no arquivo de layout.
Isto equivale a usar a expressão de layout para implementar o retorno de chamada do ouvinte correspondente. Essa prática é conhecida como vinculação de eventos .
Correspondência entre atributos de eventos e ouvintes
O nome do atributo do evento depende do nome do método listener. Por exemplo, View.OnClickListener possui onClick()
o método e View.OnLongClickListener possui onLongClick()
o método, portanto, as propriedades do evento são android:onClick
, android:onLongClick
.
Para o evento de clique, a fim de evitar o conflito de múltiplos eventos de clique, o Google também define alguns processamentos de eventos especiais, como:
Aula | Como configurar o ouvinte | propriedades na ligação |
---|---|---|
Visualização de pesquisa | setOnSearchClickListener(View.OnClickListener) | android:onSearchClick |
Controles de Zoom | setOnZoomInClickListener(View.OnClickListener) | android:onZoomIn |
Controles de Zoom | setOnZoomOutClickListener(View.OnClickListener) | android:onZoomOut |
Existem dois tipos de expressões de layout para vinculação de eventos: método de referência e ouvinte de vinculação .
método de referência
Use variáveis de layout para responder a eventos. Este método requer que os parâmetros e o valor de retorno correspondam aos do ouvinte . Se os parâmetros ou o valor de retorno não corresponderem, um erro será relatado em tempo de compilação.
public class EventHandler {
public void onClickHandle(View view) {
System.out.println("按钮被点击了");
}
}
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="eventHandler"
type="xxx.xxx.EventHandler" />
</data>
...
<Button
...
android:onClick="@{eventHandler::onClickHandle}"
... />
...
</layout>
Ao usar o Método de Referência, o ouvinte gerado encapsula a chamada do método para a variável de layout. O objeto ouvinte é criado e atribuído quando a variável de layout é definida. Se a variável de layout for nula, o ouvinte não será criado.
/*
“引用方法”的监听器创建原理如下伪代码所示
伪代码是在运行时运行的。
*/
xxxBinding.setEventHandler(EventHandler eventHandler) {
if(eventHandler != null) {
button.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
eventHandler.onClickHandle(view);
}
});
}
}
vincular ouvinte
Use lambdas em arquivos de layout para responder a eventos. O método requer apenas que o valor de retorno corresponda ao valor de retorno esperado do ouvinte .
public class Tester {
public boolean testLongClick() {
return false;
}
}
<Button
...
android:onClick="@{()->tester.testLongClick()}"
... />
Os ouvintes de ligação permitem parâmetros personalizados.
public class Tester {
public boolean testLongClick(View v, String info) {
Toast.makeText(v.getContext(), info, Toast.LENGTH_LONG).show();
}
}
<Button
...
android:onClick="@{(view)->tester.testLongClick(view, '你好')}"
... />
"Bind Listener" criará automaticamente o ouvinte necessário e registrará eventos para ele durante a compilação (o ouvinte é criado no início e não será julgado se a variável de layout está vazia até ser acionada, e nenhuma execução será executada se está vazio. operar)
/*
“绑定监听器”的监听器创建原理如下伪代码所示
伪代码是在编译时运行的。
*/
button.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if(tester != null) {
tester.testLongClick(view, "你好");
}
}
});
expressão ternária
Se precisar usar uma expressão com um predicado (por exemplo, uma expressão ternária), você poderá usar o tipo de valor de retorno que corresponde ao ouvinte como a expressão, como void para o atributo onCLick e Boolean para o atributo onLongClick.
android:onClick="@{(view)->view.isEnabled()?activity.showSign(view, user):void}"
android:onLongClick="@{(v)->v.isEnabled()?activity.showSign(user):false}"
Encadernação de páginas secundárias
Chamamos a página referenciada diretamente por Atividade/Fragmento de página de primeiro nível, e a página referenciada pelo rótulo na página de primeiro nível é chamada de página de segundo nível.
Como passar variáveis de layout da página de primeiro nível para a página de segundo nível?
Depois que uma variável de layout é definida no layout de primeiro nível , a variável pode não apenas ser recebida e usada no layout de primeiro nível, mas também se tornar um atributo do book
namespace . O objetivo deste atributo é passar variáveis de layout para o layout secundário.xmlns:app
book
//一级页面
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name = "book"
type = "xxx.xxx.Book"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="@layout/layout_content"
app:book="@{book}">
</LinearLayout>
</layout>
//二级页面
/**
在二级页面layout_content中,需要定义一个与一级页面相同的布局变量,
用来接收传递过来的数据。收到book变量后即可使用该变量了。
*/
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name = "book"
type = "xxx.xxx.Book"/>
</data>
<TextView
……
android:text="@{book.name}"/>
</layout>
Adaptador de ligação
Binding Adapter (BindingAdapter) é converter a expressão de atributo no layout na chamada de método correspondente para definir o valor .
O chamado valor de configuração é dividido em dois tipos:
①Definir o valor do atributo, como chamar o método setText()
②Definir o ouvinte de evento, como chamar o método setOnClickListener().
Ele também permite que você personalize o método de chamada para definir o valor e forneça sua própria lógica de ligação.
BindingAdapter da biblioteca DataBinding
Muitas classes XXXBindingAdapter são fornecidas na biblioteca DataBinding, que permite que controles nativos do Android suportem expressões de atributos.
//DataBinding库下ViewBindingAdapter的部分源码
public class ViewBindingAdapter {
@BindingAdapter({
"android:padding"})
public static void setPadding(View view, float paddingFloat) {
final int padding = pixelsToDimensionPixelSize(paddingFloat);
view.setPadding(padding, padding, padding, padding);
}
}