一个完全摆脱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属性 ...
随机推荐
- 写的模块和方法 wap 和 pc
createjs 画了一个曲线功能 rem 的适配方式 $.fn.stop 方法, zepto 没有的, 对于 2d的旋转 变形 还有 移动都可以停下来, 做动画的属性存储, getComputedS ...
- 【BZOJ 3994】3994: [SDOI2015]约数个数和(莫比乌斯反演)
3994: [SDOI2015]约数个数和 Description 设d(x)为x的约数个数,给定N.M,求 Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试数据的组数. 接 ...
- 2017 icpc 南宁网络赛
2000年台湾大专题...英语阅读输入输出专场..我只能说很强势.. M. Frequent Subsets Problem The frequent subset problem is define ...
- 【找规律】【递推】【二项式定理】Codeforces Round #419 (Div. 1) B. Karen and Test
打个表出来看看,其实很明显. 推荐打这俩组 11 1 10 100 1000 10000 100000 1000000 10000000 100000000 1000000000 1000000000 ...
- 【贪心】POJ2376-Cleaning Shifts
[题目大意] 给出几个小区间和大区间,求覆盖整个大区间的最少小区间个数,如果不可能则输出-1. [思路] 这道程序写得我很不爽快,迷迷糊糊写完了,提交一遍AC了,可是我自己都没怎么弄懂到底是怎么写出来 ...
- ubuntu16安装navicat字体显示不正常,显示方框以及字体倒立
昨天遇到了这个问题,网上找了很多方法都没有真正解决这个问题. 目前其他博客论坛说的主要方法有 1)将安装目录下的./start_navicat中的字符集改为zh_CN.UTF-8 2)将系统的默认字符 ...
- Educational Codeforces Round 9 D. Longest Subsequence dp
D. Longest Subsequence 题目连接: http://www.codeforces.com/contest/632/problem/D Description You are giv ...
- iOS 日志管理异常捕获组件LFLogManager
一.功能 1.分级打印保存 解决一大堆重要.不重要打印信息在控制台混为一团的尴尬局面.可设置仅打印某级别及以上的信息.分为5类打印: DDLogError(@"打印并保存特别重要信息&quo ...
- linq直接执行sql语句
1.ExecuteQuery方法 看命名,我们很容易联想到ado.net里熟悉的Command的ExecuteNonQuery方法,但是VS的智能提示告诉我们这个方法返回的是一个泛型集合,应该&quo ...
- @NotEmpty、@NotBlank、@NotNull区别
@NotEmpty 用在集合类上面 @NotBlank 用在String上面 @NotNull 用在基本类型上