Jetpack - ViewModel、LiveData、DataBinding(數據綁定、雙向數據綁定)

一、ViewModel

1、基本介紹
  • ViewModel 屬于 Android Jetpack 架構組件的一部分,ViewModel 被設計用來存儲和管理與 UI 相關的數據,這些數據在配置更改(例如,屏幕旋轉)時能夠幸存下來,ViewModel 的生命周期與 Activity 或 Fragment 的生命周期緊密相關,但比它們更長,這意味著即使 Activity 或 Fragment 被重新創建,ViewModel 中的數據也會保留下來,它有如下特點
  1. 配置更改時數據保留:當屏幕旋轉或發生其他配置更改時,ViewModel 中的數據不會丟失

  2. 生命周期管理:ViewModel 在關聯的 Activity 或 Fragment 被銷毀時也會被清理,但它在配置更改時會保留下來

  3. 數據共享:可以在多個 Fragment 或 Activity 之間共享同一個 ViewModel 實例,以共享數據

  4. 與 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)緊密集成,這意味著它只會在這些組件處于活躍狀態時更新觀察者,它有如下特點
  1. 生命周期感知:LiveData 只在有活躍的觀察者時才會分發更新,這有助于避免在組件(例如,Activity、Fragment)不再可見時發生不必要的更新

  2. 粘性事件:LiveData 可以選擇性地保留最后一個值,并在新的觀察者開始觀察時立即發送該值(粘性事件的行為),這對于如登錄狀態、配置變化等需要即時知道當前狀態的情況很有用

  3. 線程安全:可以在任何線程上修改 LiveData 的值,而觀察者會在主線程上接收到這些更新,從而避免了直接操作 UI 組件的線程安全問題

  4. 數據一致性: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);}
}
  1. observe 方法用來觀察 LiveDataViewModel 中的 currentSecond,當 currentSecond 的值改變時,onChanged 方法會被調用

  2. 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
  1. 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>
  1. 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>
  1. 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
  1. 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>
  1. 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();}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915365.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915365.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915365.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Go并發聊天室:從零構建實戰

大家好&#xff0c;今天我將分享一個使用Go語言從零開始構建的控制臺并發聊天室項目。這個項目雖然簡單&#xff0c;但它麻雀雖小五臟俱全&#xff0c;非常適合用來學習和實踐Go語言強大的并發特性&#xff0c;尤其是 goroutine 和 channel 的使用。 一、項目亮點與功能特性 …

瘋狂星期四第13天運營日報

網站運營第13天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 昨日訪問量 昨天大概60個ip, 同比上個星期是高點的&#xff0c;但是與星期四差別還是太大了。&#x1f602; 昨日搜索引擎收錄情況 百度依舊0收錄 …

吳恩達《AI for everyone》第二周課程筆記

機器學習項目工作流程以Echo/Alexa&#xff08;語音識別AI&#xff09;作為例子解釋&#xff1a; 1. collect data 收集數據——人為找很多人說 Alexa&#xff0c;并錄制音頻&#xff1b;并且還會讓一群人說其他詞語&#xff0c;比如hello 2. train model 訓練模型——用機器學…

uniapp props、$ref、$emit、$parent、$child、$on

1. uniapp props、ref、ref、ref、emit、parent、parent、parent、child、$on 1.1. 父組件和子組件 propsPage.vue導入props-son-view.vue組件的時候,我們就稱index.vue為父組件依次類推,在vue中只要能獲取到組件的實例,那么就可以調用組件的屬性或是方法進行操作 1.2. pr…

4、ubuntu | dify創建知識庫 | 上市公司個股研報知識庫

1、創建知識庫步驟 創建一個知識庫并上傳相關文檔主要涉及以下五個關鍵步驟&#xff1a; 創建知識庫&#xff1a;首先&#xff0c;需要創建一個新的知識庫。這可以通過上傳本地文件、從在線資源導入數據或者直接創建一個空的知識庫來實現。 指定分段模式&#xff1a;接下來是…

Kubernetes中為Elasticsearch配置多節點共享存儲

在Kubernetes中為Elasticsearch配置多節點共享存儲(ReadWriteMany)需結合存儲后端特性及Elasticsearch架構設計。 由于Elasticsearch默認要求每個節點獨立存儲數據(ReadWriteOnce),直接實現多節點共享存儲需特殊處理。 ??方案一:使用支持ReadWriteMany的存儲后端(推薦…

SpringBoot熱部署與配置技巧

配置文件SpringBoot 的熱部署Spring為開發者提供了一個名為spring-boot-devtools的模塊來使SpringBoot應用支持熱部署&#xff0c;提高開發者的開發效率&#xff0c;無需手動重啟SpringBoot應用相關依賴&#xff1a;<dependency> <groupId>org.springframework.boo…

Python與C#的三元運算符的寫法區別

一、語法結構對比??PyTorch示例??dev torch.device("cuda:0" if torch.cuda.is_available() else "cpu")??邏輯??&#xff1a;若torch.cuda.is_available()為真&#xff0c;則返回"cuda:0"&#xff0c;否則返回"cpu"。??作…

java 學習篇一

java知識點 一、windows不區分大小寫&#xff0c;linux區分大小寫 二、寫java需要JDK&#xff0c;一般運行環境需要JRE 三、JDK安裝一般是傻瓜是安裝 四、java主要工具javac、java&#xff1b;其中javac用于編譯.java -> .class&#xff1b;java用于執行.class文件執行時候不…

仙盟數據庫應用-外貿標簽打印系統 前端數據庫-V8--畢業論文-—-—仙盟創夢IDE

基于 Excel 標簽打印軟件的外貿打印流程優化與實踐摘要&#xff1a;在全球化外貿業務中&#xff0c;標簽打印是貨物流通、信息標識的關鍵環節。本文聚焦 “未來之窗云上打印技術” 的 Excel 標簽打印軟件&#xff0c;結合外貿平臺實際場景&#xff0c;分析其在打印流程中的應用…

【Linux】權限詳解 權限本質、權限屬性、su、sudo提權、chmod\chown\chgrp、文件類別

文章目錄一、權限的認識二、linux的權限本質三、linux的用戶su指令sudo提權四、linux角色五、文件權限屬性六、修改權限的指令操作chmod指令(權限只會驗證一次)chown/chgrp指令修改文件權限的八進制方案七、文件類別詳解一、權限的認識 什么是權限&#xff1f; 生活中處處都有權…

rman清理歸檔

1進入rman rman target / 2&#xff1a;列出所有歸檔日志的路徑 LIST ARCHIVELOG ALL; 3.然后在執行 crosscheck archivelog all;&#xff08;檢查 RMAN 存儲庫中記錄的歸檔日志是否在磁盤或備份存儲中實際存在。 4.然后在執行 delete noprompt expired archivelog all;&…

Selenium 處理動態網頁與等待機制詳解

在使用 Selenium 進行網頁自動化操作時&#xff0c;動態網頁往往是開發者遇到的第一個 “攔路虎”。想象一下&#xff1a;你明明在代碼中寫好了元素定位邏輯&#xff0c;運行時卻頻繁報錯 “元素不存在”&#xff0c;但手動打開網頁時元素明明就在眼前 —— 這很可能是因為網頁…

Salesforce 與外部系統實時集成:基于事件驅動的異步集成架構

在 Salesforce 與外部系統&#xff08;如 ERP、財務系統、物流系統等&#xff09;的實時集成中&#xff0c;“穩定性” 是核心挑戰 —— 既要保證數據同步的及時性&#xff0c;又要應對網絡波動、系統故障、并發沖突等不可控因素。以下從問題本質、技術瓶頸、解決方案細節三個維…

React 的 `cache()` 函數

文章目錄前言一、核心作用二、工作原理三、使用場景1. 避免重復數據請求2. 優化昂貴計算四、緩存規則詳解五、與其它緩存方式對比六、服務端特殊行為七、最佳實踐八、緩存失效策略九、使用限制十、與數據獲取庫集成總結&#xff1a;何時使用 cache()前言 React 的 cache() 函數…

大白編譯——autotools與cmake

注意: 本文內容于 2025-07-20 01:58:56 創建,可能不會在此平臺上進行更新。如果您希望查看最新版本或更多相關內容,請訪問原文地址:大白編譯——autotools與cmake。感謝您的關注與支持! 之前記錄了通過autotools編譯rpm包與deb包的步驟。參考小白編譯——rpm包與deb包 - …

react19+nextjs+antd切換主題顏色

在 React 19 Next.js Ant Design 項目中實現主題切換功能&#xff0c;可以通過以下步驟完成。這里將提供完整方案&#xff0c;包含靜態主題切換和動態實時切換兩種方式。一、基礎配置&#xff08;Ant Design 主題支持&#xff09; 1. 安裝必要依賴 npm install antd ant-desi…

Modbus Slave 使用教程:快速搭建模擬從站進行測試與開發

文章目錄Modbus Slave 使用教程&#xff1a;快速搭建模擬從站進行測試與開發步驟詳解&#xff1a;搭建 Modbus Slave1. 安裝與啟動2. 配置從站連接 (Connection Setup)連接3. 定義從站數據 (設置寄存器/線圈映射)4. 設置初始值與變化模式 (可選但重要)5. 連接 Master 進行測試高…

通俗易懂神經網絡:從基礎到實現

引言 神經網絡是人工智能和深度學習的核心&#xff0c;它模仿人腦的工作方式&#xff0c;通過數據學習復雜的模式。本文將以通俗易懂的方式講解神經網絡的基礎知識&#xff0c;包括單層神經網絡、多層神經網絡&#xff0c;最后用Python代碼實現一個簡單的神經網絡模型。1. 神經…

【Linux】基本指令詳解(三) 指令本質、三個查找指令、打包壓縮、重要熱鍵、linux體系結構、命令行解釋器

文章目錄date指令cal指令find指令(指令本質也是文件)which指令file指令whereis指令alias指令grep指令top指令打包和壓縮指令zip/unzip指令關于rzsz(linux與windows互傳 )tar指令linux機器之間互傳bc指令uname指令(查看linux機器體系結構)幾個重要的熱鍵[Tab]按鍵[Ctrl]c按鍵[Ct…