Android数据绑定技术二,企业级开发
PS:上一篇文章写了Databinding的简单使用,写了一个绑定textview的示例,和绑定的一些用法,估计有的人会说,之前的写的好好的,为什么要数据绑定这样的写法呢,没办法,社会在进步,当然是怎么好怎么做了。这篇主要是内容是
- 数据绑定加载网络图片
- 数据绑定ListView
- 自定义适配器(数据绑定通用适配器)
- item中某一个控件点击事件并刷新定位
先说一下数据绑定加载网络图片和本地图片,图片的载体我们都知道用ImageView,但是如果直接把图片通过set的形式传给xml文件的话,数据绑定是无法直接解析到的,这就用到了自定义属性,我们来实现一下。这次的代码先按照之前的接着写。
1:先加载一个图片处理的库,Glide
compile "com.github.bumptech.glide:glide:3.7.0"
2:在user_layout.xml文件中用
注意:app:imageUrl,此处的imageUrl是工具类的注解,只要不过分,可以随便写,但要记住,我们下面还要用
<ImageView android:layout_width="100dp" android:layout_height="100dp" app:imageUrl="@{ user.icon }"/>
3:我们写一个工具类ImgUtils.java,专门来处理图片
@BindingAdapter({"imageUrl"})这里的imageUrl就是上面所写的app:imageUrl。这里的static可是有含义的,如果没有static的话,那就是必须用DatabindingComponent来创建对象并返回,不过加上static就可以了,这个有兴趣可以去搜一下。
public class ImgUtils { @BindingAdapter({"imageUrl"}) public static void loadImage(ImageView imageView,String imgUrl){ if(imgUrl == null){ imageView.setImageResource(R.mipmap.ic_launcher); } else{ Glide.with(imageView.getContext()).load(imgUrl).into(imageView); } } }
4:最后activity处理代码
DatabindingBinding databindingBinding=DataBindingUtil.setContentView(this, R.layout.databinding);
Users users=new Users();
users.setName("用户名:");
users.setAddress("地址:");
users.setEmail("E-mail:");
users.setIcon("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1513564670&di=0b7e42fd05b25e0511c016a1b6bdd7fc&src=http://pic29.photophoto.cn/20131211/0005018385835470_b.jpg");
users.setVip(true);
Users users1=new Users();
users1.setName("李四");
users1.setAddress("北京");
users1.setEmail("1245334@163.com");
// users1.setIcon();
users1.setVip(true);
List<Users> list=new ArrayList<>();
list.add(users);
list.add(users1);
databindingBinding.setUsers(list);
这样子就可以实现了,上面的代码和我上一篇写的有关,如果不看第一篇的话,可能会看不懂。
左边是网络加载,右边是本地加载
好,到这里,加载网络图片就已经做好了,下面我们实现一个数据绑定listview,还是老样子,搞清楚几个步骤
- 创建自定义Adapter(通用)
- 并且获取上面的某一个控件的点击事件,刷新定位
- 如何把通用适配器和listview关联,并且数据也能被加载。
1:创建 自定义CommonAdapter.java(通用)
我老是说通用,怎么才能通用呢,其实很简单,只要把layoutID,variableID等通过传参的形式传入即可。我们之前都会用到Viewholder,在这里就不需要了,关键还是viewDataBinding = DataBindingUtil.inflate(LayoutInflater.from(context), layoutId, parent, false);代码很少。也很简单。 return viewDataBinding.getRoot();中getRoot(),底层代码就是返回一个view。
package databindinglistview; import android.content.Context; import android.databinding.DataBindingUtil; import android.databinding.ViewDataBinding; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import java.util.List; /** * Created by cMusketeer on 17/12/18. * * @author cmusketeer */ public class CommonAdapter<T> extends BaseAdapter { public Context context; public List<T> list;//不确定传过来的值泛型 public int layoutId;//布局ID public int variableId;//变量ID public CommonAdapter(Context context, int layoutId, List<T> list, int variableId) { this.context = context; this.layoutId = layoutId; this.list = list; this.variableId = variableId; } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewDataBinding viewDataBinding = null; if (convertView == null){ viewDataBinding = DataBindingUtil.inflate(LayoutInflater.from(context), layoutId, parent, false); }else { viewDataBinding = DataBindingUtil.getBinding(convertView); } viewDataBinding.setVariable(variableId,list.get(position)); return viewDataBinding.getRoot(); } }
2:布局文件dblistview.xml
关键:
<data> <variable name="adapter1" type="android.widget.BaseAdapter"></variable> </data>
这个地方只能这样写,有的人到这一步可能会有疑问,为什么不是把CommonAdapter加载进来呢,别急,自定义Adapter是在Activity中被加载。
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" > <data> <variable name="adapter1" type="android.widget.BaseAdapter"></variable> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:layout_width="match_parent" android:layout_height="match_parent" app:adapter="@{adapter1 }" ></ListView> </LinearLayout> </layout>
写到这还要写一个item,毕竟listview的样式是不能少的嘛;这里才是真正赋值的地方,注意:databinding.Users,为路径,你我可能写的路径不一样。
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" > <data> <variable name="user" type="databinding.Users"></variable> </data> <LinearLayout android:layout_width="match_parent" android:orientation="horizontal" android:layout_height="match_parent"> <ImageView android:layout_width="100dp" android:layout_height="80dp" app:imageUrl="@{ user.icon }"/> <LinearLayout android:layout_width="match_parent" android:layout_height="80dp" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="50dp" android:textSize="25dp" android:onClick="@{user.click1}" android:text="@{user.name}"/> <TextView android:layout_width="match_parent" android:layout_height="30dp" android:textColor="#c4c3c3" android:text="@{user.address}"/> </LinearLayout> </LinearLayout> </layout>
Users.java
package databinding; import android.view.View; import android.widget.Toast; public class Users { public String name,address,email; private boolean vip; public String icon; public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public boolean isVip() { return vip; } public void setVip(boolean vip) { this.vip = vip; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void click1(View view){ Toast.makeText(view.getContext(), "点击了:"+name, Toast.LENGTH_SHORT).show(); } public boolean longClick1(View view){ Toast.makeText(view.getContext(), "长按了:"+name, Toast.LENGTH_SHORT).show(); return true; } }
ImgUtils上面写过了,翻一翻即可
3:ListviewActivity.java
这里面写一个值和加载适配器,值是模拟的,但图片是真的,是从网上下载的,需要联网。需要注意的是retrofit.cn.myretrofit.BR.user,这个就是variableID,在我们的控件生成一个R文件的时候,它也会生成一个BR文件,里面都是int,你懂的。
public class ListviewActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); DblistviewBinding dblistviewBinding=DataBindingUtil.setContentView(this, R.layout.dblistview); List<Users> list=new ArrayList<>(); for(int i=0;i<50;i++){ Users users=new Users(); users.setIcon("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1513564670&di=0b7e42fd05b25e0511c016a1b6bdd7fc&src=http://pic29.photophoto.cn/20131211/0005018385835470_b.jpg"); users.setName("图片 :"+i); users.setAddress("北京"); list.add(users); } CommonAdapter<Users> commonAdapter=new CommonAdapter<Users>(this,R.layout.item_listview,list, retrofit.cn.myretrofit.BR.user); dblistviewBinding.setAdapter1(commonAdapter); } }
这里的是给局部加的监听,所以点击(字)图片:0等才有反应,点击北京和image是没有反应的。
完:
4:item中某一个控件点击事件并刷新
1:我们在使用普通的listview处理方法的时候,点击item中某一个控件,写的逻辑很多,还要通过接口等刷新,而且如果处理不好的话刷新后一般还会跳到第一条数据。在这数据绑定统统只用几行代码搞定。
对于商品购买添加什么的非常好用
package databinding; import android.databinding.BaseObservable; import android.databinding.Bindable; import android.view.View; import android.widget.Toast; import retrofit.cn.myretrofit.BR; public class Users extends BaseObservable { public String name,address,email; private boolean vip; public String icon; public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public boolean isVip() { return vip; } public void setVip(boolean vip) { this.vip = vip; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } public void click1(View view){ Toast.makeText(view.getContext(), "点击了:"+name, Toast.LENGTH_SHORT).show(); } public boolean longClick1(View view){ Toast.makeText(view.getContext(), "长按了:"+name, Toast.LENGTH_SHORT).show(); return true; } public void click2(View view){ setName("我被点击了"); } }
这里代码我做了修改,extends BaseObservable 观察者(可以时刻观察某个东西的变化),可以看到 getName方法上加了一个注解,@Bindable,在setName中notifyPropertyChanged(BR.name);这里需要一个id只对name做个监听,所以只能点击name才有变化,而且点击后会只刷新单个item,也可以刷新整个,但在没必要的情况下刷新整个listview是好资源的。方法_all就是对全部进行刷新。
使用click2()方法和之前一样android:onClick="@{user.click2}"。