先上git地址https://github.com/mazwu110/libhttpapiMvvmDemo
1. 适配器基类
/**
* Created by mzw on 2019/7/25.
*/
public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<BindingViewHolder>
implements BaseViewHolder.OnNotifyChangeListener {
public static Context mContext;
public LayoutInflater mLayoutInflater;
public List<T> mRecordList = new ArrayList();
public static final int TYPE_CONTENT = 1;
public BaseRecyclerAdapter(Context context) {
this.mContext = context;
this.mLayoutInflater = LayoutInflater.from(context);
}
@Override
public BindingViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
return setViewHolder(viewGroup);
}
@Override
public void onBindViewHolder(BindingViewHolder holder, int position) {
bindData(holder, position);
}
@Override
public int getItemCount() {
return mRecordList.size();
}
//空布局类型返回1,正常布局类型返回0
@Override
public int getItemViewType(int position) {
return TYPE_CONTENT;
}
@Override
public void onNotify() {
//提供给BaseViewHolder方便刷新视图
notifyDataSetChanged();
}
//添加第一页数据
public void addRecordList(List<T> datas) {
mRecordList.clear();
if (null != datas) {
mRecordList.addAll(datas);
}
notifyDataSetChanged();
}
// 添加更多数据
public void addMoreRecordList(List<T> datas) {
if (null != datas) {
mRecordList.addAll(datas);
}
notifyDataSetChanged();
}
public void clearDatas() {
mRecordList.clear();
notifyDataSetChanged();
}
public T getItem(int position) {
if (mRecordList != null && position < mRecordList.size()) {
return mRecordList.get(position);
} else {
return null;
}
}
//删除单条数据
public void deletItem(T data) {
mRecordList.remove(data);
notifyDataSetChanged();
}
/**
* 子类重写实现自定义ViewHolder
*/
public abstract BindingViewHolder setViewHolder(ViewGroup parent);
//用给定的 data 对 holder 的 view 进行赋值,交给子类自己实现
public abstract void bindData(BindingViewHolder bindingViewHolder, int positon);
// 图片加载
@BindingAdapter({"app:imageUrl"})
public static void loadImageFromUrl(ImageView imageView, String url) {
if (url.startsWith("http") || url.startsWith("HTTP")) { // 网络图片
QApp.mImageLoader.displayNetImage(imageView, url);
} else { // base64图片
//QApp.mImageLoader.addBase64Image(url, imageView);
}
}
// 解决为图片赋值本地资源时 运行效果不见图片的问题
@BindingAdapter("android:src")
public static void setSrc(ImageView view, int resId) {
view.setImageResource(resId);
}
@BindingAdapter("android:textColor")
public static void setTextColor(TextView view, int resId) {
view.setTextColor(mContext.getResources().getColor(resId));
}
}
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;
}
}
超级基类,所谓超级基类即使用的时候其他适配器直接继承他即可
//SuperBaseAdapter 所有适配器基类
// add by mzw 2019-12-22
public abstract class SuperBaseAdapter<T> extends BaseRecyclerAdapter<T> {
private int layoutId;
public SuperBaseAdapter(Context context, int layoutId) {
super (context);
this.layoutId = layoutId;
}
@Override
public BindingViewHolder setViewHolder(ViewGroup viewGroup) {
ViewDataBinding binding = DataBindingUtil.inflate (mLayoutInflater, layoutId, viewGroup, false);
return new BindingViewHolder (binding);
}
@Override
public void bindData(BindingViewHolder bindingViewHolder, int positon) {
T item = mRecordList.get (positon);
ViewDataBinding binding = bindingViewHolder.getBinding ();
binding.setVariable (BR.item, item); // item布局中名称要和这个设置的一致,否则这个基类不能用,或者自己改item为布局中的名称,以下类似
binding.setVariable (BR.click, getClickListener (item, positon, binding)); // 设置点击事件,item布局中名称要和这个设置的一致,否则这个基类不能用
binding.executePendingBindings (); // 防止闪烁
}
//由子类实现点击事件,然后子类自己在布局中绑定即可
public abstract AdapterClickListener getClickListener(T item, int postion, ViewDataBinding binding);
}
点击超类:
public abstract class AdapterClickListener {
}
欺骗编译器布局item_my_base.xml,要不SuperBaseAdapter编译不会通过
布局内容
<?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>
<!-- 此布局用来欺骗SuperBaseAdapter,让他能编译,骗过编译器的功能,不能少-->
<variable
name="item"
type="com.httpapi.BaseResultEntity" />
<variable
name="click"
type="com.qlib.libadapter.AdapterClickListener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:orientation="vertical">
</LinearLayout>
</layout>
MainActivity
// 所谓的MVVM模式,M就是获取数据的一个独立类,比如做实际的网络请求等,
// V就是界面显示的一个类,说白了就是MainActivity类似的界面,VM其实就是
// 从MainActivity分离出来,然后用来处理数据罢了,注意如果在VM中引用到了Activity中的视图控件等,记得释放
// 这里使用谷歌的AAC框架,其实就是典型的MVVM框架来解析下如何使用
// 遇到任何问题,可以加Q一起探讨交流 QQ:315145320 或者发邮箱
public class MainActivity extends BaseActivity {
// 注意: 必须要在布局的根节点上增加<layout></layout>,要不下面这个家伙出不来
// private 多余的,默认就是,没必要写了,除了private外,其他访问权限需要写哦
ActivityMainBinding mBinding;
MainActivityVM VM;
WeatherAdapter mAdapter;
UserInfoAdapter tAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 加载界面
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
init();
}
// 其他初始化
void init() {
// 注意 mAdapter 顺序,有列表的时候才需要
mAdapter = new WeatherAdapter(this, R.layout.item_weather);
VM = new MainActivityVM(this, mAdapter); // 除了这一句外,其他可以移到MainActivityVM中,以减少Activity的臃肿,这里就不移了,测试代码而已
// 注意 lambda表达式不支持Java8以下的,所以JDK必须是8及以上的哦,要不会报错
VM.getTestData().observe(this, data -> mBinding.setUserName(data));
// 设置监听
mBinding.setQClickListener(VM.getQClickListener());
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mBinding.recyclerView.setLayoutManager(layoutManager); // 设置recyclerview布局方式
mBinding.recyclerView.setAdapter(mAdapter); //给recyclerview 添加适配器
// 这个是超级封装的适配器
tAdapter = new UserInfoAdapter (this, R.layout.item_my_weather);
LinearLayoutManager layoutManagerT = new LinearLayoutManager(this);
mBinding.recyclerViewTest.setLayoutManager(layoutManagerT); // 设置recyclerview布局方式
mBinding.recyclerViewTest.setAdapter(tAdapter); //给recyclerview 添加适配器
List<QTestBean> list = new ArrayList<> ();
for (int i = 0; i < 10; i++) {
QTestBean bean = new QTestBean();
bean.setId ("" + i);
bean.setName ("张三" + i);
bean.setAge ("" + (20 + i));
list.add (bean);
}
tAdapter.addRecordList (list);
}
@Override
public void onDestroy() {
super.onDestroy();
VM.freeObject();
}
}
MainActivity 辅助类 MainActivityVM
// 所有的数据获取我统一用了泛型,大家可以看下我对MVVM中MEDEL的封装,及里面对
//retrofit2 rxjava2, ApiService 的封装。可以根据自身情况进行修改,但是这里只需要一个泛型
//model就行了,避免创建太多的model 代码文件太多不好维护,但是VM是需要多个的,
// 就像每一个界面需要一个Activity一样,有问题发我邮箱或者加Q吧 QQ:315145320
public class MainActivityVM extends BaseViewModel implements OnHttpApiListener {
QClickListener qClickListener;
WeatherAdapter mAdapter;
int index = 0;
// 动态数据绑定
MutableLiveData<TestDataBean> testData;
public MutableLiveData<TestDataBean> getTestData() {
return testData;
}
// 还可以在构造函数中增加你乐意的参数或者控件传过来
public MainActivityVM(Context context, WeatherAdapter adapter) {
this.mContext = context;
this.mAdapter = adapter;
qClickListener = new QClickListener();
testData = new MutableLiveData<>(); // 记得初始化,要不会报错哦
}
// 返回给Activity绑定点击事件
public QClickListener getQClickListener() {
return qClickListener;
}
public class QClickListener {
// key-value get 获取后台数据
public void doGet(View view) {
Map<String, Object> params = new HashMap<>();
params.put("city", "北京");
params.put("key", "0132423b3e085efed24b7b8f00d83a91");
// 第三个参数,需要用到哪个类解析数据结果就传哪个类进去就行,这里采用了泛型解析
QHttpApi.doGet(Constants.getWeather, params, WeatherBean.class, HttpWhatConfig.CODE_10, MainActivityVM.this);
// QHttpApi.doStrGet(Constants.getWeather, params, HttpWhatConfig.CODE_10, MainActivityVM.this);
}
// key-value post获取后台数据
public void doPost(View view) {
// 另外有QHttpApi.doStrGet方法可用,此返回是后台返回什么,解析出就直接返回给您,需要您自己接受了解析,包括CODE等都返回来了
Map<String, Object> params = new HashMap<>();
params.put("city", "上海");
params.put("key", "0132423b3e085efed24b7b8f00d83a91");
// 第三个参数,需要用到哪个类解析数据结果就传哪个类进去就行,这里采用了泛型解析
//
QHttpApi.doPost(Constants.getWeather, params, WeatherBean.class, HttpWhatConfig.CODE_11, MainActivityVM.this);
}
//post json格式的参数获取后台数据
public void doJsonPost(View view) {
}
// 设置数据,可以通过网络获取
public void getUserName(View view) {
TestDataBean bean = new TestDataBean();
index += 1;
bean.setUsreName("张三" + index);
// 更新数据,界面上也会同步更新
testData.setValue(bean);
}
}
@Override
public void onSuccess(int what, Object data) {
switch (what) {
case HttpWhatConfig.CODE_10: {
// 使用请求数据的时候的class反解析就行
WeatherBean bean = (WeatherBean) data;
ArrayList<FutureBean> list = bean.getFuture();
// 清除数据,然后刷新, 如果您做的是分页,可以使用 mAdapter.addMoreRecordList(list);
mAdapter.addRecordList(list);
break;
}
// 数据格式一样 就拷贝上面的解析了
case HttpWhatConfig.CODE_11:
// 使用请求数据的时候的class反解析就行
WeatherBean bean = (WeatherBean) data;
ArrayList<FutureBean> list = bean.getFuture();
// 清除数据,然后刷新, 如果您做的是分页,可以使用 mAdapter.addMoreRecordList(list);
mAdapter.addRecordList(list);
break;
}
}
@Override
public void onFailure(int what, String msg, int code) {
showToast(msg);
}
public void freeObject() {
if (qClickListener != null)
qClickListener = null;
if (mAdapter != null)
mAdapter = null;
}
}
TestDataBean
// 测试用的单个数据绑定
public class TestDataBean extends BaseObservable implements Serializable {
String usreName;
public String getUsreName() {
return usreName;
}
public void setUsreName(String usreName) {
this.usreName = usreName;
}
}
WeatherBean
// 所有的字段必须要和后台返回的一致,包括后台返回的字段是对象的,名字也必须要统一,要不GSON解析不了
public class WeatherBean extends BaseResultEntity<WeatherBean> {
String city;// 字段必须要和后台返回的一致,要不GSON解析不了
RealtimeBean realtime; // 字段必须要和后台返回的一致,要不GSON解析不了
ArrayList<FutureBean> future;// 字段必须要和后台返回的一致,要不GSON解析不了
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public RealtimeBean getRealtime() {
return realtime;
}
public void setRealtime(RealtimeBean realtime) {
this.realtime = realtime;
}
public ArrayList<FutureBean> getFuture() {
return future;
}
public void setFuture(ArrayList<FutureBean> future) {
this.future = future;
}
}
// 基础数据解析类
public class RealtimeBean extends BaseObservable implements Serializable {
String temperature;
String humidity;
String info;
String wid;
String direct;
String power;
String aqi;
public String getTemperature() {
return temperature;
}
public void setTemperature(String temperature) {
this.temperature = temperature;
}
public String getHumidity() {
return humidity;
}
public void setHumidity(String humidity) {
this.humidity = humidity;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String getWid() {
return wid;
}
public void setWid(String wid) {
this.wid = wid;
}
public String getDirect() {
return direct;
}
public void setDirect(String direct) {
this.direct = direct;
}
public String getPower() {
return power;
}
public void setPower(String power) {
this.power = power;
}
public String getAqi() {
return aqi;
}
public void setAqi(String aqi) {
this.aqi = aqi;
}
}
FutureBean
public class FutureBean extends BaseObservable implements Serializable {
String date;
String temperature;
String weather;
WidBean wid;
String direct;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getTemperature() {
return temperature;
}
public void setTemperature(String temperature) {
this.temperature = temperature;
}
public String getWeather() {
return weather;
}
public void setWeather(String weather) {
this.weather = weather;
}
public WidBean getWid() {
return wid;
}
public void setWid(WidBean wid) {
this.wid = wid;
}
public String getDirect() {
return direct;
}
public void setDirect(String direct) {
this.direct = direct;
}
}
主布局文件
<?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:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<!-- 可以导入任意包,然后调用 -->
<import type="com.libhttpapimvvmdemo.R" />
<variable
name="userName"
type="com.libhttpapimvvmdemo.javaBean.TestDataBean" />
<variable
name="qClickListener"
type="com.libhttpapimvvmdemo.viewModel.MainActivityVM.QClickListener" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<!--习惯用约束布局,减少嵌套,提高界面绘制性能 -->
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.MainActivity">
<EditText
android:id="@+id/edt_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="动态绑定内容展示"
android:text="@{userName.usreName}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_left"
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:onClick="@{qClickListener.doGet}"
android:text="get请求"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/btn_middle"
app:layout_constraintTop_toBottomOf="@id/edt_top" />
<Button
android:id="@+id/btn_middle"
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:onClick="@{qClickListener.doPost}"
android:text="post请求"
app:layout_constraintLeft_toRightOf="@+id/btn_left"
app:layout_constraintRight_toLeftOf="@+id/btn_right"
app:layout_constraintTop_toBottomOf="@id/edt_top" />
<Button
android:id="@+id/btn_right"
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:onClick="@{qClickListener.getUserName}"
android:text="获取用户明"
app:layout_constraintLeft_toRightOf="@+id/btn_middle"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edt_top" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_marginTop="224dp"
android:background="@color/white"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_right" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerViewTest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_marginTop="224dp"
android:background="@color/white"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/recyclerView" />
</android.support.constraint.ConstraintLayout>
</ScrollView>
</layout>
UserInfoAdapter
public class UserInfoAdapter extends SuperBaseAdapter<QTestBean> {
public UserInfoAdapter(Context context, int layoutId) {
super (context, layoutId);
}
// 不需要点击事件的直接重载,然后放null即可
@Override
public AdapterClickListener getClickListener(QTestBean item, int postion, ViewDataBinding binding) {
// 每个item都有一个点击对象
return new QClickListener(item, postion, binding);
}
// 点击事件逻辑处理
public class QClickListener extends AdapterClickListener {
private QTestBean item;
private int postion;
private ItemMyWeatherBinding binding; // 可以直接使用binding.获取要控制的组件,方便灵活,不用亦不影响
public QClickListener(QTestBean item, int postion, ViewDataBinding binding) {
this.item = item;
this.postion = postion;
this.binding = (ItemMyWeatherBinding) binding;
}
public void showToast(View v){
Toast.makeText (mContext, item.getName () + ";" + binding.btnTest.getText().toString (), Toast.LENGTH_LONG).show ();
}
}
}
WeatherAdapter
// SuperBaseAdapter 是我自己封装的,大家可以任意改,SuperBaseAdapter
// 使用处,只需要new WeatherAdapter,传入相应的参数即可使用,
// 如果还要实现点击事件,是要重写getClickListener,然后在其内部定义相应的方法,然后绑定到item布局即可,
// 点击事件具体可参考UserInfoAdapter中的,已实现
public class WeatherAdapter extends SuperBaseAdapter<FutureBean> {
public WeatherAdapter(Context context, int layoutId) {
super (context, layoutId);
}
// 没有点击事件,不用实现下面此方法
@Override
public AdapterClickListener getClickListener(FutureBean item, int postion, ViewDataBinding binding) {
return null;
}
}
使用方式:自定义一个
适配器,名字随意,然后继承SuperBaseAdapter 即可,如
public class WeatherAdapter extends SuperBaseAdapter<FutureBean> {
}
然后在使用中:
// 这个是超级封装的适配器 R.layout.item_my_weather 是适配器的布局文件
UserInfoAdapter tAdapter = new UserInfoAdapter (this, R.layout.item_my_weather);
LinearLayoutManager layoutManagerT = new LinearLayoutManager(this);
mBinding.recyclerViewTest.setLayoutManager(layoutManagerT); // 设置recyclerview布局方式
mBinding.recyclerViewTest.setAdapter(tAdapter); //给recyclerview 添加适配器
即可绑定了相关数据,都是算半个泛型绑定,这里所有的网络通信部分我git中也有封装,全是泛型解析数据。
特别注意,凡是所有继承SuperBaseAdapter的适配器,布局中绑定的数据选项名称必须是这两个,否则数据会绑定不上,第一个是数据对应的JavaBean名称item,另一个是点击事件对应的名称click,切记名字不能错,错了就永远绑定不了数据了。