MVVM+DataBinding+RxJava+Retofit实现一个简单的新闻demo
引入databinding
dataBinding {
enabled true
}
引入相关依赖
//ViewModel与LiveData
implementation "android.arch.lifecycle:extensions:1.1.1"
// 依赖RxAndroid 2X 的依赖库
// 增加RxJava 2X 的依赖库
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
// Retrofit库
implementation 'com.squareup.retrofit2:retrofit:2.0.2'
// Okhttp库
implementation 'com.squareup.okhttp3:okhttp:3.1.2'
//Gson依赖
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
//图片加载
implementation 'com.github.bumptech.glide:glide:4.11.0'
加入网络请求权限
<uses-permission android:name="android.permission.INTERNET"/>
代码
MainActivity
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private MainViewModel viewModel;
private NewsRecyclerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
init();
initData();
}
//调整每个item之间的距离
class SpaceItemDecoration extends RecyclerView.ItemDecoration {
int mSpace;
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.bottom = mSpace;
if (parent.getChildAdapterPosition(view) == 0) {
outRect.top = mSpace;
}
}
public SpaceItemDecoration(int space) {
this.mSpace = space;
}
}
private void init() {
//初始化ViewModel
viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
viewModel.request();
Log.i("sjh","我是你爸爸");
}
private void initData() {
binding.setModel(viewModel);
//数据请求
viewModel.getNewsList().observe(this, new Observer<List<News.NewslistBean>>() {
@Override
public void onChanged(List<News.NewslistBean> newslistBeans) {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(binding.getRoot().getContext());
binding.recyclerView.setLayoutManager(linearLayoutManager);
adapter = new NewsRecyclerAdapter(binding.getRoot().getContext());
binding.recyclerView.addItemDecoration(new SpaceItemDecoration(5));
binding.recyclerView.setAdapter(adapter);
adapter.addAll(newslistBeans);
}
});
}
}
ViewModel类,与view层交互,请求网络获取数据
public class MainViewModel extends ViewModel {
//当数据请求成功回调
protected MutableLiveData<List<News.NewslistBean>> newsList=new MutableLiveData<>();
public void request() {
//RxJava切换线程
Observable.create(new ObservableOnSubscribe<News>() {
@Override
public void subscribe(@NonNull final ObservableEmitter<News> emitter) throws Exception {
// Retrofit进行网络请求
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.tianapi.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
Call<News> call = request.getCall();
call.enqueue(new Callback<News>() {
@Override
public void onResponse(Call<News> call, Response<News> response) {
News news = response.body();
emitter.onNext(news); //将news通过发射器发送给Observer
}
@Override
public void onFailure(Call<News> call, Throwable t) {
System.out.println("连接失败");
System.out.println(t.getMessage());
}
});
}
})
.subscribeOn(Schedulers.io()) // 为上面代码分配io线程
.observeOn(AndroidSchedulers.mainThread()) // 为下面代码分配主线程
.subscribe(new Observer<News>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull News news) {
if (news != null) {
newsList.postValue(news.getNewslist());
}
}
@Override
public void one rror(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
}
public MutableLiveData<List<News.NewslistBean>> getNewsList() {
return newsList;
}
}
RecyclerView的适配器类
public class NewsRecyclerAdapter extends RecyclerView.Adapter<BindingViewHolder> {
private LayoutInflater layoutInflater;
private OnItemClickListener mListener; //item的点击监听器
private List<News.NewslistBean> list;
public interface OnItemClickListener {
void onItemClickListener(News.NewslistBean news);
}
public NewsRecyclerAdapter(Context context) {
this.layoutInflater = LayoutInflater.from(context);
this.list = new ArrayList<>();
}
@NonNull
@Override
public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.item_news_fragment,parent,false);
return new BindingViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull BindingViewHolder holder, int position) {
final News.NewslistBean news = list.get(position);
ViewDataBinding binding = holder.getBinding();
binding.setVariable(BR.news,news);
binding.executePendingBindings();
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onItemClickListener(news);
}
}
});
}
@Override
public int getItemCount() {
return list.size();
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.mListener = listener;
}
public void addAll(List<News.NewslistBean> news) {
list.addAll(news);
}
}
加载网络图片双向绑定ImageView的工具类
public class DataBindingUtil {
@BindingAdapter("BaseUrl")
public static void loadImage(ImageView view, String url){
Glide.with(view.getContext()).load(url).into(view);
}
}
网络请求接口
public interface GetRequest_Interface {
@GET("social/index?key=d44ec13840090282a1af867dec47bbbd&num=50")
Call<News> getCall();
// 注解里传入 网络请求 的部分URL地址
// Retrofit把网络请求的URL分成了两部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里
// 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略
// getCall()是接受网络请求数据的方法
}
顶层BindingViewHolder类
public class BindingViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
private T mBinding;
public BindingViewHolder(@NonNull T binding) {
super(binding.getRoot());
mBinding = binding;
}
public T getBinding() {
return mBinding;
}
}
新闻实体类
/**
* 新闻实体类
* */
public class News extends BaseObservable {
/**
* code : 200
* msg : success
* newslist : [{"id":"2f5ef5351c8139baecf39b97133db3d3","ctime":"2021-03-13 09:45","title":"这段东北女法官庭审视频火出圈了 也就看了100来遍","description":"","source":"中华社会","picUrl":"https://img0.utuku.imgcdc.com/300x200/news/20210313/9a6cc7ae-37f5-4bba-8e0e-fcd23e1eac54.jpg","url":"https://news.china.com/social/1007/20210313/39375399.html"}]
*/
private Integer code;
private String msg;
private List<NewslistBean> newslist;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public List<NewslistBean> getNewslist() {
return newslist;
}
public void setNewslist(List<NewslistBean> newslist) {
this.newslist = newslist;
}
public class NewslistBean extends BaseObservable{
/**
* id : 2f5ef5351c8139baecf39b97133db3d3
* ctime : 2021-03-13 09:45
* title : 这段东北女法官庭审视频火出圈了 也就看了100来遍
* description :
* source : 中华社会
* picUrl : https://img0.utuku.imgcdc.com/300x200/news/20210313/9a6cc7ae-37f5-4bba-8e0e-fcd23e1eac54.jpg
* url : https://news.china.com/social/1007/20210313/39375399.html
*/
private String id;
private String ctime;
private String title;
private String description;
private String source;
private String picUrl;
private String url;
@Bindable
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
notifyPropertyChanged(BR.id);
}
@Bindable
public String getCtime() {
return ctime;
}
public void setCtime(String ctime) {
this.ctime = ctime;
notifyPropertyChanged(BR.ctime);
}
@Bindable
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
notifyPropertyChanged(BR.title);
}
@Bindable
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
notifyPropertyChanged(BR.description);
}
@Bindable
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
notifyPropertyChanged(BR.source);
}
@Bindable
public String getPicUrl() {
return picUrl;
}
public void setPicUrl(String picUrl) {
this.picUrl = picUrl;
notifyPropertyChanged(BR.picUrl);
}
@Bindable
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
notifyPropertyChanged(BR.url);
}
}
}
activity_main.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>
<variable
name="model"
type="com.example.retrofit.MainViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</layout>
item_news_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="news"
type="com.example.retrofit.News.NewslistBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="12dp"
android:background="#FFF">
<ImageView
android:id="@+id/imageView13"
android:layout_width="70dp"
android:layout_height="70dp"
android:scaleType="centerCrop"
app:BaseUrl="@{news.picUrl}"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/textView13"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@{news.title}"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#000"/>
<TextView
android:id="@+id/textView15"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:maxLines="2"
android:text="@{news.ctime}"
android:textSize="12sp"/>
</LinearLayout>
</LinearLayout>
</layout>