Android DataBinding库(MVVM设计模式)
什么是MVVM
说到DataBinding,就有必要先提起MVVM设计模式。
Model–View–ViewModel(MVVM) 是一个软件架构设计模式,相比MVVM,大家对MVC或MVP可能会更加熟悉。
- MVC:(VIew-Model-Controller)
 早期将VIew、Model、Controller代码块进行划分,使得程序大部分分离,降低耦合。
- MVP:(VIew-Model-Presenter)由于MVC中View和Model之间的依赖太强,导致Activity中的代码过于臃肿。为了他们可以绝对独立的存在,慢慢演化出了MVP。在MVP中View并不直接使用Model,它们之间的通信是通过 Presenter (MVC中的Controller)来进行的。
- MVVM:(Model–View–ViewModel)
 MVVM可以算是MVP的升级版,将 Presenter 改名为 ViewModel。关键在于View和Model的双向绑定,当View有用户输入后,ViewModel通知Model更新数据,同理Model数据更新后,ViewModel通知View更新。
Data Binding
在Google I/O 2015上,伴随着Android M预览版发布的Data Binding兼容函数库。
不知道要扯什么了,还是直接上代码,来看看Data Binding的魅力吧。
- 环境要求- Data Binding对使用的环境还是有一定要求的(这货有点挑) 
 Android Studio版本在1.3以上
 gradle的版本要在1.5.0-alpha1以上
 需要在Android SDK manager中下载Android Support repository
 然后在对应的Module的build.gradle中添加- android {
 ....
 dataBinding {
 enabled =true
 }
 }- Gradle需要升级版本的可以参考升级Gradle版本 
- 创建对象- 创建一个User类 - public class User {
 private String firstName;
 private String lastName; public User(String firstName, String lastName) {
 this.firstName = firstName;
 this.lastName = lastName;
 } public String getFirstName() {
 return this.firstName;
 } public String getLastName() {
 return this.lastName;
 } public void setLastName(String lastName) {
 this.lastName = lastName;
 } public void setFirstName(String firstName) {
 this.firstName = firstName;
 }
 }
- 布局- 在activity_main.xml中布局 - <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android">
 <data>
 <import type="com.example.gavin.databindingtest.User"/>
 <variable
 name="user"
 type="User" />
 </data>
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 android:gravity="center"
 >
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@{user.firstName}"
 android:textSize="20sp" />
 <Button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@{user.lastName}"
 android:textSize="25sp" />
 </LinearLayout>
 </layout>- 这里跟平时的布局有点不同,最外层是layout,里面分别是是data以及我们的布局。 
 data:声明了需要用到的user对象,type用于是定路径。
 可以在TextView中的看到android:text="@{user.firstName}", 这是什么鬼,没见过这么写的!!!
 (不急,继续往下看)
- 绑定数据- 看看下面的MainActivity - public class MainActivity extends AppCompatActivity {
 private ActivityMainBinding binding;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
 User user = new User("Micheal", "Jack");
 binding.setUser(user);
 }
 }- 问我ActivityMainBinding哪来的?我怎么知道... 
 ActivityMainBinding是根据布局文件的名字生成的,在后面加了Binding。
 运行下看看效果吧 效果 效果
有点懵逼了,就绑定了下而已,这些数据是怎么显示到界面上的。
 
他是怎么工作的?
原来Data Binding 在程序代码正在编译的时候,找到所有它需要的信息。然后通过语法来解析这些表达式,最后生成一个类。
通过反编译我们可以看到,Data Binding为我们生成了databinding包,以及ActivityMainBinding类(反编译可以参考这里)
看看我们在onCreate中最后调用的binding.setUser(user),在ActivityMainBinding中可以看到这个方法。
 setUser方法
setUser方法我想就是这个 super.requestRebind()对数据进行了绑定,至于里面怎么实现的,有待进一步研究。
更多用法
上面只是用一个简单的例子,展示了Data Binding的用法,如果想在实际项目中使用,可不是上面这例子可以搞定的。下面就来说说Data Bindig的更多用法。
- 首先消除下大家对空指针的顾虑- 自动生成的 DataBinding 代码会检查null,避免出现NullPointerException。 
 例如在表达式中@{user.phone}如果user == null 那么会为user.phone设置默认值null而不会导致程序崩溃(基本类型将赋予默认值如int为0,引用类型都会赋值null)
- 自定义DataBinding名- 如果不喜欢自动生成的Data Binding名,我们可以自己来定义 - <data class="MainBinding">
 ....
 </data>- class对应的就是生成的Data Binding名 
- 导包- 跟Java中的用法相似,布局文件中支持import的使用,原来的代码是这样 - <data>
 <variable name="user" type="com.example.gavin.databindingtest.User" />
 </data>- 使用import后可以写成这样: - <data>
 <import type="com.example.gavin.databindingtest.User"/>
 <variable
 name="user"
 type="User" />
 </data>- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 
 遇到相同的类名的时候:- <data>
 <import type="com.example.gavin.databindingtest.User" alias="User"/>
 <import type="com.example.gavin.mc.User" alias="mcUser"/>
 <variable name="user" type="User"/>
 <variable name="mcUser" type="mcUser"/>
 </data>- 使用alias设置别名,这样user对应的就是com.example.gavin.databindingtest.User,mcUser就对应com.example.gavin.mc.User,然后 - <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@{user.firstName}"/>- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 
 当需要用到一些包时,在Java中可以自动导包,不过在布局文件中就没有这么方便了。需要使用import导入这些包,才能使用。如,需要用到View的时候- <data>
 <import type="android.view.View"/>
 </data>
 ...
 <TextView
 ...
 android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}"
 />- 注意:只要是在Java中需要导入包的类,这边都需要导入,如:Map、ArrayList等,不过java.lang包里的类是可以不用导包的 
- 表达式- 在布局中,不仅可以使用 - android:text="@{user.lastName}"- 还可以使用表达式如: - 三元运算- 在User中添加boolean类型的isStudent属性,用来判断是否为学生。 - <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text='@{user.isStudent? "Student": "Other"}'
 android:textSize="30sp"/>- 注意:需要用到双引号的时候,外层的双引号改成单引号。 
 还可以这样用- <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="学生"
 android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}"
 android:textSize="30sp"/>- 这里用到的View需要在data中声明 - <data>
 <import type="android.view.View"/>
 </data>- 注意:android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}",可能会被标记成红色,不用管它编译会通过的 - ??- 除了常用的操作法,另外还提供了一个 null 的合并运算符号 ??,这是一个三目运算符的简便写法。 - contact.lastName ?? contact.name- 相当于 - contact.lastName != null ? contact.lastName : contact.name- 所支持的操作符如下: 
 数学运算符 + - / * %
 字符串拼接 +
 逻辑运算 && ||
 二进制运算 & | ^
 一元运算符 + - ! ~
 位运算符 >> >>> <<
 比较运算符 == > < >= <=
 instanceof
 Grouping ()
 文字 - character, String, numeric, null
 类型转换 cast
 方法调用 methods call
 字段使用 field access
 数组使用 [] Arrary access
 三元运算符 ? :
- 显示图片- 除了文字的设置,网络图片的显示也是我们常用的。来看看Data Binding是怎么实现图片的加载的。 
 首先要提到BindingAdapter注解,这里创建了一个类,里面有显示图片的方法。- public class ImageUtil {
 /**
 * 使用ImageLoader显示图片
 * @param imageView
 * @param url
 */
 @BindingAdapter({"bind:image"})
 public static void imageLoader(ImageView imageView, String url) {
 ImageLoader.getInstance().displayImage(url, imageView);
 }
 }- (这方法必须是public static的,否则会报错) 
 这里只用了bind声明了一个image自定义属性,等下在布局中会用到。
 这个类中只有一个静态方法imageLoader,里面有两参数,一个是需要设置图片的view,另一个是对应的Url,这里使用了ImageLoader库加载图片。
 看看吧它的布局是什么样的吧- <?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"> <data >
 <variable
 name="imageUrl"
 type="String"/>
 </data> <LinearLayout android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 android:gravity="center"
 >
 <ImageView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 app:image = "@{imageUrl}"/>
 </LinearLayout>
 </layout>- 最后在MainActivity中绑定下数据就可以了 - binding.setImageUrl(
 "http://115.159.198.162:3000/posts/57355a92d9ca741017a28375/1467250338739.jpg");- 哇靠!!!就这样?我都没看出来它是怎么设置这些图片的。 
 不管了,先看看效果。(其中的原理以后慢慢唠,这里就负责说明怎么使用,这篇已经够长了,不想再写了) 看个美女压压惊 看个美女压压惊- 使用BindingAdapter的时候,我这还出现了这样的提示,不过不影响运行。不知道你们会不会...   - 【已解决】 
 感谢颜路同学指出@BindingAdapter({"bind:image"}) 改成 @BindingAdapter({"image"}) 就不会有警告了
- 点击事件- 在MainActivity中声明方法: - //参数View必须有,必须是public,参数View不能改成对应的控件,只能是View,否则编译不通过
 public void onClick(View view) {
 Toast.makeText(this,"点击事件", Toast.LENGTH_LONG).show();
 }- 布局中: - <data>
 ...
 <variable
 name="mainActivity"
 type="com.example.gavin.databindingtest.MainActivity"/>
 </data>
 ....
 <Button
 ...
 android:onClick="@{mainActivity.onClick}"
 />- 最后记得在MainActivity中调用 - binding.setMainActivity(this);- (发现:布局文件中,variable中的name,在binding中都会生成一个对应的set方法,如:setMainActivity。有set方法,那就应该有get方法,试试getMainActivity,还真有) 
 运行下看看效果 点击事件 点击事件- 当然如果你不想吧点击事件写在MainActivity中,你把它单独写在一个类里面: - public class MyHandler {
 public void onClick(View view) {
 Toast.makeText(view.getContext(), "点击事件", Toast.LENGTH_LONG).show();
 }
 }- <data>
 ...
 <variable
 name="handle"
 type="com.example.gavin.databindingtest.MyHandler"/>
 </data>
 ....
 <Button
 ...
 android:onClick="@{handle.onClick}"
 />
 </data>- 在MainActivity调用 - binding.setHandle(new MyHandler());
- 调用Activity中的变量- 上面看到它调用MainActivity中的onClick方法,那么可以调用MainActivity中的属性吗? 
 在MainActivity中定义mName,- public static String mName = "MM";- 布局中 - <data>
 ...
 <variable
 name="mainActivity"
 type="com.example.gavin.databindingtest.MainActivity"/>
 </data>
 <Button
 ...
 android:text="@{mainActivity.mName}"
 />- 注意:这个变量必须是public static 
- 数据改变时更新UI- 当数据发生变化时,我们可以这样更新UI - private ActivityMainBinding binding;
 private User user;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
 user = new User("Micheal", "Jack");
 binding.setUser(user);
 binding.setHandle(new MyHandler());
 delay();
 }
 /**
 * 两秒后改变firstName
 */
 private void delay() {
 new Handler().postDelayed(new Runnable() {
 @Override
 public void run() {
 user.setFirstName("Com");
 binding.setUser(user);
 }
 }, 2000);
 }- 看看调用的这个setUser是什么:  setUser setUser- 从反编译的代码中可以看出,setUser方法中重新绑定了数据。 
 看下效果
 
- BaseObservable- 使用上面的代码实现了UI的更新你就满足了?其实官方为我们提供了更加简便的方式,使User继承BaseObservable,代码如下 - public class User extends BaseObservable {
 private String firstName;
 private String lastName; public User(String firstName, String lastName) {
 this.firstName = firstName;
 this.lastName = lastName;
 }
 @Bindable
 public String getFirstName() {
 return this.firstName;
 }
 @Bindable
 public String getLastName() {
 return this.lastName;
 } public void setLastName(String lastName) {
 this.lastName = lastName;
 notifyPropertyChanged(BR.lastName);
 } public void setFirstName(String firstName) {
 this.firstName = firstName;
 notifyPropertyChanged(BR.firstName);
 }
 }- 只要user发生变化,就能达到改变UI的效果。在MainActivity中只要调用以下代码 - user.setFirstName("Com");- 有了BaseObservable就够了?不不不,我比较懒,不想写那么多@Bindable和notifyPropertyChanged。万一里面有几十个属性,那不写哭起来?而且还有可能写丢了。 
 Data Binding的开发者贴心得为我们准备了一系列的ObservableField,包括: ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat,ObservableDouble, 以及 ObservableParcelable看看它们的用法
 ObservableField的使用
 1、创建User2- public class User2 {
 public final ObservableField<String> firstName = new ObservableField<>();
 public final ObservableField<String> lastName = new ObservableField<>();
 public final ObservableInt age = new ObservableInt();
 public final ObservableBoolean isStudent = new ObservableBoolean();
 }- 这类里面没有Get/Set。 
 2、布局文件- <TextView
 ...
 android:text="@{user2.firstName}" />
 <TextView
 ...
 android:text="@{user2.lastName}" />
 <TextView
 ...
 android:text="@{String.valueOf(user2.age)}"
 />- 3、MainActivity中 - mUser2 = new User2();
 binding.setUser2(mUser2);
 mUser2.firstName.set("Mr");
 mUser2.lastName.set("Bean");
 mUser2.age.set(20);
 mUser2.isStudent.set(false);- 这里new了一个User2对象后,直接就绑定了。之后只要mUser2中的数据发生变化,UI也会随之更新。 
 除了这几个Map跟List也是必不可少的,Data Binding为我们提供了 ObservableArrayMap和ObservableArrayList。
 ObservableArrayMap的使用- ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
 user.put("firstName", "Google");
 user.put("lastName", "Inc.");
 user.put("age", 17);- <data>
 <import type="android.databinding.ObservableMap"/>
 <variable name="user" type="ObservableMap<String, Object>"/>
 </data>
 …
 <TextView
 android:text='@{user["lastName"]}'
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>
 <TextView
 android:text='@{String.valueOf(1 + (Integer)user["age"])}'
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>- ObservableArrayList的使用 - ObservableArrayList<Object> user = new ObservableArrayList<>();
 user.add("Google");
 user.add("Inc.");
 user.add(17);- <data>
 <import type="android.databinding.ObservableList"/>
 <import type="com.example.my.app.Fields"/>
 <variable name="user" type="ObservableList<Object>"/>
 </data>
 …
 <TextView
 android:text='@{user[Fields.LAST_NAME]}'
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>
 <TextView
 android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>- 在布局中使用中文时,编译无法通过。 - android:text='@{user2.isStudent?"学生":"非学生"}'- 感谢吕檀溪同学的解决方案: 
 这是java环境的问题,在系统环境变量中增加一个变量,变量名为: JAVA_TOOL_OPTIONS, 变量值为:-Dfile.encoding=UTF-8,保存。要重启一次电脑,中文就解决了,但是在某些地方,编译的时候控制台会出现部分乱
- 在RecyclerView或ListView中使用- 前面说了那么多基础的用法,可还是不能达到我们的需求。几乎在每个app中都有列表的存在,RecyclerView或ListView,从上面所说的似乎还看不出Data Binding在RecyclerView或ListView中是否也能起作用。(用屁股想也知道,Google的开发团对怎么可能会犯这么低级的错误)。下面以RecyclerView为例子: 
 1、直接看Item的布局(user_item.xml):- <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android">
 <data>
 <variable
 name="user2"
 type="com.example.gavin.databindingtest.User2" />
 </data>
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:padding="10dp"
 android:orientation="horizontal">
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@{user2.firstName}"/>
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="·"/>
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@{user2.lastName}"/>
 <View
 android:layout_width="0dp"
 android:layout_height="0dp"
 android:layout_weight="1"/>
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text='@{user2.age+""}'/>
 </LinearLayout>
 </layout>- 2、RecyclerView的数据绑定是在Adapter中完成的,下面看看Adapter(这里使用了一个Adapter,如果你在使用的时候发现RecyclerView的动画没了,去这里寻找答案) - public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyHolder> { private List<User2> mData = new ArrayList<>(); public MyAdapter(List<User2> data) {
 this.mData = data;
 } @Override
 public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 return MyHolder.create(LayoutInflater.from(parent.getContext()), parent);
 } @Override
 public void onBindViewHolder(MyHolder holder, int position) {
 holder.bindTo(mData.get(position));
 } @Override
 public int getItemCount() {
 if (mData == null)
 return 0;
 return mData.size();
 } static class MyHolder extends RecyclerView.ViewHolder {
 private UserItemBinding mBinding; static MyHolder create(LayoutInflater inflater, ViewGroup parent) {
 UserItemBinding binding = UserItemBinding.inflate(inflater, parent, false);
 return new MyHolder(binding);
 } private MyHolder(UserItemBinding binding) {
 super(binding.getRoot());
 this.mBinding = binding;
 } public void bindTo(User2 user) {
 mBinding.setUser2(user);
 mBinding.executePendingBindings();
 } }
 }- 3、最后在布局和MainActivity中的使用跟平时的用法一样 
 布局中加入RecyclerView:- <android.support.v7.widget.RecyclerView
 android:id="@+id/recycler_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"/>- MainActivity中: - List<User2> data = new ArrayList<>();
 for (int i = 0; i < 20; i++) {
 User2 user2 = new User2();
 user2.age.set(30);
 user2.firstName.set("Micheal " + i);
 user2.lastName.set("Jack " + i);
 data.add(user2);
 }
 RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
 LinearLayoutManager layoutManager = new LinearLayoutManager(
 this, LinearLayoutManager.VERTICAL, false);
 recyclerView.setLayoutManager(layoutManager);
 recyclerView.setAdapter(new MyAdapter(data));- 这样就可以了。 
 不过,在自动生成的ActivityMainBinding中,我们可以看到根据RecyclerView的id,会自动生成一个recyclerView。  - 所以在MainActivity中,我们可以不用findViewById,直接使用binding.recyclerView。 - LinearLayoutManager layoutManager = new LinearLayoutManager(
 this, LinearLayoutManager.VERTICAL, false);
 binding.recyclerView.setLayoutManager(layoutManager);
 binding.recyclerView.setAdapter(new MyAdapter(data));- 来看看效果吧:  RecyclerView RecyclerView
Tips:
- tip1:若需要显示int类型,需要加上"":如- user.age为int类型,需要这样用 - <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text='@{""+user.age}'/>- 或者 - <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@{String.valueOf(user.age)}"/>
- tip2:不建议新手使用,出现错误的时候根据提示,不容易找到出错位置。(是根本找不到...)
源码地址https://github.com/Gavin-ZYX/DataBindingTest
Android DataBinding库(MVVM设计模式)的更多相关文章
- 设计模式笔记之三:Android DataBinding库(MVVM设计模式)
		本博客转自郭霖公众号:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236908&idx=1&sn=9e53 ... 
- android dataBinding详解
		官方介绍地址:http://developer.android.com/intl/zh-cn/tools/data-binding/guide.html 2015 Google IO 大会带来的 Da ... 
- [Module] 06 - DataBinding and MVVM
		下一步学习列表: Android DataBinding使用总结(一) *** Android DataBinding使用总结(二) Android DataBinding使用总结(三)列表展示 An ... 
- Android  中的mvvm
		我们来了解一下MVVM模式与Databinding ,MVVM是一种模式,Databinding 是一种框架.DataBinding是一个实现数据和UI绑定的框架.而ViewModel和View可以通 ... 
- 免费的Android UI库及组件推荐
		短短数年时间Android平台就已经形成了一个庞大而活跃的开发者社区.许多社区开发的项目业已进入成熟阶段,甚至可以用于商业的软件生产中,且不用担心质量问题. 本文编译自androiduipattern ... 
- 介绍三个Android支持库控件:TabLayout+ViewPager+RecyclerView
		本文主要介绍如下三个Android支持库控件的配合使用: TabLayout:android.support.design.widget.TabLayout ViewPager:android.sup ... 
- Android开源库项目集锦
		一.兼容类库 ActionBarSherlock : Action Bar是Android 3.0后才開始支持的,ActionBarSherlock是让Action Bar功能支持2.X后的全部平台. ... 
- Android MVC,MVP,MVVM模式入门——重构登陆注册功能
		一 MVC模式: M:model,业务逻辑 V:view,对应布局文件 C:Controllor,对应Activity 项目框架: 代码部分: layout文件(适用于MVC和MVP两个Demo): ... 
- Android 支持库迁移到AndroidX
		一.背景 Android系统版本在不断更新,从最初的Android 1.0到现在Google和各大手机厂商正在推的Android 10,平均下来每个年头都有一个大的版本更新.但用户正在用的手机上的An ... 
随机推荐
- tomcat8.5请求参数限制的问题
			前段时间遇到这个问题: 包含json字符串类型的参数的http请求失败,返回状态码400,提示invalid character found in the request target. Tomcat ... 
- session_write_close() 用法
			1.需要session控制的大文件下载,防止因为占用session文件时间太久,导致其他页面的session无法执行 session_write_close() worked as a lifesav ... 
- poj 2104 静态主席树
			我的第一道主席树(静态). 先记下自己对主席树的理解: 主席树的作用是用于查询区间第k大的元素(初始化nlog(n),查询log(n)) 主席树=可持续线段树+前缀和思想 主席树实际上是n棵线段树(由 ... 
- 小识.htaccess文件
			.htaccess文件(或者"分布式配置文件")提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录.作为用户 ... 
- iOS防止button重复点击
			项目中常会遇到在按钮的点击事件中去执行一些耗时操作.如果处理不当经常会出现连续多次点击push多次的情况,造成不好的用户体验. 一种情况是用户快速连续点击,这种情况无法避免.另一种情况是点击一次后响应 ... 
- Ext.form.ComboBox常用属性详解
			Ext.form.ComboBox常用属性详解 标签: Extjs js combo js 代码 var combo = new Ext.form.ComboBox({ store : new Ext ... 
- IOS-百度地图API用点生成线路、导航、自定义标注 2013年11月更新
			IOS百度地图API开发自定义气泡,点击气泡自动生成路线,以及拖拽IOS百度地图开发POISearch搜索附近停车场,附近加油站IOS百度地图视角跳到用户当前位置IOS百度地图开发实时路况IOS开发百 ... 
- ADC In An FPGA
			http://davidkessner.wordpress.com/2011/05/01/adc-in-an-fpga/ Geek Alert! What follows is very techn ... 
- gdb -Mysql源代码级调试方法
			http://blog.csdn.net/hitzhang/article/details/5985474 gdb -q --batch --ex "set height 0" - ... 
- linux下关于压缩、解压相关的操作
			本文转自: http://alex09.iteye.com/blog/647128 很不错的linux下关于压缩.解压相关的操作,适合于linux初学者. .tar 解包:tar xvf Fil ... 
