一个完全摆脱findViewById的自动绑定库
问题
先来看一个正常的写法:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="TEXT_VIEW"
android:textSize="22sp"/>
<Button
android:id="@+id/btn_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="BUTTON"/>
</LinearLayout>
public class StartActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tv_test;
private Button btn_test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
// init view
tv_test = findViewById(R.id.tv_test);
btn_test = findViewById(R.id.btn_test);
// init listener
btn_test.setOnClickListener(this);
tv_test.setText("Text changed!");
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_test:
Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
break;
}
}
}
以上的那些变量的定义、findViewById、setOnClickListener等等,写起来重复繁琐,我们下面就是要解决这些问题。
流行的解决方法
一、DataBinding
- 没有解决
setOnClickListener的问题,解决了也是很繁琐,需要设置各种Handler
public class StartActivity extends AppCompatActivity implements View.OnClickListener {
private ActivityStartBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_start);
mBinding.tvTest.setText("Text changed!");
mBinding.btnTest.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_test:
Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
break;
}
}
}
二、ButterKnife
- 还是要定义各种变量,当界面比较复杂的时候,代码看起来就没那么好看了
- 在子module中使用会非常繁琐
public class StartActivity extends AppCompatActivity {
@BindView(R.id.tv_test) TextView tv_test;
@BindView(R.id.btn_test) Button btn_test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
ButterKnife.bind(this);
tv_test.setText("Text changed!");
}
@OnClick(R.id.btn_test)
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_test:
Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
break;
}
}
}
三、直接使用 android:onClick="onClick"
- 在
Fragment中设置此属性无效 - 支持库中的组件如
AppCompatButton等等,设置此属性无效
还有很多其它解决方法,这里就不一一列举了。
我的解决方法
- 凡是设置了id的view,直接使用即可,变量名为id名
- 凡是设置了
android:onClick="onClick",在Activity中实现onClick方法即可
@BindActivity(value = R.layout.activity_main, layoutFilename = "activity_main", extendsClass = "android.support.v7.app.AppCompatActivity")
public class MainActivity extends MainActivity_ {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv_test.setText("Text changed!");
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_test:
Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
break;
}
}
}
实现过程
原理是配置一些注解,在编译期间生成相应文件,那些繁琐的代码就放在相应文件中了。
- 定义注解
传入三个参数:布局文件ID、布局文件名、需要继承的类
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface BindActivity {
/**
* layoutResID
*/
int value();
String layoutFilename();
String extendsClass();
}
- 继承
AbstractProcessor类,在编译期间会回调到此类的process方法,在此方法中获取注解,生成相应文件
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Project project = Project.init();
Set<? extends Element> bindActivityElements = roundEnvironment.getElementsAnnotatedWith(BindActivity.class);
for (Element element : bindActivityElements) {
BindActivityHandler handler = new BindActivityHandler(mFiler, mElementUtils);
handler.bind(project, element);
}
Set<? extends Element> bindFragmentElements = roundEnvironment.getElementsAnnotatedWith(BindFragment.class);
for (Element element : bindFragmentElements) {
BindFragmentHandler handler = new BindFragmentHandler(mFiler, mElementUtils);
handler.bind(project, element);
}
return false;
}
读取
settings.gradle文件,获取所有module,在遍历module下面的所有布局文件解释布局文件,获取所有设置了id和设置了onclick的view
private void parserElement(Element element) {
IdView view = null;
for (Iterator<Attribute> it = element.attributeIterator(); it.hasNext(); ) {
Attribute attribute = it.next();
switch (attribute.getQualifiedName()) {
case "android:id":
view = new IdView();
view.setType(element.getName());
view.name = attribute.getValue().substring(5);
idViews.add(view);
break;
case "android:onClick":
// must set android:onClick="onClick"
if (view != null && "onClick".equalsIgnoreCase(attribute.getValue())) {
view.hasOnClick = true;
}
break;
}
}
if (view != null) System.out.println(view.toString());
}
- 根据注解找到对应的布局文件,生成对应的类文件
后会生成一个文件名类似MainActivity_的文件,和MainActivity在同一个目录,大致为build/generated/source/apt/debug/包名
@Override
public void bind(Project project, Element element) {
String _package = getPackageName(element);
String _class = getClassName(element);
BindActivity bindActivity = element.getAnnotation(BindActivity.class);
int layoutResID = bindActivity.value();
String layoutFilename = bindActivity.layoutFilename();
String extendsClass = bindActivity.extendsClass();
Layout layout = project.getLayout(layoutFilename);
if (layout != null) {
try {
JavaFileObject jfo = mFiler.createSourceFile(_package + "." + _class, new Element[]{});
Writer writer = jfo.openWriter();
StringBuilderHelper sb = new StringBuilderHelper();
sb.line("package " + _package + ";");
sb.line();
sb.line("import android.os.Bundle;");
sb.line("import android.support.annotation.Nullable;");
sb.line("import android.view.View;");
sb.line();
sb.line("// Generated code, dot not edit!!!");
sb.line();
if (layout.hasOnClick()) {
sb.line("public abstract class " + _class + " extends " + extendsClass + " implements View.OnClickListener {");
} else {
sb.line("public abstract class " + _class + " extends " + extendsClass + " {");
}
sb.line();
sb.line____("protected final int layoutResID = " + layoutResID + ";");
sb.line();
sb.line____("protected View rootView;");
for (IdView idView : layout.idViews) {
sb.line____("protected " + idView.getType() + " " + idView.name + ";");
}
sb.line();
sb.line____("@Override");
sb.line____("protected void onCreate(@Nullable Bundle savedInstanceState) {");
sb.line________("super.onCreate(savedInstanceState);");
sb.line________("setContentView(layoutResID);");
sb.line();
for (IdView idView : layout.idViews) {
sb.line________(idView.name + " = findViewById(R.id." + idView.name + ");");
}
sb.line();
initOnClick(layout, sb);
sb.line____("}");
sb.line("}");
writer.write(sb.toString());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用方法
比如类为
MainActivity,继承AppCompatActivity,布局文件为R.layout.activity_main
- 设置类注解
@BindActivity(value = R.layout.activity_main,
layoutFilename = "activity_main",
extendsClass = "android.support.v7.app.AppCompatActivity")
- 布局文件设置id属性和onClick属性
<Button
android:id="@+id/btn_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="BUTTON"/>
MakeProject- 修改
MainActivity继承MainActivity_即可
文件结构图

演示效果图

运行代码可能出现的问题
compileSdkVersion 27,可以改成你电脑中存在的SDK版本。
这里用的是 gradle-4.4-all.zip,如果你用的是其它版本,那么可能会下载超级慢,建议改成你电脑中存在的gradle版本,改文件PermissionHelper/gradle/wrapper/gradle-wrapper.properties即可。
其它问题可以直接联系我。一个完全摆脱findViewById的自动绑定库
注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权
一个完全摆脱findViewById的自动绑定库的更多相关文章
- 一个可以将 json 字符串 直接绑定到 view 上的Android库
android-data-binding 这是一个可以将 json 字符串 直接绑定到 view 上的库, 不用先将 json 转换为 model 类. 传送门(https://github.com/ ...
- At.js – 用于 Web 应用程序的自动完成库
At.js 是一个自动完成库,用来实现自动完成提示,表情等,就像你在 Github 或 Twitter 上看到的一样.它支持 HTML5 contentEditable 元素.你可以听任何字符,而不仅 ...
- XamarinAndroid 自动绑定View变量
Android 编程时我们少不了使用FindIdByView函数,在Xamarin Android开发时也需要如此.这个工作很无聊且烦人.在常规Android开发中,人们已经发明了一些方法免除这项工作 ...
- [开源] gnet: 一个轻量级且高性能的 Golang 网络库
Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...
- Kelp.Net是一个用c#编写的深度学习库
Kelp.Net是一个用c#编写的深度学习库 基于C#的机器学习--c# .NET中直观的深度学习 在本章中,将会学到: l 如何使用Kelp.Net来执行自己的测试 l 如何编写测试 l ...
- FlexiGrid 使用 全选、自动绑定
1.介绍 Flexigrid是一个类似于Ext Gird,但基于jQuery开发的Grid.它具有的功能包括:可以调整列宽,合并列标题,分页,排序,显示/隐藏表格等.Flexigrid显示的数据能够通 ...
- php 自动绑定di容器实现
<?php class Bim { public function doSth() { echo __METHOD__.PHP_EOL; } } class Bar { protected $b ...
- Xamarin.Android绑定库分享
使用Xamarin.Android时,会用到各种第三方库,而这些库基本上是java编写的,要在Xamarin.Android中使用这些库,就需要通过Android Binding Project绑定对 ...
- paip.提升效率---request自动绑定domain object
paip.提升效率---request自动绑定domain object #.keyword,subtitle关键字,子标题 ------------------------- 复制request属性 ...
随机推荐
- 【并查集】星球大战starwar
BZOJ1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec Memory Limit: 162 MBSubmit: 6407 Solved: 2973[Su ...
- MySql数据库理解
在之前的面试过程中,有被问到很多次,关于MySQL数据库相关知识,其中有问到了解存储引擎,数据库优化等问题,问得一脸懵X,确实以前在学习的时候没有去深入了解过这一块儿,今天找到了相应的数据库视频,稍稍 ...
- java乱码问题总结
在基于Java的编程中,经常会碰到汉字的处里及显示的问题,比如一大堆乱码或问号. 这是因为JAVA中默认的编码方式是UNICODE,而中国人通常使用的文件和DB都是基于GB2312或者BIG5等编码, ...
- 【OpenJudge9270】【Pku2440】【递推】DNA
DNA [描述] A kind of virus has attacked the X planet, and many lives are infected. After weeks of stud ...
- WPF中的动画——(六)演示图板
前面所介绍的都是单一的动画,它只能修改单一属性.有的时候,我们需要将一组动画一起进行,对于一个按钮,我们可能有如下需求: 选择该按钮时,该按钮增大并更改颜色. 单击该按钮时,该按钮缩小并恢复其原始大小 ...
- Cronz表达式
- 【iOS开发】canOpenURL: failed for URL
控制台输出 如图是在我启动一个 Xcode 7 + iOS 9 的 App 之后,控制台的输出. 这在 Xcode 6.4 + iOS 8 时,是不会有的情况,原因是[为了强制增强数据访问安全, iO ...
- xss编码小结
一.JS编码与HTML编码区分: HTML实体可以使用十进制与十六进制编码:javascript可以使用Unicode与八进制与十六进制进行编码. 二.编码原理区分: 三.编码与非编码 对于JS编码: ...
- 转: ios的关于autolayout的设计与实现
http://www.taijicoder.com/2015/12/12/iOS-Layout-and-Masnory/
- PHP5.3下加速器ZendGuardLoader安装 (LNMP/lnmpa)
PHP5.3下加速器ZendGuardLoader安装 (LNMP/lnmpa) 由于Zend新产品ZendGuardLoader的面世,Zend Optimizer已经不支持php5.3了,官方给出 ...