Android jetpack(ViewModel,LiveData,DataBinding)

筆記,記錄一下ViewModel,LiveData,DataBinding的使用小案例。

利用ViewMode點擊一個按鈕把數(shù)字加一

image.png
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".jetpack.viewmodel.ViewModelActivity">
    <RelativeLayout
        android:id="@+id/rl_top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/tv_number"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:textSize="24sp"
            android:text="0"/>
        <Button
            android:id="@+id/bt_plus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+"
            android:layout_centerHorizontal="true"
            android:layout_below="@id/tv_number"/>
    </RelativeLayout>
</RelativeLayout>

然后在activity中編寫

public class ViewModelActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView mTvNumber;
    private Button mBtPlus;
    private MyViewModel myViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model);
        mTvNumber = findViewById(R.id.tv_number);
        myViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
        mTvNumber.setText(myViewModel.number+"");
        mBtPlus = findViewById(R.id.bt_plus);
        mBtPlus.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if(id == R.id.bt_plus){
            mTvNumber.setText((++myViewModel.number)+"");
        }
    }
}

我們的數(shù)據(jù)也就存入到了MyViewModel中,代碼如下:

public class MyViewModel extends ViewModel {
    public int number;
}

利用ViewModel 存儲的數(shù)據(jù),在手機(jī)發(fā)生旋轉(zhuǎn)的時候不會被重新還原,我們都知道,如果沒有在清單文件設(shè)置activity的android:configChanges是會導(dǎo)致重新走生命周期的,數(shù)據(jù)也就會還原了。

android:configChanges="keyboardHidden|orientation|screenSize"

ViewModel+LiveData使兩個fragment數(shù)據(jù)共享

image.png
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".jetpack.livedata.LiveDataActivity">
    <fragment
        android:id="@+id/first"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:name="com.example.myapplication.jetpack.livedata.FirstFragment"/>
    <fragment
        android:id="@+id/second"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:name="com.example.myapplication.jetpack.livedata.SecondFragment"/>
</LinearLayout>

布局很簡單,上下一分為二設(shè)置兩個fragment
然后準(zhǔn)備我們的activity

public class LiveDataActivity extends AppCompatActivity {

    private MyViewModel myViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_data);

    }

    public MyViewModel getViewModel(){
        if(myViewModel == null){
            myViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
        }
        return myViewModel;
    }
}

ok,可以寫我們的fragment的界面了,因為兩個fragment界面代碼和邏輯代碼都一樣,所以只貼一份出來。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".jetpack.livedata.FirstFragment">

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:min="0"
        android:max="100"/>

</FrameLayout>
public class FirstFragment extends Fragment {

    private SeekBar seekBar;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_first, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        seekBar = view.findViewById(R.id.seekbar);
        final MyViewModel myViewModel = ((LiveDataActivity)getActivity()).getViewModel();
        myViewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                Log.d("yanjin","progress = integer "+integer);
                seekBar.setProgress(integer);
            }
        });
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                myViewModel.getProgress().setValue(progress);
                Log.d("yanjin","progress = "+progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }
}

ok,這就能實現(xiàn)一邊拖動seekbar另外一邊的seekbar聯(lián)動的效果了。
注意一點哦,ViewModel的setValue是在主線程執(zhí)行,如果需要在子線程更新數(shù)據(jù),得用postValue。

DataBinding的使用

一個簡單的案例了解一下DataBinding


image.png

我們看布局

<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="userBean"
           type="com.example.myapplication.jetpack.databinding.UserBean" />
       <variable
           name="onClick"
           type="com.example.myapplication.jetpack.databinding.DataBindingActivity" />
   </data>

   <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       tools:context=".jetpack.databinding.DataBindingActivity">
       <ImageView
           android:id="@+id/iv_head"
           android:layout_width="100dp"
           android:layout_height="100dp"
           app:image="@{userBean.head}"
           android:onClick="@{onClick::onClickView}"
           android:layout_centerHorizontal="true"
           android:layout_marginTop="50dp"
           android:src="@drawable/ic_launcher_background"/>
       <TextView
           android:id="@+id/tv_age"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:textSize="14sp"
           android:layout_centerHorizontal="true"
           android:layout_below="@id/iv_head"
           android:layout_marginTop="80dp"
           android:text="@{String.valueOf(userBean.age)}"
           tools:text="年齡"/>
       <include layout="@layout/user_age"
           app:userBean="@{userBean}"/>
   </RelativeLayout>
</layout>

然后include里面的代碼如下

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="userBean"
            type="com.example.myapplication.jetpack.databinding.UserBean" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:tools="http://schemas.android.com/tools">
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="14sp"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="180dp"
            android:text="@{userBean.userName}"
            tools:text="姓名"/>
    </RelativeLayout>
</layout>

那么怎么把普通的布局轉(zhuǎn)成DataBinding專用的呢?


image.png

首先在gradle中加入

        dataBinding{
            enabled = true
        }

然后選中普通布局的最外成layout,alt+回車就能選擇切換成databinding的布局了


image.png

在代碼中

<variable
            name="userBean"
            type="com.example.myapplication.jetpack.databinding.UserBean" />

是將數(shù)據(jù)源傳入到布局中。然后綁定數(shù)據(jù),比如簡單的text數(shù)據(jù)綁定

android:text="@{String.valueOf(userBean.age)}"

如果要綁定網(wǎng)絡(luò)圖片怎么辦?那就要用到BindingAdapter注解了,我們寫一個工具類,以后想要綁定網(wǎng)絡(luò)圖片都可以用這個工具類

public class ImageBindingAdapter {
    @BindingAdapter("image")
    public static void setImage(ImageView imageView,String path){
        RequestOptions options = new RequestOptions();
        options.placeholder(R.drawable.ic_launcher_background);
        options.error(R.drawable.ic_launcher_background);
        Glide.with(imageView.getContext()).load(path).apply(options).into(imageView);
    }
}

然后在布局的imageview中使用

app:image="@{userBean.head}"

這里為啥用image呢?因為要和BindingAdapter注解中的參數(shù)字段統(tǒng)一。
關(guān)于點擊事件的綁定,網(wǎng)絡(luò)上的解法五花八門,這里就介紹一種,直接將要需要處理點擊事件的類傳進(jìn)來,寫上方法即可

<variable
            name="onClick"
            type="com.example.myapplication.jetpack.databinding.DataBindingActivity" />

這里我們傳入activity了,那么在布局中添加點擊事件

android:onClick="@{onClick::onClickView}"

最后傳入的DataBindingActivity實現(xiàn)onClickView方法,整體activity代碼如下:

public class DataBindingActivity extends AppCompatActivity {

    private UserBean userBean;
    private ActivityDataBindingBinding mViewDataBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mViewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding);
        initData();
        mViewDataBinding.setUserBean(userBean);
        mViewDataBinding.setOnClick(this);
    }

    private void initData() {
        userBean = new UserBean("張山",18,"https://www.baidu.com/img/flexible/logo/pc/result.png");
    }

    public void onClickView(View view){
        if(view.getId() == R.id.iv_head){
            Toast.makeText(view.getContext(),"哪里不會點哪里",Toast.LENGTH_LONG).show();
        }
    }
}

記住,布局里面需要的數(shù)據(jù)或者事件類,都要傳入。

mViewDataBinding.setUserBean(userBean);
mViewDataBinding.setOnClick(this);

onClickView這個方法的話,用法就和平時實現(xiàn)view的onClick方法一樣了,可以根據(jù)view的id判斷,并做出相應(yīng)處理。
當(dāng)然,除了variable導(dǎo)入類和數(shù)據(jù),還有import也可以不如我們在布局中導(dǎo)入一個工具類

<import type="com.example.l.gjj_databinding_demo.MyStringUtils"/>

然后在布局中就能使用了

android:text="@{MyStringUtils.capitalize(user.firstName)}"

還有,如果include中的布局也需要傳入數(shù)據(jù)的話,就用

app:userBean="@{userBean}"

app:userBean這個和上面variable中的定義要統(tǒng)一。

RecycleView中使用DataBinding

老樣子,布局登場

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

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".jetpack.databinding.recycle.DataBindingRecycleViewActivity">
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycle_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </RelativeLayout>
</layout>

activty代碼入場

public class DataBindingRecycleViewActivity extends AppCompatActivity {

    private ActivityDataBindingRecycleViewBinding mViewBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mViewBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding_recycle_view);
        mViewBinding.recycleView.setLayoutManager(new LinearLayoutManager(this));
        MyAdapter myAdapter = new MyAdapter(initData());
        mViewBinding.recycleView.setAdapter(myAdapter);
    }

    private List<String> initData() {
        List<String> list = new ArrayList<>();
        for(int x = 0;x<100;x++){
            list.add(x+"");
        }
        return list;
    }
}

重中之重的adapter代碼

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private List<String> list;
    public MyAdapter(List<String> list){
        this.list = list;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_item_layout, parent, false);
        TestItemLayoutBinding bind = DataBindingUtil.bind(view);
        return new ViewHolder(bind);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.binding.setNumber(list.get(position));
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder{
        TestItemLayoutBinding binding;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
        }

        public ViewHolder(TestItemLayoutBinding bind) {
            super(bind.getRoot());
            binding = bind;
        }
    }
}

有木有發(fā)現(xiàn),我們不用再用itemView去findViewById了真好
最后的條目布局登場

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="number"
            type="String" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="45dp">

        <TextView
            android:id="@+id/tv_number"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="@{number}"
            android:gravity="center" />
    </RelativeLayout>
</layout>

ViewModel+LiveData+DataBinding案例

來個綜合點的案例,有兩個分?jǐn)?shù),一個是A隊分?jǐn)?shù),一個是B隊分?jǐn)?shù),點擊+3,分別為A或B隊加3分,重置是把分?jǐn)?shù)清零,撤銷上一步操作是返回上一次分?jǐn)?shù)。


image.png

直接上代碼?。?!

<?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>
        <variable
            name="viewModel"
            type="com.example.myapplication.jetpack.databinding.dlv.MyViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        tools:context=".jetpack.databinding.dlv.DLVActivity">
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent">
            <TextView
                android:id="@+id/tv_number1"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:text="@{String.valueOf(viewModel.teamAScore)}"
                android:textSize="18sp"
                tools:text="0"
                android:textColor="@android:color/white"
                android:layout_centerHorizontal="true"
                android:gravity="center"
                android:background="@color/colorPrimary"/>
            <Button
                android:id="@+id/btn_plus1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="+3"
                android:onClick="@{viewModel.addTeamScore}"
                android:layout_below="@id/tv_number1"
                android:textSize="14sp"/>
            <Button
                android:id="@+id/btn_reset_last"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="撤銷上一步操作"
                android:onClick="@{viewModel.resetLast}"
                android:layout_below="@id/btn_plus1"/>
        </RelativeLayout>
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent">
            <TextView
                android:id="@+id/tv_number2"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:text="@{String.valueOf(viewModel.teamBScore)}"
                android:textSize="18sp"
                tools:text="0"
                android:textColor="@android:color/white"
                android:layout_centerHorizontal="true"
                android:background="@color/colorPrimaryDark"
                android:gravity="center"/>
            <Button
                android:id="@+id/btn_plus2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="+3"
                android:onClick="@{viewModel.addTeamScore}"
                android:layout_below="@id/tv_number2"
                android:textSize="14sp"/>
            <Button
                android:id="@+id/btn_reset"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="重置"
                android:onClick="@{viewModel.reset}"
                android:layout_below="@id/btn_plus2"/>
        </RelativeLayout>
    </LinearLayout>
</layout>
public class DLVActivity extends AppCompatActivity {

    private ActivityDLVBinding mBinding;
    private MyViewModel mViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_d_l_v);
        mViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
        mBinding.setViewModel(mViewModel);
        mViewModel.getTeamAScore().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                mBinding.setViewModel(mViewModel);
            }
        });
        mViewModel.getTeamBScore().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                mBinding.setViewModel(mViewModel);
            }
        });
    }
}
public class MyViewModel extends ViewModel {
    private MutableLiveData<Integer> teamAScore;

    public MutableLiveData<Integer> getTeamAScore() {
        if(teamAScore == null){
            teamAScore = new MutableLiveData<>();
            teamAScore.setValue(0);
        }
        return teamAScore;
    }
    private MutableLiveData<Integer> teamBScore;

    public MutableLiveData<Integer> getTeamBScore() {
        if(teamBScore == null){
            teamBScore = new MutableLiveData<>();
            teamBScore.setValue(0);
        }
        return teamBScore;
    }

    private int lastATeamScore = 0;
    private int lastBTeamScore = 0;

    public void addTeamScore(View view){
        int id = view.getId();
        if(id == R.id.btn_plus1){
            Integer value = teamAScore.getValue();
            lastATeamScore = value;
            value = value + 3;
            teamAScore.setValue(value);
        }else if(id == R.id.btn_plus2){
            Integer value = teamBScore.getValue();
            lastBTeamScore = value;
            value = value + 3;
            teamBScore.setValue(value);
        }
    }

    public void reset(View view){
        int id = view.getId();
        if(id == R.id.btn_reset){
            teamAScore.setValue(0);
            teamBScore.setValue(0);
        }
    }

    public void resetLast(View view){
        int id = view.getId();
        if(id == R.id.btn_reset_last){
            teamAScore.setValue(lastATeamScore);
            teamBScore.setValue(lastBTeamScore);
        }
    }
}

有木有發(fā)現(xiàn),現(xiàn)在的Activity變得特別的清晰明了了,哈哈哈哈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容