博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android数据绑定框架介绍
阅读量:6279 次
发布时间:2019-06-22

本文共 12064 字,大约阅读时间需要 40 分钟。

github地址

背景

数据绑定框架有很多,其实我就看过谷歌官方的数据绑定框架,官方的框架用起来的时候,觉得不是很顺手,侵入性还比较强。而且也一直纠结彷徨,从心底里质疑数据绑定框架的价值,到底给我们开发带来了什么,实用吗,可维护吗?

某一天的早晨突然灵光一现,决定自己去试试开发一个自己喜欢的数据绑定框架,经过没日没夜的艰苦风斗(当然是开玩笑的),中间反复修改设计,最后尘埃落定。虽然这个框架已经出世,也在生产环境试运行了一些页面,但是我对于数据绑定框架的价值,始终抱有质疑的态度,所以今天我将其取名sword,希望他像一把双刃剑,能够有一个面给开发者带来一些小小的便利。

价值

数据绑定的价值,我的理解就是一句话:就是通过操作一个数据对象,达到修改视图的目的,或者反过来,通过操作视图对象,达到修改数据对象的目的。

Screen_Shot_2016_01_11_at_9_37_37_PM

提示:在大多数的情况下,通常都是数据单向操作视图的,极少的情况下需要数据和视图双向绑定,互相操作。

Screen_Shot_2016_01_11_at_9_38_54_PM

模型分析

textView.setText(CharSequence data);data = textView.getText();

第一行代码代表值到视图的映射,第二行代码代表视图到值的映射。

值到视图的绑定

Screen_Shot_2016_01_12_at_9_20_46_AM

视图到值的绑定

Screen_Shot_2016_01_12_at_10_46_50_AM

sword的使用方法

sword的用法分为单向绑定(常用的),双向绑定2种。

单向绑定

新建data

class Data implements BindImpl {    @Bind(viewId = R.id.end_title,setMethod = B.setText)    public String end_title = "1234";    public String end_desc = "aaa";    @Bind(viewName = "end_title", setMethod = B.setTextColor)    public int endTitleColor=getResources().getColor(R.color.colorAccent);    @Bind(viewName = "end_title", setMethod = B.setOnClickListener, getMethod = B.getMethodNull)    public View.OnClickListener end_titleL = new View.OnClickListener() {            @Override            public void onClick(View v) {                end_title = "88888";                end_desc = "+++++";                endTitleColor = getResources().getColor(R.color.colorPrimary);                url = "~~~~~";                bindH.dataChange();            }        };}

值的写法有3种

@Bind(viewId = R.id.end_title,setMethod = B.setText)public String end_title = "1234";
@Bind(viewName = "end_title",setMethod = B.setText)public String end_title = "1234";
@Bind(setMethod = B.setText)public String end_title = "1234";

如果要映射的视图是一个textview,setMethod的默认值就是“setText”那么还有第四种写法.

public String end_title = "1234";

我来解释一下,上面3种用法,直接上图。

Screen_Shot_2016_01_12_at_11_10_19_AM

天然的2个缺陷

值对应的缺陷

textView.setText(CharSequence data);

所谓绑定,就是一一对应,view的方法只能接受一个参数。抽离出关键字就是:view,一个方法参数。但是有时候没办法处理特殊情况,比如我们使用volly框架的NetworkImageView,用法是这样的

NetworkImageView.setImageUrl(String url, ImageLoader imageLoader)

解决缺陷办法

class Data implements BindImpl {...        @Bind(viewName = B.viewNameSelf, setMethod = "setImageUrl")        private String url;                void setImageUrl(String url) {            networkImageView.setImageUrl(url, imageLoader);        }        ....        }

指定到data自身viewName = B.viewNameSelf,指定到自身的方法setImageUrl(名字可以任意取),在方法内部构造特殊情况。

伪值绑定的缺陷

textView.setText(int textId);

上面代码是找不到视图到值的对应,即不存在:

textId = textView.getText()

这样的对应,我将其称之为伪值对应,即只存在数据到视图的对应。

所以这样的类型我们要告知没有返回值(单向绑定的情况下不需要告知,只有双向绑定的时候才需要这么做,即添加getMethod = B.getMethodNull),代码如下:

@(Bind viewName="text",getMethod = B.getMethodNull)int textId

完整的用法:

使用的时候只需要2句代码即可:

data = new Data(); swordBind.bindOneWay(this, data);

data变化时通知view变化:

end_title = "88888";    end_desc = "+++++";    endTitleColor = getResources().getColor(R.color.colorPrimary);    url = "~~~~~";    swordBind.dataChange();

完整代码:

package com.taobao.pandora.hello;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.View;import com.taobao.sword.BindImpl;import com.taobao.sword.SwordBind;import com.taobao.sword.meta.B;import com.taobao.sword.meta.Bind;/** * Created by shanksYao on 1/5/16. */public class BindActivity extends Activity {    Data data;    SwordBind swordBind = new SwordBind();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.live_include_deal_status);        data = new Data();        swordBind.bindOneWay(this, data);    }    /**     * data就是用来操作View   任何跟View无关的属性 请不要放进来,以免造成设计混乱     */    class Data implements BindImpl {        @Bind(viewName = "end_title")        public String end_title = "1234";        public String end_desc = "aaa";        @Bind(viewName = "end_title", setMethod = B.setTextColor)        public int endTitleColor = getResources().getColor(R.color.colorAccent);        @Bind(viewName = "end_title", setMethod = B.setOnClickListener, getMethod = B.getMethodNull)        public View.OnClickListener end_titleL = new View.OnClickListener() {            @Override            public void onClick(View v) {                end_title = "88888";                end_desc = "+++++";                endTitleColor = getResources().getColor(R.color.colorPrimary);                url = "~~~~~";                swordBind.dataChange();            }        };        @Bind(viewName = "end_desc", setMethod = B.setOnClickListener)        public View.OnClickListener end_descL = new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.e("===", end_desc);                end_title = "+++++";                end_desc = "88888";                endTitleColor = getResources().getColor(R.color.colorAccent);                url = "++++++++";                swordBind.dataChange();                Log.e("===", end_desc);            }        };        @Bind(viewName = B.viewNameSelf, setMethod = "setImageUrl")        private String url;        void setImageUrl(String url) {            Log.e("self", "--------" + url);        }    }}

双向绑定

双向绑定和单向绑定的使用类似,只不过双向绑定,需要建立一个view的java类,如下:

class ViewHolder implements BindImpl {        private View price_ll;        private TextView luochui;        private View price_num;        private View other_win_desc;        private View end_rl;        private ImageView end_img;        private TextView end_title;        private TextView end_desc;        private View to_my_order;    }

使用时viewHolder的属性名就是xml里面id的名字,也就是说 private TextView luochui;对应

双向绑定的用法:

SwordBind.renderById(holder,this);        data = new Data();        swordBind.bind(holder,data);

view变化时,通知data变化:

holder.luochui.setText("123");        holder.end_desc.setText("hahahah");        ...        swordBind.viewChange();

优化特性:

1.getMethod不指明的情况下,是根据setMethod来推演的,推演出getMethod或者isMethod.

2.内部有一个对应常量关系,罗列了几乎所有用到的view的setMethod方法和对应的getMethod方法。
3.数据更新的时候,是变化的数据才会应用更新,内部采用差量更新的策略。

法则

  1. 尽量使用真值绑定(真值指的是具有双向绑定的值,上面提到的伪值绑定有缺陷)
  2. 单向绑定受外力可能会有影响(外力指的是除了data作用外的其他调用方式)
  3. 单向绑定很容易转化为双向绑定
  4. 尽量不要使用外力影响view的展现

请务必记住这张图

Screen_Shot_2016_01_12_at_4_48_13_PM

最新功能

  1. 2016.14 ,对 minifyenable =true 支持。## 标题 ##

Sword 框架新增实验特性

标签(空格分隔): Android


背景

这段时间一直在想能不能去掉swordBind.dataChange();或者swordBind.viewChange();代码的调用.即用户只需要关心操作数据,或者操作视图,不需要关心操作完数据后,通知sword框架数据有变化。这段时间有一些想法,虽然不是很完美,但是确实做到了去掉swordBind.dataChange();swordBind.viewChange();代码的目的。

直接代码对比

原来的代码是这样写的

end_title = "+++++";        end_desc = "88888";        url = "++++++++";        swordBind.dataChange();

现在成了这样:

end_title = "+++++";        end_desc = "88888";        url = "++++++++";

少了一行swordBind.dataChange();,只不过我把这行代码隐藏起来放到了另外的地方。

如何做到

Android的UI是运行在一个loop线程上的,每一次loop运行完,android.os.Looper.loop()都会调用代码

if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }

利用logging的回调,我们可以注入一个android.util.Printer对象,每次UI的线程执行完,我们都会得到一次回调。在这个回调里面做我们想做的事情,即swordBind.dataChange();或者swordBind.viewChange();.

限定模型

我不敢保证我上述的做法在任何情况下都是OK的,但是每一次引起数据变化,肯定是存在交互,交互最后都会回溯到UI线程上面,在UI线程的底部完成交互。即使是网络调用,AsycTask依赖会回调到UI线程上onPostExecute.

根据这一普适的特性,我可以构造一个属于我的模型,在这个模型里面我可以保证这一个实验特性的正确性。
Screen_Shot_2016_01_18_at_3_11_36_PM

收窄模型,即只要遵循data的值操作都是在UI线程上进行,(视图操作就不用说了,肯定在UI线程操作)

Screen_Shot_2016_01_19_at_9_27_03_AM

总结: 只要data的值操作都是在UI线程上进行,那么这个模型一定有效。这个实验特性也是一样有效

核心代码

/check value change    private boolean useExperiment=false;    private static List
swordBindList = new ArrayList<>(); private static boolean isInitSmart=false; public void setUseExperiment(boolean useExperiment) { this.useExperiment = useExperiment; } private void initSmartCheck(final Context context) { if (!isInitSmart) { context.getMainLooper().setMessageLogging(new Printer() { @Override public void println(String x) { if(x.startsWith("<<<<< Finished to")) for (SwordBind swordBind : swordBindList) { swordBind.dataChange(); swordBind.viewChange(); } } }); isInitSmart = true; } } public void onPause(){ if(useExperiment) swordBindList.remove(this); } public void onResume(){ if(!useExperiment) return; if(parent!=null) initSmartCheck(parent.getContext()); else initSmartCheck(activity); swordBindList.add(this); }

效率

为了平衡最少代码调用和代码执行效率2个方面,我苦苦挣扎了好几天,最后还是效率为王,即sword所在的activity或者fragment当前是否在前台,需要主动告诉我。如果在adaptor里一定要用这个实验特性,需要构造2个回调方法,让activity或者fragment回调。

用法

开启实验特性

swordBind.setUseExperiment(true);

开启完毕之后,调用2个核心代码即可:

@Override    protected void onResume() {        super.onResume();        swordBind.onResume();    }    @Override    protected void onPause() {        super.onPause();        swordBind.onPause();    }

完整代码:

package com.taobao.pandora.shanks;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import com.taobao.pandora.sword.BindImpl;import com.taobao.pandora.sword.SwordBind;import com.taobao.pandora.sword.meta.B;import com.taobao.pandora.sword.meta.Bind;/** * Created by shanksYao on 1/5/16. */public class BindActivity extends Activity {    Data data;    SwordBind swordBind = new SwordBind();     ViewHolder holder = new ViewHolder();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.live_include_deal_status);        swordBind.renderById(holder,this);        data = new Data();        swordBind.bind(holder,data);        swordBind.setUseExperiment(true);    }    @Override    protected void onResume() {        super.onResume();        swordBind.onResume();    }    @Override    protected void onPause() {        super.onPause();        swordBind.onPause();    }    class ViewHolder implements BindImpl {        private View price_ll;        private TextView luochui;        private View price_num;        private View other_win_desc;        private View end_rl;        private ImageView end_img;        private TextView end_title;        private TextView end_desc;        private View to_my_order;    }    /**     * data就是用来操作View   任何跟View无关的属性 请不要放进来,以免造成设计混乱     */    class Data implements BindImpl {        @Bind(viewName = "end_title",setMethod = B.setText,getMethod = "getText")        public CharSequence end_title = "1234";        public String end_desc = "aaa";        @Bind(viewName = "end_title", setMethod = B.setTextColor)        public int endTitleColor = getResources().getColor(R.color.colorAccent);        @Bind(viewName = "end_title", setMethod = B.setOnClickListener, getMethod = B.getMethodNull)        public View.OnClickListener end_titleL = new View.OnClickListener() {            @Override            public void onClick(View v) {               /* end_title = "88888";                end_desc = "+++++";                endTitleColor = getResources().getColor(R.color.colorPrimary);                url = "~~~~~";*/                holder.end_title.setText("123");                holder.end_desc.setText("hahahah");            //    swordBind.dataChange();            }        };        @Bind(viewName = "end_desc", setMethod = B.setOnClickListener)        public View.OnClickListener end_descL = new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.e("===", end_desc);                end_title = "+++++";                end_desc = "88888";                endTitleColor = getResources().getColor(R.color.colorAccent);                url = "++++++++";             //   swordBind.dataChange();                Log.e("===", end_desc);            }        };        //        void setEndTitle(int id) {            end_title = getResources().getString(id);            //swordBind.dataChange();        }        void setUrl(ViewHolder holder, String url) {            /设置方法处理            holder.end_img.setImageDrawable(null);        }        @Bind(viewName = B.viewNameSelf, setMethod = "setImageUrl")        private String url;        void setImageUrl(String url) {            Log.e("self", "--------" + url);        }    }    Ui数据绑定}

转载地址:http://ynbva.baihongyu.com/

你可能感兴趣的文章
Android 使用 ViewPager+RecyclerView+SmartRefreshLayout 实现顶部图片下拉视差效果
查看>>
Flutter之基础Widget
查看>>
写给0-3岁产品经理的12封信(第08篇)——产品运营能力
查看>>
ArcGIS Engine 符号自动化配置工具实现
查看>>
小程序 · 跳转带参数写法,兼容url的出错
查看>>
flutter error
查看>>
Flask框架从入门到精通之模型数据库配置(十一)
查看>>
10年重新出发
查看>>
2019年-年终总结
查看>>
聊聊elasticsearch的RoutingService
查看>>
让人抓头的Java并发(一) 轻松认识多线程
查看>>
从源码剖析useState的执行过程
查看>>
地包天如何矫正?
查看>>
中间件
查看>>
Android SharedPreferences
查看>>
css面试题
查看>>
Vue组建通信
查看>>
用CSS画一个带阴影的三角形
查看>>
前端Vue:函数式组件
查看>>
程鑫峰:1.26特朗.普力挺美元力挽狂澜,伦敦金行情分析
查看>>