Android绑定框架-FSS开源框架之FssBind

介绍

fss_bind是fss框架的一部分,使用它可以实现Activity或Fragment的绑定,比如layout绑定,finish方法绑定,View/OnClick绑定(类似ButterKnife),参数绑定,参数绑定到databinding变量,view绑定到intent对象等等。
gitHub地址:https://github.com/gongbox/fss

使用教程

  • 用法1:绑定布局文件(Activity,Fragmeng等)
 @BindActivity(R.layout.activity_bind_test)
 public class BindTestActivity extends BaseFssActivity {
 }

另外,BindActivity可以绑定退出按钮,如下:

 @BindActivity(value = R.layout.activity_bind_test, finishViewId = R.id.btn_finish)
 public class BindTestActivity extends BaseFssActivity {
 }

这样就可以在点击id为btn_finish的控件时直接调用finish方法,退出Activity
如果是Fragment,也可以绑定

 @BindFragment(R.layout.fragment_bind_test)
 public class BindTestFragment extends BaseFssFragment {
 }
  • 用法2:绑定视图
 public class BindTestActivity extends BaseFssActivity {
     @BindView(R.id.list_view)
     private ListView listView;  //支持绑定所有类型的视图
 }
  • 用法3:绑定点击事件
 public class BindTestActivity extends BaseFssActivity {
     @BindOnClick(R.id.btn_click)
     private void click() {
         ...
     }
     //或者
     @BindOnClick(R.id.btn_click)
     private void click(View view) {
         ...
     }
     //或者
     @BindOnClick(R.id.btn_click)
     private void click(Button button) {
         ...
     }
 }

或者可以声明写在类上

 @BindOnClick(value = R.id.btn_test, onClickMethod = "test")
 public class BindTestActivity extends BaseFssActivity {
  //方法可以无参或者有一个该控件类型或父类型的参数,如private void test(Button button){},或private void click(View view){}
  private void test(){
     ...
  }
 }

上面的写法有其他妙用,比如绑定退出按钮到finish方法上

  • 用法4:绑定路由
 //当用户点击id为btn_route的控件时,便可以直接跳转到BindDetailActivity页面(支持携带参数,这里先不介绍)
 @BindRoute(viewId = R.id.btn_route, toActivity = BindDetailActivity.class)  
 public class BindTestActivity extends BaseFssActivity {
 }
  • 用法5: 绑定参数(支持Activity或Fragmeng)
    假设A页面跳转到B页面,并且传递了一个Integer类型(或其他类型)的参数,key值为“value”,那么在B页面可以直接使用BindExtra绑定变量获取这个参数
 public class BindTestActivity extends BaseFssActivity {
     @BindExtra(value = "value")//key值与变量名相同时可以省略为@BindExtra
     private Integer value;
 }
  • 用法6:绑定databinding变量
    如果你的项目使用了databinding(数据绑定),那么你还可以这么用:
    假设A页面跳转到B页面,并且传递了一个String类型(或其他类型)的参数,key值为“value”,那么在B页面可以直接使用BindExtra绑定变量获取这个参数,并且可以将这个参数绑定到页面对应的视图中,如下所示:
    B页面的布局文件:
 <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android">

     <data>
         <variable
             name="value"
             type="String" />
     </data>

     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical">

         <TextView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="50dp"
             android:gravity="center"
             android:text="@{value}" />

     </LinearLayout>
 </layout>

B页面的代码

 public class BindTestActivity extends BaseFssActivity {
     //BindExtra除了会将A页面传过来的参数绑定到value变量上,也会将该变量绑定到布局文件中databinding变量名value的变量中
     @BindExtra(id = BR.value) 
     private String value;
 }

如果你不需要使用value变量的话,你也可以这么写,效果同上

 @BindExtra(value = "value", id = BR.value) 
 public class BindTestActivity extends BaseFssActivity {
 }

看到这儿,如果你觉得也没多简洁的话,那我们接下来看看实际使用中的几个例子。

使用与集成

implementation 'com.github.gongbox.fss:bind:{lastversion}'
  • 一般Android项目中会有一个Activity或Fragmengt的父类,我们在父类中初始化后即可使用。如下
public abstract class BaseFssActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //获取布局
        int layoutId = FssBind.getLayoutId(this);
        //设置布局
        setContentLayout(layoutId);
        //绑定参数(只绑定BaseFssActivity及其子类的参数)
        FssBind.bind(this, BaseFssActivity.class);
        ...
    }
    
    //这样写是为了支持databinding
     protected void setContentLayout(@LayoutRes int layoutResID) {
        setContentView(layoutResID);
    }
}
public abstract class BaseFssFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //获取布局
        int layoutId = FssBind.getLayoutId(this);
        //根据布局填充
        return inflater.inflate(layoutId, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //绑定参数(只绑定BaseFssFragment及其子类的参数)
        FssBind.bind(this, BaseFssFragment.class);
        ...
    }
}

如果使用databinding的话,可以在写一个databinding的父类,如下:

public abstract class BaseBindingActivity<VB extends ViewDataBinding> extends BaseActivity {

    protected VB binding;  //变量名必须为binding

    @Override
    protected void setContentLayout(int layoutResID) {
        binding = DataBindingUtil.setContentView(this, layoutResID);
    }
}
public abstract class BaseBindingFragment<VB extends ViewDataBinding> extends BaseFragment {
    protected VB binding;  //变量名必须为binding

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, FssBind.getLayoutId(this), container, false);
        return binding.getRoot();
    }
}

声明上面的父类后,我们就可以在子类中直接使用了

例子

我们先看个简单的例子

  • 例子1
    比如我们有一个详情页面,这个详情页由另一个Activity跳转过来,并且向这个详情页传递了参数(key值为“value”),详情页页面只有一个textView(id为R.id.text)以及一个返回按钮(R.id.back),详情页要求只需要显示数据并且返回就可以了就可以了,那么我们可以这么写
@BindActivity(value = R.layout.activity_detail, finishViewId = R.id.back)
public class DetailActivity extends BaseBindingActivity<ActivityDetailBinding> {
    
   //绑定intent参数,将key值为value的参数绑定达到value变量
   @BindExtra(value = "value")
   private String value;
    
    //绑定视图
   @BindView(R.id.text)
   private TextView textView;
    
   //使用的时候,注意initView方法在OnCreate方法里会自己调用,参考fss_runpriority框架
   @Override
   protected void initView(){
       textView.setText(value);
   }
}

如果使用了databinding的话,那么代码又会更加简单了,假设databinding页面中使用变量value,该变量绑定到textView的text属性上:

@BindActivity(value = R.layout.activity_detail, finishViewId = R.id.back)
public class DetailActivity extends BaseBindingActivity<ActivityDetailBinding> {

    @BindExtra(value = "value",  id = BR.value)
    private String value;

}

上面的代码还可以写的更简单:

@BindActivity(value = R.layout.activity_detail, finishViewId = R.id.back)
@BindExtra(value = "EXTRA_VALUE",  id = BR.value)
public class DetailActivity extends BaseBindingActivity<ActivityDetailBinding> {
}

看到没有,一行代码都没有了!!!,这就是使用fss_bind框架的简洁。
然后我们看一个比较完整的例子:

  • 例子2
//绑定layout,并且将视图中id为R.id.img_back的点击事件绑定到finish方法,如果是Fragment,注解需要改为BindFragment,并且没有finish参数
@BindActivity(value = R.layout.activity_a, finishViewId = R.id.img_back)
//绑定Route,当用户点击id为R.id.to_activity_b的view后,会跳转到ActivityB
@BindRoute(viewId = R.id.to_activity_b, toActivity = ActivityB.class)
//绑定Route,当用户点击id为R.id.to_activity_c的view后,会跳转到ActivityC,并会传递参数,参数key值为EXTRA_VALUE2,携带参数为"value":value变量值,"value2":"789","value3":[234](这里为int类型数组)
@BindRoute(viewId = R.id.to_activity_c,toActivity = ActivityC.class,
        extras = {":@value", "value2:789", "value3:(int)[234]"}
)
//绑定intent参数到databinding变量
@BindExtra(value = "EXTRA_VALUE4",  id = BR.value4)
//绑定点击事件,将id为R.id.test的view的点击事件绑定到test方法上,要求该方法没有参数或只有一个View类型的参数
@BindOnClick(value = {R.id.test}, onClickMethod = "test")
public class ActivityA extends BaseBindingActivity<ActivityABinding> {
    
    //绑定intent参数,将key值为EXTRA_VALUE的参数绑定达到value变量
    @BindExtra(value = "EXTRA_VALUE")
    private Integer value;
    
    //绑定intent参数,和上面的BindParam不同的时,除了会将变量绑定外,在使用databinding的情况下,还会将该变量值直接绑定到databinding变量
    //另外,该注解可以直接写到类上面,因为有的时候我只需要将intent传递过来的参数绑定到databinding中,而我并不关心它的值
    @BindExtra(value = "EXTRA_VALUE3",  id = BR.value3)
    private String value3;
    
    //路由参数,用于绑定路由时传递参数
    private String value2 = "123456";
    
    //绑定视图
    @BindView(value = R.id.list_view)
    private ListView listView;

    //绑定点击事件,将id为R.id.img_fun1的view的点击事件绑定到opertation方法上,注意该方法没有参数
    @BindOnClick(R.id.img_fun1)
    public void opertation() {
    }
    
    //绑定点击事件,将id为R.id.img_fun2,R.id.img_fun3的view的点击事件绑定到opertation方法上
    //注意该方法有一个参数View,该参数即为对应触发点击事件对的view
    @BindOnClick({R.id.img_fun2,R.id.img_fun3})
    public void opertation(View view) {
    }
    
    private void test(){
    }
}

想想如果不用fss_bind框架上面的代码会有多少?

例子我们先看到这儿,如果想要更详细了解怎么使用,我为大家提供了demo工程

接下来,我们总结一下fss_bind框架的特点。

  • 大量运用注解,开发更简洁,规范
  • 由于使用了反射,运行效率不高。(虽然不高,但也可以接受,除非你的应用要求特别高)

用过ButterKnife的可能知道什么是绑定视图,fss_bind可不是ButterKnife的山寨,它完全来自于笔者自己的灵感,实现方式与ButterKnife不大相同,它的功能也不仅仅是绑定视图。
我们再对比一下和butterKnife的区别

  1. fss_bind框架中BindView视图,BindOnClick方法等修饰的属性或方法可以为任意访问修饰符,而ButterKnife必须为public修饰符
  2. ButterKnife适合绑定视图(绝大多数情况),绑定资源文件,而fss_bind除了绑定视图或点击事件外还可以绑定参数,路由等等功能
  3. ButterKnife中用到的注解不能继承,而fss_bind所使用到的注解可以被继承,这在开发模板类时十分有用
  4. ButterKnife的优势是性能,因为它没有使用反射,因此效率比较高,而fss_bind是使用反射实现的,效率不及ButterKnife

笔者在实际使用fss框架开发过程中,页面代码量较以往简洁清晰了很多,以往一个几十上百行代码的页面可以优化到甚至没有一行或仅仅几行代码。

更多FSS框架:

推荐阅读更多精彩内容