JetPack 组件总结

news2024/11/18 12:42:59

文章目录

  • JetPack
      • Lifecycle
          • 使用Lifecycle解耦页面和组件
          • 使用Lifecycle解耦Service与组件
          • 使用ProcessLifecycleOwner监听应用程序生命周期
      • ViewModel 与 LiveData
          • ViewModel
          • LiveData
          • ViewModel + LiveData 实现Fragment间通信
      • DataBinding 的意义与应用
          • 意义
          • 使用前的配置
          • import标签
          • 事件绑定
          • 二级页面的绑定
          • 自定义BindingAdapter加载网络图片
          • 双向绑定BaseObservable与ObservableField
          • RecycleView的绑定
          • DataBinding + ViewModle + LiveData
          • DataBinding总结
      • Room(Android官方ORM库Room)
          • Room重要概念
          • Room应用
          • Room+ViewModel+LiveData
          • 升级数据库
          • 异常处理
          • Schema文件
          • 销毁和重建策略
          • 预填充数据库

JetPack

在这里插入图片描述

Lifecycle

使用Lifecycle解耦页面和组件

如果组件的操作与activity的生命周期强关联,可以用Lifecycle解耦

以计时器Demo为例(app打开时计时器启动,app退到后台时计时器停止,再回到app时计时器继续启动)

1、自定义组件 实现 LifecycleObserver

//自定义Chronometer计时器 (继承Chronometer),然后实现LifecycleObserver
public class MyChronometer extends Chronometer implements LifecycleObserver{

    private long elapsedTime;

    public MyChronometer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)  //对应activity的 onResume
    private void startMeter(){
        setBase(SystemClock.elapsedRealtime() - elapsedTime);
        start();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)  //对应activity的  onPause
    private void stopMeter(){
        elapsedTime = SystemClock.elapsedRealtime() - getBase();
        stop();
    }
}

2、在布局文件中使用

<com.kaiya.mvp.jetpacktest.custom.MyChronometer
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="hello world "
        android:textSize="30sp"
        android:id="@+id/chronometer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

3、在onCreate方法中调用

myChronometer = findViewById(R.id.chronometer);
getLifecycle().addObserver(myChronometer);  //添加观察(监听)

4、总结:
涉及到界面生命周期的操作放在了控件内部,使得控件与界面(activity)解耦了。如果后续需要复用控件的话,直接将控件拿去使用就行,不用再关心是否涉及界面生命周期操作

使用Lifecycle解耦Service与组件

以app获取GPS定位为例(点击Start按钮开始获取定位,点击Stop停止获取)

1、添加依赖(LifecycleService)及权限

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

在AndroidManifest.xml清单文件中添加权限

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

2、创建MyLocationService继承LifecycleService

public class MyLocationService extends LifecycleService{
    public MyLocationService() {
        Log.e("mylog","MyLocationService");
        //创建观察者
        MyLocationObserver myLocationObserver = new MyLocationObserver(this);
        //给lifecycle添加观察者
        getLifecycle().addObserver(myLocationObserver);
    }
}

3、创建MyLocationObserver 实现 LifecycleObserver

public class MyLocationObserver implements LifecycleObserver {

    private Context context;
    private LocationManager locationManager;
    private MyLocationListener myLocationObserver;

    public MyLocationObserver(Context context) {
        this.context = context;
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)  //对应Service 的 onCreate
    private void startGetLocation() {
        Log.e("mylog","startGetLocation");
        //获取location管理
        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        myLocationObserver = new MyLocationListener();
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1, myLocationObserver);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) //对应Service 的 onDestory
    private void stopGetLocation(){
        Log.e("mylog","stopGetLocation");
        locationManager.removeUpdates(myLocationObserver);
    }

    //创建Location监听
    static class MyLocationListener implements LocationListener{

        @Override
        public void onLocationChanged(@NonNull Location location) {
            Log.e("mylog","location changed:"+location.toString());
        }

        @Override
        public void onLocationChanged(@NonNull List<Location> locations) {
            Log.e("mylog","1");
        }

        @Override
        public void onFlushComplete(int requestCode) {
            Log.e("mylog","2");
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            Log.e("mylog","3");
        }

        @Override
        public void onProviderEnabled(@NonNull String provider) {
            Log.e("mylog","4");
        }

        @Override
        public void onProviderDisabled(@NonNull String provider) {
            Log.e("mylog","5");
        }
    }
}

4、调用

public class LifeCycleServerActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_life_cycle_server);
        checkPermission();
    }
    //动态注册权限
    public void checkPermission() {
        ActivityCompat.requestPermissions(this,
                new String[]{
                        Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_COARSE_LOCATION},
                111);
    }

    public void startGPS(View view) {
        startService(new Intent(this, MyLocationService.class));
    }

    public void stopGPS(View view) {
        stopService(new Intent(this, MyLocationService.class));
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 111) {
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "缺少权限(" + permissions[i] + ")",Toast.LENGTH_SHORT);
                    return;
                }
            }
        }
    }
}

5、总结
可以看到,获取gps信息需要在生命周期(onCreate和onDestory)中注册和移除监听。而使用了lifecycle后,MyLocationService 仅在构造方法中进行观察者创建和添加。将service与LocationListener解耦。在MyLocationObserver 中实现了逻辑,且可单独出来使用。

使用ProcessLifecycleOwner监听应用程序生命周期
  • 针对整个应用程序的监听,与Activity数量无关
  • Lifecycle.Event.ON_CREATE 只会被调用一次,Lifecycle.Event.ON_DESTORY永远不会被调用

1、自定义Application

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //使用ProcessLifecycleOwner 添加观察者
        ProcessLifecycleOwner.get().getLifecycle().addObserver(new ApplicationObserver());
    }
}

2、创建ApplicationObserver 实现 LifecycleObserver

public class ApplicationObserver implements LifecycleObserver {
    private String TAG = "mylog";

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) //对应应用程序的oncreate
    public void onCreate(){
        Log.e(TAG,"lifecycle.Event.ON_CREATE");
    };
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onStart(){
        Log.e(TAG,"lifecycle.Event.ON_START");
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume(){
        Log.e(TAG,"lifecycle.Event.ON_RESUME");
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause(){
        Log.e(TAG,"lifecycle.Event.ON_PAUSE");
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop(){
        Log.e(TAG,"lifecycle.Event.ON_STOP");
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestory(){
        Log.e(TAG,"lifecycle.Event.ON_DESTORY");
    }
}

3、在AndroidManifest.xml 中添加自定义application

<application
        android:name=".ProcessLifecycleOwner.MyApplication" //添加自定义Application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.JetPackTest">
        <activity android:name=".LifeCycleActivity" />
        <activity android:name=".LifeCycleServerActivity" />
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".LifecycleService.MyLocationService" />
    </application>

4、总结
和之前俩个Lifecycle的使用一样,将application中关于生命周期的操作放到了LifecycleObserver中,使得其独立且能单独使用
刚打开应用时:
在这里插入图片描述
当应用返回到后台时:
在这里插入图片描述
当应用又后台返回前台时:
在这里插入图片描述
5、好处

  • 帮助开发者建立可感知生命周期的组件
  • 组件在其内部管理自己的生命周期,从而降低模块耦合度
  • 降低内存泄露发生的可能性
  • Activity、Fragment、Service、Application均有Lifecycle支持

ViewModel 与 LiveData

ViewModel

由于

  • 瞬态数据丢失(手机界面旋转导致数据丢失)
  • 异步调用的内存泄露
  • 类膨胀提高维护难度和测试难度

出现了ViewModel,作用

  • 它介于View(视图)和Model(数据模型)之间的桥梁
  • 使视图和数据能够分离,也能保持通信
  • 在这里插入图片描述

应用(屏幕旋转后用户操作数据仍然存在)
1、创建ViewModel

public class MyViewModel extends ViewModel {
    public int number;
}

2、使用

public class ViewModelActivity extends AppCompatActivity {
    private TextView textView;
    private MyViewModelActivity viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model);
        textView = findViewById(R.id.content);
        //加载ViewModel
        viewModel = new ViewModelProvider(
                this,
                new ViewModelProvider.AndroidViewModelFactory(getApplication())
                ).get(MyViewModel.class);
        //使用
        textView.setText(String.valueOf(viewModel.number));
    }

    public void addNumber(View view) {
    	//操作ViewModel
        textView.setText(String.valueOf(++viewModel.number));
    }
}

3、效果
在这里插入图片描述在这里插入图片描述
通过加号按钮可以进行数字的增加操作,当屏幕旋转后,数据不丢失

4、原理
在这里插入图片描述

  • 不要向ViewModel中传入context,会导致内存泄露
  • 如果要使用context,请使用AndroidViewModel中的Application
    //继承 AndroidViewModel ,构造方法中就有aplication
    public class MyViewModel extends AndroidViewModel{
    	public int number;
    	public MyViewModel(@NonNull Application application(){
    		super(application);
    	}
    }
    
LiveData

与ViewModel关系

  • 在ViewModel中的数据发生变化时通知页面
    在这里插入图片描述

应用(当ViewModel改变时,LiveData更新View)
1、创建ViewModel+LiveData

public class MyViewModel extends ViewModel {
	//在ViewModel中声明一个LiveData
    private MutableLiveData<Integer> currentSecond;
	//单例模式返回LiveData对象
    public MutableLiveData<Integer> getCurrentSecond() {
        if(currentSecond == null) {
            currentSecond = new MutableLiveData<>();
            currentSecond.setValue(0);
        }
        return currentSecond;
    }
}

2、调用

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model_and_data);
        textView = findViewById(R.id.textView);
        //或者ViewModel对象
        myViewModel = new ViewModelProvider(this
                ,new ViewModelProvider.AndroidViewModelFactory(getApplication())
        ).get(MyViewModel.class);
        //给textView设置值(ViewModel中的LiveData)
        textView.setText(String.valueOf(myViewModel.getCurrentSecond()));
        //当ViewModel中的LiveData值改变时,给textView重新赋值
        myViewModel.getCurrentSecond().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                textView.setText(String.valueOf(integer));
            }
        });
        startTimer();
    }
    
//定时器
private void startTimer() {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                //非UI线程(异步),用postValue
                //UI线程,用setValue
                myViewModel.getCurrentSecond().postValue(
                        myViewModel.getCurrentSecond().getValue()+1
                );
            }
        },1000,1000);
    }
ViewModel + LiveData 实现Fragment间通信

1、创建ViewModel和LiveData

public class FViewModel extends ViewModel {
	//在ViewModel中声明LiveData
    private MutableLiveData<Integer> progress;
	//单例模式
    public MutableLiveData<Integer> getProgress() {
        if(progress == null){
            progress = new MutableLiveData<>();
            progress.setValue(0);
        }
        return progress;
    }
}

2、创建两个Fragment(FirstFragment和SecondFragment),并在布局文件中各放入一个可拖拽的进度条

<SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:min="0"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

3、在FristFragment中监听SeekBar的改变并修改ViewModel中LiveData值

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_1, container, false);
        seekBar = root.findViewById(R.id.seekBar);
        //获取ViewModel
        FViewModel fViewModel = new ViewModelProvider(
                getActivity(),
                new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())
        ).get(FViewModel.class);
		//监听SeekBar(可拖拽进度条),并修改LiveData值
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                fViewModel.getProgress().setValue(i);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
        return root;
    }

4、在SecondFragment中监听LiveData值得改变,并对SecondFragment中的SeekBar赋值,使得SecondFragment与FirstFragment中的SeekBar同步拖动

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_2, container, false);
        seekBar = root.findViewById(R.id.seekBar);
        //获取ModelView对象
        FViewModel fViewModel = new ViewModelProvider(
                getActivity(),
                new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())
        ).get(FViewModel.class);
        //监听ViewModel中LiveData的改变,并对Seekbar赋值
        fViewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                seekBar.setProgress(integer);
            }
        });

        return root;
    }

5、效果图
在这里插入图片描述
liveData优势

  • 确保界面符合数据状态
  • 不会发生内存泄露
  • 不会因Activity停止而导致奔溃
  • 数据始终保持最新状态
  • 适当的配置更改
  • 共享资源

DataBinding 的意义与应用

意义

让布局文件承担了部分原来属于页面的工作,使页面与布局耦合度进一步降低

使用前的配置

1、在build.gradle(app)中

defaultConfig {
        ...
        ...
        //添加以下代码
        buildFeatures {
        	dataBinding true
    	}
    }

2、创建一个对象 Idol

public class Idol {
    public String name;
    public String start;

    public Idol(String name, String start) {
        this.name = name;
        this.start = start;
    }
}

3、在布局文件中
将鼠标指针放在文件左上方的 <?xml 前面,按 alt+回车 ,弹出如图选项
在这里插入图片描述
选择 “Convert to data binding layout ”后,会在布局的外层添加一层布局 <layout xmlns…<data标签
在这里插入图片描述
然后在 <data 标签中传入变量

<data>
	<variable
            name="idol"
            type="com.kaiya.mvp.jetpacktest.dataBinding.Idol" />
</data>

在onCreate中使用

//获取绑定对象
ActivityDataBindingBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding);
Idol idol = new Idol("张三张三张三","五星五星五星");
//设置属性
binding.setIdol(idol);
//可以直接通过id设置控件的值,省略了 findViewById()
binding.image.setImageResource(R.drawable.ic_launcher_foreground);

在布局文件中引用属性

<TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            tools:text="姓名"
            android:text="@{idol.name}"  //引入属性
            android:textSize="30sp"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRig htOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/image" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:textSize="20sp"
            tools:text="星级"
            android:text="@{idol.start}" //引用属性
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView1" />

效果:
在这里插入图片描述
总结:
布局文件绑定了类,让类的属性与控件值绑定,使得页面上减少了 findViewById 和 赋值代码。布局文件承担了部分原来属于页面的工作,使得页面和布局的耦合度进一步降低

import标签

在DataBinding使用中,如果 Idol 类包含int类型属性,在布局文件中如何赋值?

public class Idol {
    public String name;
    public int start;

    public Idol(String name, int start) {
        this.name = name;
        this.start = start;
    }
}

在布局文件中赋值时,如何将int转换成字符串?

Idol idol = new Idol("张三张三张三",4);
//设置属性
binding.setIdol(idol);

<TextView>
	...
	android:text="@{idol.start}" //如何将 4 转换成字符串?
	...
</TextView>

需要创建一个处理类

public class StartUtils {
    public static String getStart(int start){
        switch (start){
            case 1:
                return "一星";
            case 2:
                return "二星";
            case 3:
                return "三星";
            case 4:
                return "四星";
            case 5:
                return "五星";
        }
        return "";
    }
}

然后在布局文件中使用 <import 标签 引入处理类

<data>
        <variable
            name="idol"
            type="com.kaiya.mvp.jetpacktest.dataBindingF.Idol" />
        //引入处理类
        <import type="com.kaiya.mvp.jetpacktest.dataBindingF.StartUtils" />
    </data>

使用:

<TextView>
	...
	android:text="@{StartUtils.getStart(idol.start)}"
	...
</TextView>
事件绑定

1、定义一个事件类

public class EventHandleListener {
    private Context context;
    public EventHandleListener(Context context) {
        this.context = context;
    }
    //定义事件方法
    public void buttonOnClick(View view){
        Toast.makeText(context,"喜欢",Toast.LENGTH_SHORT).show();
        view.setBackgroundColor(context.getResources().getColor(R.color.teal_200));
    }
}

2、给 DataBinding 设置事件

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_data_binding);
        binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding);
        Idol idol = new Idol("张三张三张三","五星五星五星");
        binding.setIdol(idol);
        binding.image.setImageResource(R.drawable.ic_launcher_foreground);
        binding.setEventHandle(new EventHandleListener(this)); // 添加事件
    }

3、在布局文件中引入事件并调用

<data>
        <variable
            name="idol"
            type="com.kaiya.mvp.jetpacktest.dataBindingF.Idol" />
        <variable
            name="eventHandle"
            type="com.kaiya.mvp.jetpacktest.dataBindingF.EventHandleListener" /> //引入事件
        <import type="com.kaiya.mvp.jetpacktest.dataBindingF.StartUtils" />
    </data>
//调用
<Button
	android:onClick="@{eventHandle.buttonOnClick}" //调用点击事件
	android:layout_marginTop="20dp"
	app:layout_constraintTop_toBottomOf="@+id/textView2"
	app:layout_constraintLeft_toLeftOf="parent"
	app:layout_constraintRight_toRightOf="parent"
	android:text="喜欢"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content" />

二级页面的绑定

在这里插入图片描述
现在用 <include 实现一个嵌套布局

<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>
        <variable
            name="idol"
            type="com.kaiya.mvp.jetpacktest.dataBindingF.Idol" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".DataBindingTwoActivity">

        <ImageView
            android:id="@+id/image"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:layout_marginTop="20dp"
            android:src="@drawable/ic_launcher_background"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
		//嵌套布局
        <include
            android:layout_marginTop="50dp"
            layout="@layout/item_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/image" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

如何将 idol 对象传给 二级布局 <include中呢?在 <include中引入对象

 <include
            app:idol="@{idol}"  //引入对象
            android:layout_marginTop="50dp"
            layout="@layout/item_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/image" />

然后在二级布局中绑定对象

<data>
        <variable
            name="idol"
            type="com.kaiya.mvp.jetpacktest.dataBindingF.Idol" />
    </data>

然后调用

<TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{idol.name}" //使用
            android:textSize="30sp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="姓名" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{idol.start}" //使用
            android:textSize="20sp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView1"
            tools:text="五星" />

DataBinding设置对象

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_data_binding_two);
        binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding_two);
        Idol idol = new Idol("嵌套布局赋值","嵌套布局赋值");
        binding.setIdol(idol);
    }

效果:
在这里插入图片描述

自定义BindingAdapter加载网络图片

例子:使用 @BindingAdapter 加载网络图片

1、创建MyBindingAdapter,添加 @BindingAdapter 注解

public class MyBindingAdapter {
	//@BindingAdapter 可以单独使用
	//其中 ("image")为自定义字符串, 在布局文件中通过 app:image 使用
    @BindingAdapter("image")
    public static void setImage(ImageView image, String url){
        if(!TextUtils.isEmpty(url))
            Picasso.get()
                .load(url)
                .placeholder(R.drawable.ic_launcher_background)
                .into(image);
        else
            image.setBackgroundColor(Color.GRAY);
    }
}

1、在布局文件中绑定图片

<data>
        <variable
            name="networkImage"  //定义名称
            type="String" />  //类型
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".CustomBindAdapterActivity">

        <ImageView
        	//app:image对应 @BindingAdapter(“image”) 中的image
        	//然后使用<data>中定义的networkImage变量
            app:image="@{networkImage}" 
            tools:srcCompat="@tools:sample/avatars"
            android:id="@+id/imageView"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="300dip"
            android:layout_height="300dip" />

    </androidx.constraintlayout.widget.ConstraintLayout>

3、通过DataBinding设置网络图片路径
访问网络图片,需要先添加权限

<uses-permission android:name="android.permission.INTERNET" />

使用

//获取binding
ActivityCustomBindAdapterBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_custom_bind_adapter);
//设置networkimage
binding.setNetworkImage("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2018-06-27%2F5b3345789ca2c.jpg&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1672813829&t=478fb36f728ae1f18cd508b261c4b556");

流程:
通过setNetworkImage() 将图片路径传到布局文件中的 networkImage 变量中,然后通过 app:image=“@{networkImage}” 又将图片路径传到类 MyBindingAdaptersetImage() 方法中,然后进行逻辑处理

例子:使用 @BindingAdapter 加载本地图片**
1、布局文件中增加变量,并使用变量

<data>
        <variable
            name="networkImage"
            type="String" />
        <variable
            name="localImage" //增加的变量
            type="int" />
    </data>
`
//使用
<ImageView
            app:image="@{localImage}" //使用
            tools:srcCompat="@tools:sample/avatars"
            android:id="@+id/imageView"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="300dip"
            android:layout_height="300dip" />

``

2、重载setImage方法(相同的注解和关键字符串,不同的参数)

```java
	@BindingAdapter("image")
    public static void setImage(ImageView image, String url){
        if(!TextUtils.isEmpty(url))
            Picasso.get()
                .load(url)
                .placeholder(R.drawable.ic_launcher_background)
                .into(image);
        else
            image.setBackgroundColor(Color.GRAY);
    }

    @BindingAdapter("image")  
    public static void setImage(ImageView image, int resId){ //重载
        image.setImageResource(resId);
    }

3、给 DataBinding 设置本地图片路径

binding.setLocalImage(R.drawable.ic_launcher_background);

效果
在这里插入图片描述
例子:使用 @BindingAdapter 加载多个来源

1、布局文件引用多个变量

<ImageView
        	//引用了两个变量,imageOne和imageTwo
            app:imageOne="@{networkImage}" 
            app:imageTwo="@{localImage}" 
            tools:srcCompat="@tools:sample/avatars"
            android:id="@+id/imageView"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="300dip"
            android:layout_height="300dip" />

2、对应的注解和方法

//参数可选,网络图片为空时,加载本地图片
@BindingAdapter(value = {"imageOne","imageTwo"}, requireAll = false)
    public static void setImage(ImageView image, String url,int resId){
        if(!TextUtils.isEmpty(url))
            Picasso.get()
                .load(url)
                .placeholder(R.drawable.ic_launcher_background)
                .into(image);
        else
            image.setImageResource(resId);
    }

3、设置哪个值就显示哪个

//如果只设置网络图片路径,则控件只展示网络图片
binding.setNetworkImage("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2018-06-27%2F5b3345789ca2c.jpg&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1672813829&t=478fb36f728ae1f18cd508b261c4b556");
//如果只设置本地图片路径,则控件只展示本地图片
binding.setLocalImage(R.drawable.songshu);
双向绑定BaseObservable与ObservableField

在这里插入图片描述

BaseObservable实现双向绑定

1、创建对象类

public class User {
    public String username;

    public User(String username) {
        this.username = username;
    }
}

2、创建继承与BaseObservable的类

public class userViewModel extends BaseObservable {
    private User user;
	//初始化该类时,自动创建User对象
    public userViewModel() {
        this.user = new User("张三");
    }

    @Bindable  //使用@Bindable ,当User发生改变时,会调用该方法
    public String getUserName(){
        return user.username;
    }
	//当EditText中的内容发生改变,就会调用这个方法
    public void setUserName(String username){
        if(!TextUtils.isEmpty(username)) {
            user.username = username;
            Log.e("mylog","setUsername"+username);
            notifyPropertyChanged(BR.userName);
        }
    }
}

3、绑定类并引用

<data>
        <variable
            name="userViewModel"  //绑定类
            type="com.kaiya.mvp.jetpacktest.BaseObsAndObsField.userViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".BaseObsFieldActivity">

        <EditText
            android:text="@={userViewModel.userName}" //引用类
            android:hint="请输入内容"
            android:layout_marginTop="30dp"
            android:id="@+id/edit"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="300dp"
            android:layout_height="30dp" />

    </androidx.constraintlayout.widget.ConstraintLayout>

4、DataBinding 设置类

ActivityBaseObsFieldBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_base_obs_field);
binding.setUserViewModel(new userViewModel());

//修改对象属性的方法
public void changeName(View view) {
        userViewModel.setUserName("李四王麻子");
}

5、效果

在这里插入图片描述
当点击按钮修改对象属性为“李四王麻子”后,editView内容也发生改变。

在这里插入图片描述

而当修改editView内容时,也同时会修改对象的属性。
在这里插入图片描述

ObservableField实现双向绑定

1、同样需要创建对象

public class User {
    public String username;

    public User(String username) {
        this.username = username;
    }
}

2、创建viewmodel 并 使用ObservableField

public class ObsFieldViewModel {
    private ObservableField<User> userObservableField; //声明

    public ObsFieldViewModel(){
        User user = new User("zhangsan");
        userObservableField = new ObservableField<>();
        userObservableField.set(user); //设置对象
    }
	//get方法
    public String getUserName(){
        return userObservableField.get().username;
    }
	//set方法
    public void setUserName(String userName){
        userObservableField.get().username = userName;
    }
}

3、绑定及引用

<variable
            name="ObsFieldViewModel"
            type="com.kaiya.mvp.jetpacktest.baseObsAndObsField.ObsFieldViewModel" />
//引用
<EditText
            app:layout_constraintTop_toBottomOf="@+id/textView2"
            android:text="@={ObsFieldViewModel.userName}" //引用
            android:hint="请输入内容"
            android:layout_marginTop="30dp"
            android:id="@+id/edit2"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="300dp"
            android:layout_height="50dp" />

4、dataBinding设置类

binding.setObsFieldViewModel(obsFieldViewModel); //设置类
//点击按钮修改对象的属性
public void changeName1(View view) {
        obsFieldViewModel.setUserName("lisiwangmazi");
    }

5、效果同 BaseObservable,但更加简洁

RecycleView的绑定

1、recycleView布局

<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>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".RecycleViewActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycleview"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

2、创建Idol类

public class Idol {
    public String chName;
    public String enName;
    public String image;

    public Idol(String chName, String enName, String image) {
        this.chName = chName;
        this.enName = enName;
        this.image = image;
    }
}

3、创建recycleViewAdapter

public class recycleViewAdapter extends RecyclerView.Adapter<recycleViewAdapter.MyViewHolder> {
    List<Idol> idols;

    public recycleViewAdapter(List<Idol> idols) {
        this.idols = idols;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemBinding itemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
                R.layout.item,
                parent,
                false);
        return new MyViewHolder(itemBinding);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Idol idol = idols.get(position);
        holder.itemBinding.setIdol(idol);
    }

    @Override
    public int getItemCount() {
        return idols.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder{

        private ItemBinding itemBinding;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
        }

        public MyViewHolder(ItemBinding itemBinding) {
            super(itemBinding.getRoot());
            this.itemBinding = itemBinding;
        }
    }

}

4、adapter 的item布局

<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>
        <variable
            name="idol"  //引用类
            type="com.kaiya.mvp.jetpacktest.recycleView.Idol" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="65dp">

        <ImageView
            app:itemImage="@{idol.image}"  //通过@bindingAdapter加载图片
            android:id="@+id/image"
            android:src="@drawable/ic_launcher_background"
            android:layout_margin="10dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            android:layout_width="50dp"
            android:layout_height="50dp" />

        <TextView
            android:id="@+id/nameC"
            android:text="@{idol.chName}" //赋值
            android:layout_marginTop="2dp"
            android:layout_marginStart="10dp"
            app:layout_constraintLeft_toRightOf="@+id/image"
            app:layout_constraintTop_toTopOf="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/nameE"
            android:text="@{idol.enName}" //赋值
            android:layout_marginBottom="2dp"
            app:layout_constraintBottom_toBottomOf="@+id/image"
            app:layout_constraintLeft_toLeftOf="@+id/nameC"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

5、因为要使用@BindingAdapter加载图片,需创建

public class ImageViewBindingAdapter {

    @BindingAdapter("itemImage")  //与布局文件中的标签一致
    public static void setImage(ImageView image, String url){
        if(!TextUtils.isEmpty(url))
            Picasso.get()
                .load(url)
                .placeholder(R.drawable.ic_launcher_background)
                .into(image);
        else
            image.setBackgroundColor(Color.GRAY);
    }
}

6、使用
先创建数据源

public class IdolUtils {
    public static List<Idol> get(){
        List<Idol> list = new ArrayList<>();
        Idol i1 = new Idol("张三","zhaangsan","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202105%2F10%2F20210510174256_4b4d0.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1673661917&t=6e3d1f6624a399479681ee213355f3b0");
        list.add(i1);
        Idol i2 = new Idol("李四","lisii","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202106%2F09%2F20210609081952_51ef5.thumb.1000_0.jpg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1673661992&t=b380fa35e774dc0ffe409012af243f47");
        list.add(i2);
        Idol i3 = new Idol("王麻子","wangmazi","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202111%2F10%2F20211110082815_526b2.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1673662006&t=b1d90ec1b0011bf00f89544bf55f0b7d");
        list.add(i3);
        return list;
    }
}

然后在activity中使用

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //获取DataBinding
        ActivityRecycleViewBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_recycle_view);
        //给recycleView设置线性布局
        binding.recycleview.setLayoutManager(new LinearLayoutManager(this));
        //设置数据源
        recycleViewAdapter adapter = new recycleViewAdapter(IdolUtils.get());
        binding.recycleview.setAdapter(adapter);
    }

7、效果
在这里插入图片描述

DataBinding + ViewModle + LiveData

例子:计分板
在这里插入图片描述
1、布局文件

<?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>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".DataBindViewModelLiveDataActivity">

        <TextView
            android:layout_marginTop="20dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/item2"
            android:id="@+id/item1"
            android:text="TeamA"
            android:textSize="25sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            app:layout_constraintTop_toTopOf="@id/item1"
            app:layout_constraintLeft_toRightOf="@id/item1"
            app:layout_constraintRight_toRightOf="parent"
            android:id="@+id/item2"
            android:text="TeamB"
            android:textSize="25sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_marginTop="20dp"
            app:layout_constraintTop_toBottomOf="@+id/item1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/textView2"
            android:id="@+id/textView1"
            android:text="0"
            android:textSize="45sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            app:layout_constraintTop_toTopOf="@id/textView1"
            app:layout_constraintLeft_toRightOf="@id/textView1"
            app:layout_constraintRight_toRightOf="parent"
            android:id="@+id/textView2"
            android:text="0"
            android:textSize="45sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:text="+1"
            android:layout_marginTop="20dp"
            android:id="@+id/jiaOne1"
            app:layout_constraintTop_toBottomOf="@+id/textView1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/jiaOne2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:text="+1"
            android:id="@+id/jiaOne2"
            app:layout_constraintTop_toTopOf="@+id/jiaOne1"
            app:layout_constraintLeft_toRightOf="@id/jiaOne1"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:text="+2"
            android:layout_marginTop="20dp"
            android:id="@+id/jiaTwo1"
            app:layout_constraintTop_toBottomOf="@+id/jiaOne1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/jiaTwo2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:text="+2"
            android:id="@+id/jiaTwo2"
            app:layout_constraintTop_toTopOf="@+id/jiaTwo1"
            app:layout_constraintLeft_toRightOf="@id/jiaTwo1"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:text="+3"
            android:layout_marginTop="20dp"
            android:id="@+id/jiaThree1"
            app:layout_constraintTop_toBottomOf="@+id/jiaTwo1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/jiaThree2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:text="+3"
            android:id="@+id/jiaThree2"
            app:layout_constraintTop_toTopOf="@+id/jiaThree1"
            app:layout_constraintLeft_toRightOf="@id/jiaThree1"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <ImageButton
            android:background="@mipmap/reten"
            android:text="+3"
            android:layout_marginTop="20dp"
            android:id="@+id/imageBtn1"
            app:layout_constraintTop_toBottomOf="@+id/jiaThree1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/imageBtn2"
            android:layout_width="50dp"
            android:layout_height="50dp" />

        <ImageButton
            android:text="+3"
            android:id="@+id/imageBtn2"
            android:background="@mipmap/reset"
            app:layout_constraintTop_toTopOf="@+id/imageBtn1"
            app:layout_constraintLeft_toRightOf="@id/imageBtn1"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="50dp"
            android:layout_height="50dp" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

2、创建ViewModel

public class MyViewModel extends ViewModel {

    private MutableLiveData<Integer> aTeamScore; //定义LiveData的变量
    private MutableLiveData<Integer> bTeamScore; //定义LiveData的变量
    private Integer aLast;
    private Integer bLast;
	
	//提供get方法
    public MutableLiveData<Integer> getaTeamScore() {
        if(aTeamScore == null){
            aTeamScore = new MutableLiveData<>();
            aTeamScore.setValue(0);
        }
        return aTeamScore;
    }
	//提供get方法
    public MutableLiveData<Integer> getbTeamScore() {
        if(bTeamScore == null){
            bTeamScore = new MutableLiveData<>();
            bTeamScore.setValue(0);
        }
        return bTeamScore;
    }
	//加法
    public void aTeamAdd(int i){
        saveLastScore();
        aTeamScore.setValue(aTeamScore.getValue() + i);
    }
    //加法
    public void bTeamAdd(int i){
        saveLastScore();
        bTeamScore.setValue(bTeamScore.getValue() + i);
    }
    //撤销
    public void undo(){
        aTeamScore.setValue(aLast);
        bTeamScore.setValue(bLast);
    }
    //重置
    public void reset(){
        aTeamScore.setValue(0);
        bTeamScore.setValue(0);
    }

    //记录上一次的分数
    private  void saveLastScore(){
        this.aLast = aTeamScore.getValue();
        this.bLast = bTeamScore.getValue();
    }
}

3、在布局文件中引用并使用

	//绑定viewmodel
    <data>
        <variable
            name="viewModel"
            type="com.kaiya.mvp.jetpacktest.dataBindViewModelLiveData.MyViewModel" />
    </data>

//将变量值赋给控件
<TextView
            android:layout_marginTop="20dp"
            app:layout_constraintTop_toBottomOf="@+id/item1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/textView2"
            android:id="@+id/textView1"
            android:text="@{String.valueOf(viewModel.getaTeamScore())}" //赋值
            android:textSize="45sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
//让按钮点击事件调用加法方法
<Button
            android:onClick="@{()->viewModel.aTeamAdd(1)}" //调用加法,+1
            android:text="+1"
            android:layout_marginTop="20dp"
            android:id="@+id/jiaOne1"
            app:layout_constraintTop_toBottomOf="@+id/textView1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/jiaOne2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
<Button
            android:onClick="@{()->viewModel.aTeamAdd(2)}" //调用加法,+2
            android:text="+2"
            android:layout_marginTop="20dp"
            android:id="@+id/jiaTwo1"
            app:layout_constraintTop_toBottomOf="@+id/jiaOne1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/jiaTwo2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
//重置和撤销
<ImageButton
            android:onClick="@{()->viewModel.undo()}" //撤销
            android:background="@mipmap/reten"
            android:text="+3"
            android:layout_marginTop="20dp"
            android:id="@+id/imageBtn1"
            app:layout_constraintTop_toBottomOf="@+id/jiaThree1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/imageBtn2"
            android:layout_width="50dp"
            android:layout_height="50dp" />

        <ImageButton
            android:onClick="@{()->viewModel.reset()}" //重置
            android:text="+3"
            android:id="@+id/imageBtn2"
            android:background="@mipmap/reset"
            app:layout_constraintTop_toTopOf="@+id/imageBtn1"
            app:layout_constraintLeft_toRightOf="@id/imageBtn1"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_width="50dp"
            android:layout_height="50dp" />

4、界面调用

//获取DataBinding
        ActivityDataBindViewModelLiveDataBinding binding =
                DataBindingUtil.setContentView(this,R.layout.activity_data_bind_view_model_live_data);
        //获取ViewModel
        MyViewModel viewModel =
                new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication()))
                        .get(MyViewModel.class);
        binding.setViewModel(viewModel);
        binding.setLifecycleOwner(this); //感知activity的生命周期
DataBinding总结

1、不在需要findViewById,项目更加简洁,可读性更高
2、布局文件可以包含简单的业务逻辑

Room(Android官方ORM库Room)

Android采用SQLite作为数据库存储,开源社区常见的ORM(Object Relational Mapping)库有ORMLite、GreenDAO等。Room和其他库一样,也是在SQLite上提供一层封装

Room重要概念
  • Entity: 实体类,对应的是数据库的一张表结构,使用注解@Entity标记
  • Dao: 包含访问一系列访问数据库方法,使用注解@Dao标记
  • Database: 数据库持有者,作与应用持久化相关数据的底层连接的主要接入点。使用@Database标记,另外需满足以下条件:定义的类必须是一个继承与RoomDatabase的抽象类,在注解中需要定义与数据库相关联的实体类列表。包含一个没有参数的抽象方法并返回一个Dao对象
  • 在这里插入图片描述
Room应用

例子:对数据实现增删改查

在这里插入图片描述
1、布局文件

<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=".RoomActivity">
    <Button
        android:text="增加"
        android:layout_marginTop="20dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/delete"
        android:id="@+id/add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:text="删除"
        app:layout_constraintTop_toTopOf="@+id/add"
        app:layout_constraintLeft_toRightOf="@+id/add"
        app:layout_constraintRight_toRightOf="parent"
        android:id="@+id/delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:text="修改"
        app:layout_constraintTop_toBottomOf="@+id/add"
        android:layout_marginTop="20dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/search"
        android:id="@+id/change"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:text="查询"
        app:layout_constraintTop_toTopOf="@+id/change"
        app:layout_constraintLeft_toRightOf="@+id/change"
        app:layout_constraintRight_toRightOf="parent"
        android:id="@+id/search"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycleview"
        app:layout_constraintTop_toBottomOf="@+id/change"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_width="0dp"
        android:layout_height="0dp" />


</androidx.constraintlayout.widget.ConstraintLayout>

1、添加依赖

implementation 'androidx.room:room-runtime:2.2.5'
annotationProcessor 'androidx.room:room-compiler:2.2.5'

2、定义Entity(会自动创建表)

@Entity(tableName = "student") //表名
public class Student {

    @PrimaryKey(autoGenerate = true)  //主键,自动增长
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER) //列名"id", 类型 INTEGER
    public int id;

    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
    public String name;

    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    public int age;

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Ignore  //忽略(仅自己使用,room不会管这个构造方法)
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Ignore //忽略
    public Student(int id) {
        this.id = id;
    }
}

3、定义Dao

@Dao 
public interface StudentDao {

    @Insert
    void insertStudent(Student... students);

    @Delete
    void deleteStudent(Student... students);

    @Update
    void updateStudent(Student... students);

    @Query("SELECT * FROM student")
    List<Student> getAllStudent();

    @Query("SELECT * FROM student WHERE id = :idd")
    List<Student> getStudentById(int idd);
}

4、定义MyDatabase

@Database(entities = {Student.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase{
    private static final String DATABASE_NAME = "Download/jetpack.db"; //db文件名
    private static final String DB_PATH = String.format("%s/%s",
            Environment.getExternalStorageDirectory().getAbsolutePath(), DATABASE_NAME);


    private static MyDatabase mInstance;

    public static synchronized MyDatabase getInstance(Context context){
        Log.e("mylog",DB_PATH);
        if(mInstance == null)
            mInstance = Room.databaseBuilder(
                    context.getApplicationContext(),
                    MyDatabase.class,
                    //直接写db文件名,会默认创建到data/data路径下
                    //如果写具体路径,则会创建到具体路径下
                    DATABASE_NAME) 
                    .build();
        return mInstance;
    }

    public abstract StudentDao getStudentDao();
}

5、定义RecycleView的adapter

public class StudentRecycleAdapter extends RecyclerView.Adapter{
    List<Student> students;

    public StudentRecycleAdapter(List<Student> students) {
        this.students = students;
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.room_item,parent,false);
        return new MyViewHolder(root);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Student student = students.get(position);
        TextView id = holder.itemView.findViewById(R.id.userid);
        TextView name = holder.itemView.findViewById(R.id.username);
        TextView age = holder.itemView.findViewById(R.id.userage);
        id.setText(String.valueOf(student.id));
        name.setText(student.name);
        age.setText(String.valueOf(student.age));
    }

    @Override
    public int getItemCount() {
        return students.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder{

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }
}

6、使用

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_room);
        RecyclerView recyclerView = findViewById(R.id.recycleview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        List<Student> students = new ArrayList<>();
        students.add(new Student(1,"王丹江",19));
        adapter = new StudentRecycleAdapter(students);
        recyclerView.setAdapter(adapter);

        MyDatabase database = MyDatabase.getInstance(this);
        dao = database.getStudentDao();
    }

增删改查

public void mInsert(View view) {
        Log.e("mylog","mInsert");
        Student s1 = new Student("张三",20);
        Student s2 = new Student("李四",22);
        new InsertTask(dao).execute(s1,s2);
    }

    class InsertTask extends AsyncTask<Student,String,String> {
        private StudentDao studentDao;

        public InsertTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected String doInBackground(Student... students) {
            studentDao.insertStudent(students);
            return null;
        }
    }

    public void mDelete(View view) {
        Log.e("mylog","mDelete");
        dao.deleteStudent();
    }

    public void mUpdate(View view) {
        Log.e("mylog","mUpdate");
        dao.updateStudent();
    }

    public void mSearch(View view) {
        Log.e("mylog","mSearch");
        new GetAllTask(dao).execute();
    }

    class GetAllTask extends AsyncTask<Void,Void,Void>{

        private StudentDao studentDao;

        public GetAllTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            List<Student> students = studentDao.getAllStudent();
            Log.e("mylog","getStudent:"+students.size());
            adapter.setStudents(students);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            adapter.notifyDataSetChanged();
        }
    }
Room+ViewModel+LiveData

在这里插入图片描述
可以对上面的demo做修改,使用ViewModel+LiveData实现自动更新

1、创建 Repository,并持有Dao引用,然后操作数据库

//Repository 仓库
public class StudentRepository {
	//持有的dao层引用
    private StudentDao studentDao; 

    public StudentRepository(Context context) {
        MyDatabase database = MyDatabase.getInstance(context);
        this.studentDao = database.getStudentDao();
    }

    public void insertStudent(Student... students){
        new InsertTask(studentDao).execute(students);
    }
    public void updateStudent(Student... students){
        new UpdateTask(studentDao).execute(students);
    }
    public void deleteStudent(Student... students){
        new DeleteTask(studentDao).execute(students);
    }
    public void deleteAllStudent(){
        new DeleteAllTask(studentDao).execute();
    }
    public LiveData<List<Student>> getAllStudentLive(){
        return studentDao.getAllStudentLive();
    }

    class InsertTask extends AsyncTask<Student,Void ,Void>{

        private StudentDao dao;

        public InsertTask(StudentDao dao) {
            this.dao = dao;
        }

        @Override
        protected Void doInBackground(Student... students) {
            dao.insertStudent(students);
            return null;
        }
    }
    class UpdateTask extends AsyncTask<Student,Void,Void>{

        private StudentDao dao;

        public UpdateTask(StudentDao dao) {
            this.dao = dao;
        }

        @Override
        protected Void doInBackground(Student... students) {
            dao.updateStudent(students);
            return null;
        }
    }
    class DeleteTask extends AsyncTask<Student,Void,Void>{
        private StudentDao dao;

        public DeleteTask(StudentDao dao) {
            this.dao = dao;
        }

        @Override
        protected Void doInBackground(Student... students) {
            dao.deleteStudent(students);
            return null;
        }
    }
    class DeleteAllTask extends AsyncTask<Void,Void,Void>{
        private StudentDao dao;

        public DeleteAllTask(StudentDao dao) {
            this.dao = dao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            dao.deleteAllStudent();
            dao.deleteSequence();
            return null;
        }
    }
}

2、创建ViewModel ,持有Repository引用 ,从而实现数据库操作

public class StudentViewModel extends AndroidViewModel {
    private StudentRepository repository;

    public StudentViewModel(@NonNull Application application) {
        super(application);
        repository = new StudentRepository(application);
    }

    public void insertStudent(Student... students){
        repository.insertStudent(students);
    }

    public void deleteStudent(Student... students){
        repository.deleteStudent(students);
    }
    public void deleteAllStudent(){
        repository.deleteAllStudent();
    }
    public void updateStudent(Student... students){
        repository.updateStudent(students);
    }
    public LiveData<List<Student>> getAllStudentLive(){
        return repository.getAllStudentLive();
    }

}

特别注意 getAllStudentLive() 方法

//返回类型是 LiveData
public LiveData<List<Student>> getAllStudentLive(){
	return repository.getAllStudentLive(); //直接返回dao层的方法,实现自动调用
}

3、需要在Dao层添加 getAllStudentLive 方法

@Query("SELECT * FROM student")
LiveData<List<Student>> getAllStudentLive(); //dao是直接 支持 LiveData的

4、使用

public class RoomVModelLDataActivity extends AppCompatActivity {
    private StudentRecycleAdapter adapter;
    private StudentViewModel studentViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_room_v_model_l_data);
        RecyclerView recyclerView = findViewById(R.id.recycleview1);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        List<Student> students = new ArrayList<>();
        adapter = new StudentRecycleAdapter(students);
        recyclerView.setAdapter(adapter);
		//获取ViewModel
        studentViewModel = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication()))
                .get(StudentViewModel.class);
        //监听数据变化,实现自动查询 更新
        studentViewModel.getAllStudentLive().observe(this, new Observer<List<Student>>() {
            @Override
            public void onChanged(List<Student> students) {
                for(Student s: students){
                    Log.e("mylog",s.name);
                }
                adapter.setStudents(students);
                adapter.notifyDataSetChanged();
            }
        });
    }

    public void rmInsert(View view) {
        Student s1 = new Student("jack",20);
        Student s2 = new Student("rose",28);
        studentViewModel.insertStudent(s1,s2);
    }

    public void rmDelete(View view) {
        Student s1 = new Student(2);
        studentViewModel.deleteStudent(s1);
    }

    public void rmUpdate(View view) {
        Student s1 = new Student(3,"LLLL",21);
        studentViewModel.updateStudent(s1);
    }

    public void rmClean(View view) {
        studentViewModel.deleteAllStudent();
    }
}

5、效果
在这里插入图片描述在这里插入图片描述在这里插入图片描述

升级数据库

1、在 MyDatabase 中添加

//从版本1升级到版本2
static final Migration MIGRATION_1_2 = new Migration(1,2){
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
        	//添加字段 sex,数字类型,不为空,默认1
            database.execSQL("ALTER TABLE student ADD COLUMN sex INTEGER NOT NULL DEFAULT 1");
        }
    };

2、给 MyDatabase 添加

mInstance = Room.databaseBuilder(
                    context.getApplicationContext(),
                    MyDatabase.class,
                    DATABASE_NAME)
                    .addMigrations(MIGRATION_1_2) //添加Mirations
                    .build();

3、修改版本号在这里插入代码片

修改版本号为2
@Database(entities = {Student.class}, version = 2, exportSchema = false)

4、对应实体类也相应改变(增加字段sex)

@ColumnInfo(name = "sex", typeAffinity = ColumnInfo.INTEGER)
    public int sex;

5、效果
在这里插入图片描述
注意 :如果要从版本1 升级到版本3 。
当 存在 Migration_1_3 = new Migration(1,3){ 时,会调用
当不存在时,会依次调用 Migration_1_2Migration_2_3

异常处理
  • 假设我们将数据库版本升级到4,却没有为此写相应的Migration,则会出现一个 lllegalStrateException异常 ,加入 fallbackToDestructiveMigration() ,该方法在出现升级异常时,重建数据库,同时 数据也会丢失
    mInstance = Room.databaseBuilder(
                        context.getApplicationContext(),
                        MyDatabase.class,
                        DATABASE_NAME)
                        .addMigrations(MIGRATION_1_2,MIGRATION_2_3)
                        .fallbackToDestructiveMigration()  //加入这句
                        .build();
    
Schema文件
  • Room在每次数据库升级过程中,都会导出一个Schema文件,这是一个json格式的文件,其中包含了数据库的基本信息,有了该文件,开发者能清楚的知道数据库的历次变更情况,极大地方便了开发者排查问题

    1、修改 exportSchema 属性

    @Database(entities = {Student.class}, version = 3, exportSchema = true)
    

    2、在 build.gradle 中添加

    defaultConfig {
            applicationId "com.kaiya.mvp.jetpacktest"
            minSdkVersion 28
            //noinspection ExpiredTargetSdkVersion
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            javaCompileOptions{
                annotationProcessorOptions{
                    //指定数据库schema导出的位置
                    arguments = ["room.schemaLocation":"$projectDir/scheas".toString()]
                }
            }
        }
    

    3、在每次数据库变更时,就会自动生成文件
    在这里插入图片描述

销毁和重建策略

在SQLite中修改表结构比较麻烦,例如,我们想将Student表中的sex字段类型从INTEGER改为TEXT,最好的方式是采用销毁与重建策略,大致分为以下步骤:

  • 创建一张符合表结构要求的临时表temp_student

  • 将数据从旧表student赋值到临时表temp_sutdent中

  • 删除旧表student

  • 将临时表temp_student重命名为 student
    在这里插入图片描述
    1、创建 MIGRATION_3_4

    static final Migration MIGRATION_3_4 = new Migration(3,4){
            @Override
            public void migrate(@NonNull SupportSQLiteDatabase database) {
            	//创建需要的临时表
                database.execSQL("CREATE TABLE temp_student (" +
                        "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                        "name TEXT,"+
                        "age INTEGER NOT NULL,"+
                        "sex TEXT DEFAULT 'M',"+
                        "bar_data INTEGER NOT NULL DEFAULT 1 )");
                //将所有数据拷贝到 临时表中
                database.execSQL("INSERT INTO tmep_student (name,age,sex,bar_data)" +
                        " SELECT name,age,sex,bar_data FROM student");
                // 删除student表
                database.execSQL("DROP TABLE student");
                // 将临时表改名为 student
                database.execSQL("ALTER TABLE temp_student RENAME TO student");
            }
        };
    

    2、添加到 MyDatabase

    public static synchronized MyDatabase getInstance(Context context){
            if(mInstance == null)
                mInstance = Room.databaseBuilder(
                        context.getApplicationContext(),
                        MyDatabase.class,
                        DATABASE_NAME)
                        .addMigrations(MIGRATION_2_3,MIGRATION_3_4) //添加
                        .fallbackToDestructiveMigration()
                        .build();
            return mInstance;
        }
    

    3、修改版本号为4

    @Database(entities = {Student.class}, version = 4, exportSchema = true)
    

    4、修改实体

    //将  INTEGER 改为 TEXT,
        @ColumnInfo(name = "sex", typeAffinity = ColumnInfo.TEXT)
        public String sex;
    

    5、效果
    在这里插入图片描述
    SEX 字段类型变为 TEXT

预填充数据库
  • 有时候我们希望应用自带一些数据供我们使用,我们可以将数据库文件放入assets目录一起打包发布,在用户首次打开App时,使用 createFromAsset()createFromFile() 创建Room数据库
    在这里插入图片描述
    1、创建assets文件夹
    对着 main目录右键,new - Directory - 选择 assets
    在这里插入图片描述
    在这里插入图片描述
    1、先创建一个db数据库
    在这里插入图片描述
    2、将数据库放入assets中
    在这里插入图片描述
    3、添加到 MyDatabase

    mInstance = Room.databaseBuilder(
                        context.getApplicationContext(),
                        MyDatabase.class,
                        DATABASE_NAME)
                        .addMigrations(MIGRATION_2_3,MIGRATION_3_4)
                        //.fallbackToDestructiveMigration()
                        .createFromAsset("prestudent.db") //添加
                        .build();
    

    4、效果
    在这里插入图片描述
    而且新创建的数据库中也存在这些数据:
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/107237.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SWPUCTF 2021 新生赛

&#x1f60b;大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白&#xff0c;自学ing。 本人水平有限&#xff0c;欢迎各位大佬指点&#xff0c;一起学习&#x1f497;&#xff0c;一起进步⭐️。 ⭐️此后如竟没有炬火&#xff0c;我便是唯一的光。⭐️ 目录 g…

Kotlin + Jpa + Querydsl

Kotlin Jpa Querydsl 本篇主要介绍一下 kotlin jpa querydsl . jpa querydsl 是我很喜欢的一种搭配,它能够让你写sql语句一样的去写代码 , 以前我也写过关于 java版本的, 本篇就来看看kotlin中如何去使用 1.引入插件 主要引入jpa插件 和 kapt Kotlin annotation processi…

【剧前爆米花--爪哇岛寻宝】Cloneable接口和深浅拷贝

作者&#xff1a;困了电视剧 专栏&#xff1a;《JavaSE语法与底层详解》 文章分布&#xff1a;这是一篇关于接口的文章&#xff0c;在本篇文章中我会分享Cloneable接口的用法和机制&#xff0c;同时从底层分析深拷贝和浅拷贝的区别。 Cloneable接口 Cloneable这类接口可以说是…

ZigBee硬件平台小记(CC2530)

文章目录1.CC2530射频模块特征2.CC2530射频模块主要技术指标3.调试器输出接口特点输出引脚排列1.CC2530射频模块特征 基于CC2530F256单芯片ZigBee SOC&#xff08;System On Chip&#xff09;&#xff0c;集成8051内核模块尺寸&#xff1a;36mm x 26mmSMA底座&#xff0c;外接…

代码随想录训练营第56天|LeetCode 647. 回文子串、516.最长回文子序列

参考 代码随想录 题目一&#xff1a;LeetCode 647. 回文子串 如果用暴力求解&#xff0c;两层for循环加一层判断&#xff0c;两个遍历指针i和j构成一个区间&#xff0c;每次判断这个区间内的字符串是否为回文串&#xff0c;这样的求法时间复杂度为O(n^3)。这里使用动态规划可…

Jetpack Compose中的手势操作

点击事件 监听点击事件非常简单&#xff0c;使用 clickable 和 combinedClickable 修饰符即可满足需求&#xff1a; OptIn(ExperimentalFoundationApi::class) Composable fun ClickableExample() {Column{Box(Modifier.clickable { println("clickable") }.size(3…

异常检测原理及其在计算机视觉中的应用

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达这篇文章涵盖了三件事&#xff0c;首先什么是视觉角度的异常检测&#xff1f;用于异常检测的技术有哪些&#xff1f;它在哪里使用&#xff1f;异常检测是什么&#xff1f;…

微服务架构下的配置治理模式

微服务被滥用是不争的事实。被滥用的同时&#xff0c;很少人留意到它所带来的配置治理的问题。本文我们介绍两种常见的治理模式。基于common的配置治理模式当微服务数量多时&#xff0c;开发人员倾向于创建这样的配置文件&#xff1a;common-redis.jsoncommon-mysql.jsoncommon…

〖产品思维训练白宝书 - 产品思维认知篇③〗- 产品思维 VS 技术思维

大家好&#xff0c;我是 哈士奇 &#xff0c;一位工作了十年的"技术混子"&#xff0c; 致力于为开发者赋能的UP主, 目前正在运营着 TFS_CLUB社区。 &#x1f4ac; 人生格言&#xff1a;优于别人,并不高贵,真正的高贵应该是优于过去的自己。&#x1f4ac; &#x1f4e…

【Numpy基础知识】Broadcasting广播

Numpy广播 来源&#xff1a;Numpy官网&#xff1a;https://numpy.org/doc/stable/user/basics.html 广播描述了 NumPy 在算术运算期间如何处理具有不同形状的数组。根据某些约束&#xff0c;较小的数组将“广播”到较大的阵列&#xff0c;以便它们具有兼容的形状。 导包 impo…

【Python机器学习】条件随机场模型CRF及在中文分词中实战(附源码和数据集)

需要源码请点赞关注收藏后评论区留言私信~~~ 基本思想 假如有另一个标注序列&#xff08;代词 动词 名词 动词 动词&#xff09;&#xff0c;如何来评价哪个序列更合理呢&#xff1f; 条件随机场的做法是给两个序列“打分”&#xff0c;得分高的序列被认为是更合理的。既然要…

移动设备软件开发-Shape详解

Spape详解 1.自定义背景shape 1.1gradient 1.简介 定义渐变色&#xff0c;可以定义两色渐变和三色渐变&#xff0c;及渐变样式&#xff0c;它的属性有下面几个2.属性 angle&#xff0c;只对线性渐变是有效的放射性渐变必须指定放射性的半径&#xff0c;gradientRadiouscentetX和…

STM32的ST-link调试下载,各种调试接口硬件介绍

调试原理 STM32F-10X使用M3内核&#xff0c;该内核支持复杂的同i傲视操作&#xff0c;硬件调试模块允许在取指令&#xff08;指令单步运行&#xff09;或访问数据&#xff08;数据断电时&#xff09;使得内核停止。在内核停止时&#xff0c;内核状态都可被查询&#xff0c;完成…

JS基于编码方式实现加密解密文本

JS基于编码方式实现加密解密文本 严格来讲这是一种简单的编码方式&#xff1a;加密&#xff0c;将明文【注】转成编码。解密则是编码转码为明文本。 【注&#xff1a;明文是指没有加密的文字(或者字符串)&#xff0c;一般人都能看懂。】 下面源码用到 这个fromCharCode() 方…

年底了,感谢大家2022年的支持,虚竹哥送10本JAVA好书

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作者&#x1f3c6;&#xff0c;阿里云专家博主&#x1f3…

碧兴物联IPO过会:拟募资4亿 预计年净利降幅超10%

雷递网 雷建平 12月21日碧兴物联科技&#xff08;深圳&#xff09;股份有限公司&#xff08;简称&#xff1a;“碧兴物联”&#xff09;日前IPO过会&#xff0c;准备在科创板上市。碧兴物联计划募资4.13亿元。其中&#xff0c;1.92亿元用于智慧生态环境大数据服务项目&#xff…

【MySQL】JDBC编程重点知识汇总

文章目录1. JDBC (API):2. JDBC代码编写:2.1 创建数据源对象:2.2 建立连接:2.3 构造SQL语句:2.4 执行SQL:2.5 释放资源:1. JDBC (API): 前面学过很多的SQL, 实际开发中大多数的SQL都不是手敲的, 都是通过程序来执行的. 各种的数据库都会提供API方便编程语言来控制; API (Appli…

【JavaEE】网络初识

初识网络协议 OSI七层和TCP/IP五层&#xff08;四层&#xff09; 应用层 应用程序 代码实现 传输层 端到端传输 &#xff08;如玩家对玩家&#xff09; 操作系统内核实现 网络层 点到点传输 操作系统内核实现 数据链路层 相邻节点之间的传输 &#xff08;如集散点…

ChatGPT能接入微信了

前两天还看到不少人讨论&#xff0c;要是ChatGPT接入微信是啥感觉&#xff1f; 这不&#xff0c;想你所想&#xff0c;项目已经来了~ 来看效果&#xff0c;ChatGPT就出现在普通的微信对话框里&#xff0c;有问必答&#xff1a; 甚至还能拉入群聊&#xff0c;大家共用&#xf…

Adobe Premiere Pro 2020 系统兼容性报告:不支持的视频驱动程序

Adobe Premiere Pro 2020 系统兼容性报告&#xff1a;不支持的视频驱动程序 1. 问题 打开Adobe Premiere Pro 2020&#xff0c;看见系统兼容性报告&#xff1a;不支持的视频驱动程序。如下图&#xff1a; 点击修复&#xff0c;进入安装 Intel 图形驱动程序教程页面&#xff0…