一、ViewModel
1、基本介紹
- ViewModel 屬于 Android Jetpack 架構組件的一部分,ViewModel 被設計用來存儲和管理與 UI 相關的數據,這些數據在配置更改(例如,屏幕旋轉)時能夠幸存下來,ViewModel 的生命周期與 Activity 或 Fragment 的生命周期緊密相關,但比它們更長,這意味著即使 Activity 或 Fragment 被重新創建,ViewModel 中的數據也會保留下來,它有如下特點
-
配置更改時數據保留:當屏幕旋轉或發生其他配置更改時,ViewModel 中的數據不會丟失
-
生命周期管理:ViewModel 在關聯的 Activity 或 Fragment 被銷毀時也會被清理,但它在配置更改時會保留下來
-
數據共享:可以在多個 Fragment 或 Activity 之間共享同一個 ViewModel 實例,以共享數據
-
與 LiveData 配合使用:ViewModel 通常與 LiveData 一起使用,以便在數據發生變化時通知 UI 更新
2、不使用 ViewModel
(1)Activity Layout
- activity_view_model_no_use.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=".ViewModelNoUseActivity"><Buttonandroid:id="@+id/btn_add"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="add"android:text="add"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_num"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="0"android:textSize="20sp"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.35000002" /></androidx.constraintlayout.widget.ConstraintLayout>
(2)Activity Code
- ViewModelNoUseActivity.java
package com.my.viewmodel;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.view.View;
import android.widget.TextView;public class ViewModelNoUseActivity extends AppCompatActivity {private TextView tvNum;private int num;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_view_model_no_use);tvNum = findViewById(R.id.tv_num);}public void add(View view) {tvNum.setText(String.valueOf(++num));}
}
3、使用 ViewModel
(1)ViewModel
- MyViewModel.java
package com.my.jetpackdemo.viewmodel;import androidx.lifecycle.ViewModel;public class MyViewModel extends ViewModel {public int num;
}
(2)Activity Layout
- activity_view_model_no_use.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=".ViewModelNoUseActivity"><Buttonandroid:id="@+id/btn_add"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="add"android:text="add"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_num"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="0"android:textSize="20sp"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.35000002" /></androidx.constraintlayout.widget.ConstraintLayout>
(3)Activity Code
- ViewModelNoUseActivity.java
package com.my.viewmodel;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;import android.os.Bundle;
import android.view.View;
import android.widget.TextView;import com.my.viewmodel.viewmodel.MyViewModel;public class ViewModelActivity extends AppCompatActivity {private TextView tvNum;private MyViewModel myViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_view_model);tvNum = findViewById(R.id.tv_num);myViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);tvNum.setText(String.valueOf(myViewModel.num));}public void add(View view) {tvNum.setText(String.valueOf(++myViewModel.num));}
}
二、LiveData
1、基本介紹
- LiveData 是 Android Jetpack 架構組件中的一個類,它用于在數據發生變化時通知觀察者(通常是 UI 組件),LiveData 是一個可觀察的數據持有者,它與生命周期感知組件(例如,Activity、Fragment)緊密集成,這意味著它只會在這些組件處于活躍狀態時更新觀察者,它有如下特點
-
生命周期感知:LiveData 只在有活躍的觀察者時才會分發更新,這有助于避免在組件(例如,Activity、Fragment)不再可見時發生不必要的更新
-
粘性事件:LiveData 可以選擇性地保留最后一個值,并在新的觀察者開始觀察時立即發送該值(粘性事件的行為),這對于如登錄狀態、配置變化等需要即時知道當前狀態的情況很有用
-
線程安全:可以在任何線程上修改 LiveData 的值,而觀察者會在主線程上接收到這些更新,從而避免了直接操作 UI 組件的線程安全問題
-
數據一致性:LiveData 保證觀察者會收到最新的數據,即使它們是在數據變化之后開始觀察的
2、LiveData 方法
方法 | 說明 |
---|---|
setValue | 用于設置 LiveData 對象的新值,當調用此方法時,所有活動的觀察者都會收到這個新值 如果 LiveData 對象當前沒有活動的觀察者,setValue 方法調用不會做任何事情 但是,一旦有觀察者,它將立即收到最近設置的值 注:setValue 方法應該在主線程上調用,因為觀察者會在主線程上接收更新 |
getValue | 用于獲取 LiveData 對象當前持有的值,如果沒有設置值,則返回 null |
postValue | 另一個用于設置 LiveData 值的方法,但它可以在任何線程上調用 從非主線程更新 LiveData 時,應該使用 postValue 方法而不是 setValue 方法 postValue 方法會將更新操作安排到主線程上執行,從而確保 UI 的更新是在主線程上進行的 |
3、演示
(1)ViewModel
- MyLiveData.java
package com.my.livedata.viewmodel;import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;public class MyLiveData extends ViewModel {private MutableLiveData<Integer> currentSecond;public MutableLiveData<Integer> getCurrentSecond() {if (currentSecond == null) {currentSecond = new MutableLiveData<>();currentSecond.setValue(0);}return currentSecond;}
}
- 上述代碼使用了 MutableLiveData 來存儲和分發一個整數值,它允許修改存儲的值并通知觀察者
(2)Activity Layout
- activity_live_data.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=".LiveDataActivity"><TextViewandroid:id="@+id/tv_current_second"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="0"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
(3)Activity Code
- LiveDataActivity.java
package com.my.livedata;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;import android.os.Bundle;
import android.widget.TextView;import com.my.livedata.viewmodel.MyLiveData;import java.util.Timer;
import java.util.TimerTask;public class LiveDataActivity extends AppCompatActivity {private TextView tvCurrentSecond;private MyLiveData myLiveData;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_live_data);tvCurrentSecond = findViewById(R.id.tv_current_second);myLiveData = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyLiveData.class);myLiveData.getCurrentSecond().observe(this, new Observer<Integer>() {@Overridepublic void onChanged(Integer integer) {tvCurrentSecond.setText(String.valueOf(integer));}});startTimer();}private void startTimer() {new Timer().schedule(new TimerTask() {@Overridepublic void run() {myLiveData.getCurrentSecond().postValue(myLiveData.getCurrentSecond().getValue() + 1);}}, 1000, 1000);}
}
-
observe 方法用來觀察 LiveDataViewModel 中的 currentSecond,當 currentSecond 的值改變時,onChanged 方法會被調用
-
postValue 方法用來更新 LiveDataViewModel 中的 currentSecond 的值
4、LiveData 優化
(1)ViewModel
- MyAnotherLiveData.java
package com.my.livedata.viewmodel;import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;public class MyAnotherLiveData extends ViewModel {private MutableLiveData<Integer> currentSecond = new MutableLiveData<>(0);public LiveData<Integer> getCurrentSecond() {return currentSecond;}public Integer getValue() {return currentSecond.getValue();}public void setValue(Integer integer) {currentSecond.setValue(integer);}public void postValue(Integer integer) {currentSecond.postValue(integer);}
}
(2)Activity Layout
- activity_another_live_data.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=".AnotherLiveDataActivity"><TextViewandroid:id="@+id/tv_current_second"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="0"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
(3)Activity Code
- AnotherLiveDataActivity.java
package com.my.livedata;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;import android.os.Bundle;
import android.widget.TextView;import com.my.livedata.viewmodel.MyAnotherLiveData;import java.util.Timer;
import java.util.TimerTask;public class AnotherLiveDataActivity extends AppCompatActivity {private TextView tvCurrentSecond;private MyAnotherLiveData myAnotherLiveData;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_another_live_data);tvCurrentSecond = findViewById(R.id.tv_current_second);myAnotherLiveData = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyAnotherLiveData.class);myAnotherLiveData.getCurrentSecond().observe(this, (integer) -> {tvCurrentSecond.setText(String.valueOf(integer));});startTimer();}private void startTimer() {new Timer().schedule(new TimerTask() {@Overridepublic void run() {myAnotherLiveData.postValue(myAnotherLiveData.getValue() + 1);}}, 1000, 1000);}
}
5、Fragment 通信
(1)ViewModel
- SeekBarViewModel.java
package com.my.jetpackdemo.viewmodel;import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;public class SeekBarViewModel extends ViewModel {private MutableLiveData<Integer> process;public MutableLiveData<Integer> getProcess() {if (process == null) {process = new MutableLiveData<>();process.setValue(0);}return process;}
}
(2)Fragment
- fragment_first.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=".fragment.FirstFragment"><SeekBarandroid:id="@+id/seek_bar_first"android:layout_width="0dp"android:layout_height="wrap_content"android:max="100"android:min="0"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- FirstFragment.java
package com.my.livedata.fragment;import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;import com.my.livedata.R;
import com.my.livedata.viewmodel.SeekBarViewModel;public class FirstFragment extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View root = inflater.inflate(R.layout.fragment_first, null, false);SeekBar seekBarFirst = root.findViewById(R.id.seek_bar_first);SeekBarViewModel seekBarViewModel = new ViewModelProvider(getActivity(),new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(SeekBarViewModel.class);seekBarViewModel.getProcess().observe(getActivity(), new Observer<Integer>() {@Overridepublic void onChanged(Integer integer) {seekBarFirst.setProgress(integer);}});seekBarFirst.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {seekBarViewModel.setValue(progress);}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});return root;}
}
- fragment_second.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=".fragment.SecondFragment"><SeekBarandroid:id="@+id/seek_bar_second"android:layout_width="0dp"android:layout_height="wrap_content"android:max="100"android:min="0"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- SecondFragment.java
package com.my.livedata.fragment;import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;import com.my.livedata.R;
import com.my.livedata.viewmodel.SeekBarViewModel;public class SecondFragment extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View root = inflater.inflate(R.layout.fragment_second, null, false);SeekBar seekBarSecond = root.findViewById(R.id.seek_bar_second);SeekBarViewModel seekBarViewModel = new ViewModelProvider(getActivity(),new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(SeekBarViewModel.class);seekBarViewModel.getProcess().observe(getActivity(), new Observer<Integer>() {@Overridepublic void onChanged(Integer integer) {seekBarSecond.setProgress(integer);}});seekBarSecond.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {seekBarViewModel.setValue(progress);}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});return root;}
}
(3)Activity Layout
- activity_live_data_fragment.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=".LiveDataFragmentActivity"><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/gl"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"app:layout_constraintGuide_end="365dp" /><androidx.fragment.app.FragmentContainerViewandroid:id="@+id/fcv1"android:name="com.my.livedata.fragment.FirstFragment"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toTopOf="@+id/gl"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><androidx.fragment.app.FragmentContainerViewandroid:id="@+id/fcv2"android:name="com.my.livedata.fragment.SecondFragment"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="@+id/gl" />
</androidx.constraintlayout.widget.ConstraintLayout>
(4)Activity Code
- LiveDataFragmentActivity.java
package com.my.livedata;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;public class LiveDataFragmentActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_live_data_fragment);}
}
三、數據綁定
1、基本介紹
- Android DataBinding 是一種布局書寫方式,它允許將數據直接綁定到布局的 XML 中,使得數據的變化能夠直接反映到 View 上,這是 Android團隊實現 MVVM 架構的一種方法,旨在減少 Android 開發中的大量模板代碼(例如,findViewById()),增加代碼及邏輯清晰度,提高開發效率和維護效率
2、演示
(1)Setting
- 模塊級 build.gradle
android {...defaultConfig {...dataBinding {enabled = true}}
}
(2)Entity
- Idol.java
package com.my.jetpackdemo.entity;public class Idol {public String name;public String star;public Idol(String name, String star) {this.name = name;this.star = star;}
}
(3)Activity Layout
- activity_data_binding.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><variablename="idol"type="com.my.databinding.entity.Idol" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".DataBindingActivity"><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/gl"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"app:layout_constraintGuide_percent="0.5" /><TextViewandroid:id="@+id/tv_idol_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{ idol.name }"android:textSize="20sp"app:layout_constraintBottom_toTopOf="@+id/gl"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_idol_star"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{ idol.star }"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="@+id/gl" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- DataBindingActivity.java
package com.my.databinding;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;import com.my.databinding.databinding.ActivityDataBindingBinding;
import com.my.databinding.entity.Idol;public class DataBindingActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityDataBindingBinding activityDataBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding);Idol idol = new Idol("張學友", "五星");activityDataBindingBinding.setIdol(idol);}
}
3、更多用法
(1)Entity
- User.java
package com.my.databinding.entity;public class User {public String name;public int star;public User(String name, int star) {this.name = name;this.star = star;}
}
(2)Listener
- EventHandleListener.java
package com.my.databinding.listener;import android.content.Context;
import android.view.View;
import android.widget.Toast;public class EventHandleListener {private Context context;public EventHandleListener(Context context) {this.context = context;}public void btnOnClick(View view) {Toast.makeText(context, "點贊", Toast.LENGTH_SHORT).show();}
}
(3)Util
- UserUtil.java
package com.my.jetpackdemo.util;public class UserUtil {public static String getStar(int star) {switch (star) {case 1:return "一星";case 2:return "一星";case 3:return "三星";case 4:return "四星";case 5:return "五星";}return "零星";}
}
(4)Activity Layout
- activity_user.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><variablename="user"type="com.my.databinding.entity.User" /><variablename="eventHandleListener"type="com.my.databinding.listener.EventHandleListener" /><import type="com.my.databinding.util.UserUtil" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".UserActivity"><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/guideline2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"app:layout_constraintGuide_end="365dp" /><TextViewandroid:id="@+id/tv_user_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{ user.name }"android:textSize="20sp"app:layout_constraintBottom_toTopOf="@+id/guideline2"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_user_star"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{ UserUtil.getStar(user.star) }"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="@+id/guideline2" /><Buttonandroid:id="@+id/btn_like"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="點贊"android:textSize="20sp"android:onClick="@{ eventHandleListener.btnOnClick }"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintVertical_bias="0.508" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(5)Activity Code
- UserActivity.java
package com.my.databinding;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;import com.my.databinding.databinding.ActivityUserBinding;
import com.my.databinding.listener.EventHandleListener;
import com.my.databinding.entity.User;public class UserActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_user);ActivityUserBinding activityUserBinding = DataBindingUtil.setContentView(this, R.layout.activity_user);User user = new User("張三", 3);activityUserBinding.setUser(user);EventHandleListener eventHandleListener = new EventHandleListener(this);activityUserBinding.setEventHandleListener(eventHandleListener);}
}
4、二級頁面數據綁定
(1)Entity
package com.my.databinding.entity;public class User {public String name;public int star;public User(String name, int star) {this.name = name;this.star = star;}
}
(2)Util
- UserUtil.java
package com.my.jetpackdemo.util;public class UserUtil {public static String getStar(int star) {switch (star) {case 1:return "一星";case 2:return "一星";case 3:return "三星";case 4:return "四星";case 5:return "五星";}return "零星";}
}
(3)Activity Layout
- user_son1.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><variablename="user"type="com.my.databinding.entity.User" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/son_tv_user_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{ user.name }"android:textColor="#E91E63"android:textSize="24sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- user_son2.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><variablename="user"type="com.my.databinding.entity.User" /><import type="com.my.databinding.util.UserUtil" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/son_tv_user_star"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{ UserUtil.getStar(user.star) }"android:textColor="#E91E63"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- activity_user_father.java
<?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><variablename="user1"type="com.my.databinding.entity.User" /><variablename="user2"type="com.my.databinding.entity.User" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".UserFatherActivity"><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/gl"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"app:layout_constraintGuide_percent="0.5" /><includelayout="@layout/user_son1"app:user="@{ user1 }"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toTopOf="@+id/gl"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><includelayout="@layout/user_son2"app:user="@{ user2 }"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="@+id/gl" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- UserFatherActivity.java
package com.my.databinding;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;import android.os.Bundle;import com.my.databinding.databinding.ActivityUserFatherBinding;
import com.my.databinding.entity.User;public class UserFatherActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityUserFatherBinding activityUserFatherBinding = DataBindingUtil.setContentView(this, R.layout.activity_user_father);User user1 = new User("張三", 3);User user2 = new User("李四", 4);activityUserFatherBinding.setUser1(user1);activityUserFatherBinding.setUser2(user2);}
}
5、RecyclerView 數據綁定
(1)Entity
- Student.java
package com.my.databinding.entity;public class Student {public String name;public int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getAgeStr() {return age + "";}
}
(2)Activity Layout
- recycler_view_binding_item.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><variablename="student"type="com.my.databinding.entity.Student" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="#03A9F4"android:paddingTop="25dp"android:paddingBottom="25dp"><TextViewandroid:id="@+id/tv_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{ student.name }"android:textSize="24sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_age"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="25dp"android:text="@{ student.ageStr }"android:textSize="20sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/tv_name" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- activity_recycler_view_binding.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"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.my.databinding.RecyclerViewBindingActivity"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv"android:layout_width="match_parent"android:layout_height="match_parent"tools:ignore="MissingConstraints"tools:layout_editor_absoluteX="1dp"tools:layout_editor_absoluteY="1dp" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(3)Adapter
- RecyclerViewBindingAdapter.java
package com.my.databinding.adapter;import android.view.LayoutInflater;
import android.view.ViewGroup;import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;import com.my.databinding.R;
import com.my.databinding.databinding.RecyclerViewBindingItemBinding;
import com.my.databinding.entity.Student;import java.util.List;public class RecyclerViewBindingAdapter extends RecyclerView.Adapter<RecyclerViewBindingAdapter.MyViewHolder> {List<Student> studentList;public RecyclerViewBindingAdapter(List<Student> studentList) {this.studentList = studentList;}@NonNull@Overridepublic MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {RecyclerViewBindingItemBinding recyclerViewBindingItemBinding =DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.recycler_view_binding_item, parent, false);return new MyViewHolder(recyclerViewBindingItemBinding);}@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {Student student = studentList.get(position);holder.recyclerViewBindingItemBinding.setStudent(student);}@Overridepublic int getItemCount() {return studentList.size();}static class MyViewHolder extends RecyclerView.ViewHolder {private RecyclerViewBindingItemBinding recyclerViewBindingItemBinding;public MyViewHolder(RecyclerViewBindingItemBinding recyclerViewBindingItemBinding) {super(recyclerViewBindingItemBinding.getRoot());this.recyclerViewBindingItemBinding = recyclerViewBindingItemBinding;}}
}
(4)Activity Code
- RecyclerViewBindingActivity.java
package com.my.databinding;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import android.os.Bundle;import com.my.databinding.adapter.RecyclerViewBindingAdapter;
import com.my.databinding.databinding.ActivityRecyclerViewBindingBinding;
import com.my.databinding.entity.Student;import java.util.ArrayList;
import java.util.List;public class RecyclerViewBindingActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityRecyclerViewBindingBinding activityRecyclerViewBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view_binding);LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);activityRecyclerViewBindingBinding.rv.setLayoutManager(linearLayoutManager);List<Student> studentList = new ArrayList<>();studentList.add(new Student("jack", 18));studentList.add(new Student("smith", 19));studentList.add(new Student("tom", 20));RecyclerViewBindingAdapter recyclerViewBindingAdapter = new RecyclerViewBindingAdapter(studentList);activityRecyclerViewBindingBinding.rv.setAdapter(recyclerViewBindingAdapter);}
}
四、雙向數據綁定
2、演示
(1)Entity
- Info.java
package com.my.databinding.entity;public class Info {public String username;public String password;public Info(String username, String password) {this.username = username;this.password = password;}
}
(2)viewModel
- InfoViewModel.java
package com.my.databinding.viewmodel;import android.util.Log;import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;import com.my.databinding.BR;
import com.my.databinding.entity.Info;public class InfoViewModel extends BaseObservable {private Info info;public InfoViewModel(Info info) {this.info = info;}@Bindablepublic String getUsername() {return info.username;}public void setUsername(String username) {if (username != null && !username.equals(info.username)) {info.username = username;Log.d("my ==========", "setUsername: " + info.username);notifyPropertyChanged(BR.username);}}@Bindablepublic String getPassword() {return info.password;}public void setPassword(String password) {if (password != null && !password.equals(info.password)) {info.password = password;Log.d("my ==========", "setPassword: " + info.password);notifyPropertyChanged(BR.password);}}
}
(3)Activity Layout
- activity_data_binding_two.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><variablename="infoViewModel"type="com.my.databinding.viewmodel.InfoViewModel" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".DataBindingTwoActivity"><EditTextandroid:id="@+id/et_username"android:layout_width="wrap_content"android:layout_height="wrap_content"android:ems="10"android:inputType="textPersonName"android:text="@={ infoViewModel.username }"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.1" /><EditTextandroid:id="@+id/et_password"android:layout_width="wrap_content"android:layout_height="wrap_content"android:ems="10"android:inputType="textPersonName"android:text="@={ infoViewModel.password }"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/et_username"app:layout_constraintVertical_bias="0.2" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- DataBindingTwoActivity.java
package com.my.databinding;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;import com.my.databinding.databinding.ActivityDataBindingTwoBinding;
import com.my.databinding.viewmodel.InfoViewModel;
import com.my.databinding.entity.Info;public class DataBindingTwoActivity extends AppCompatActivity {private InfoViewModel infoViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityDataBindingTwoBinding activityDataBindingTwoBinding =DataBindingUtil.setContentView(this, R.layout.activity_data_binding_two);Info info = new Info("123", "456");infoViewModel = new InfoViewModel(info);activityDataBindingTwoBinding.setInfoViewModel(infoViewModel);test();}public void test() {new Thread(() -> {try {Thread.sleep(5000);infoViewModel.setUsername("112233");infoViewModel.setPassword("445566");} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}
3、另一種實現演示
(1)Entity
- Info.java
package com.my.databinding.entity;public class Info {public String username;public String password;public Info(String username, String password) {this.username = username;this.password = password;}
}
(2)viewModel
package com.my.databinding.viewmodel;import android.util.Log;import androidx.databinding.ObservableField;import com.my.databinding.entity.Info;public class AnotherInfoViewModel {public ObservableField<String> username;public ObservableField<String> password;public AnotherInfoViewModel(Info info) {username = new ObservableField<>(info.username);password = new ObservableField<>(info.password);}public String getUsername_() {return username.get();}public void setUsername_(String username) {Log.d("my ==========", "setUsername: " + username);this.username.set(username);}public String getPassword_() {return password.get();}public void setPassword_(String password) {Log.d("my ==========", "setPassword: " + password);this.password.set(password);}
}
(3)Activity Layout
- activity_data_binding_two_another.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><variablename="anotherInfoViewModel"type="com.my.databinding.viewmodel.AnotherInfoViewModel" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.my.databinding.DataBindingTwoAnotherActivity"><EditTextandroid:id="@+id/et_username"android:layout_width="wrap_content"android:layout_height="wrap_content"android:ems="10"android:inputType="textPersonName"android:text="@={ anotherInfoViewModel.username }"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.1" /><EditTextandroid:id="@+id/et_password"android:layout_width="wrap_content"android:layout_height="wrap_content"android:ems="10"android:inputType="textPersonName"android:text="@={ anotherInfoViewModel.password }"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/et_username"app:layout_constraintVertical_bias="0.2" /><Buttonandroid:id="@+id/btn_change"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Change"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- DataBindingTwoAnotherActivity.java
package com.my.databinding;import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;import android.os.Bundle;import com.my.databinding.databinding.ActivityDataBindingTwoAnotherBinding;
import com.my.databinding.entity.Info;
import com.my.databinding.viewmodel.AnotherInfoViewModel;public class DataBindingTwoAnotherActivity extends AppCompatActivity {private AnotherInfoViewModel anotherInfoViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityDataBindingTwoAnotherBinding activityDataBindingTwoAnotherBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding_two_another);Info info = new Info("123", "456");anotherInfoViewModel = new AnotherInfoViewModel(info);activityDataBindingTwoAnotherBinding.setAnotherInfoViewModel(anotherInfoViewModel);findViewById(R.id.btn_change).setOnClickListener((v) -> {anotherInfoViewModel.setUsername_("112233");anotherInfoViewModel.setPassword_("445566");});test();}public void test() {new Thread(() -> {try {Thread.sleep(5000);anotherInfoViewModel.setUsername_("112233");anotherInfoViewModel.setPassword_("445566");} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}