一个完全摆脱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属性 ...
随机推荐
- HashMap的实现原理 HashMap底层实现,hashCode如何对应bucket?
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 数组和链表组合成的链表散列结构,通过hash算法,尽量将数组中的数据分布均匀,如果has ...
- 「PKUSC2018」星际穿越 (70分做法)
5371: [Pkusc2018]星际穿越 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 27 Solved: 11[Submit][Status] ...
- 【动态规划】Round Subset
CF837D. Round Subset Let's call the roundness of the number the number of zeros to which it ends. Yo ...
- 【hdu1280】前M大的数
前m大的数 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submi ...
- bzoj 1734: [Usaco2005 feb]Aggressive cows 愤怒的牛
1734: [Usaco2005 feb]Aggressive cows 愤怒的牛 Description Farmer John has built a new long barn, with N ...
- Android程序设计
Android程序设计-1 要求安装 Android Stuidio 完成Hello World, 要求修改res目录中的内容,Hello World后要显示自己的学号,提交代码运行截图和码云Git链 ...
- 实验三 敏捷开发与XP实践实验报告
实验三 敏捷开发与XP实践实验报告 实验内容 1. XP基础 2. XP核心实践 3. 相关工具 实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vi ...
- JavaScript之引用类型(Object类型)
ECMAScript提供了很多原生的引用类型,以便开发人员进行常见的计算任务. 对象是某一个特定引用类型的的实例. Object类型 用的最多.虽然这个Object实例不具备多少功能,但是在应用程序的 ...
- 推荐一套WPF主题皮肤
在CodePlex上发现了一套WPF的主题皮肤,直接应用于系统自带的控件,一共有四种配色方案,做得也还比较精致.感兴趣的朋友可以使用一下.点击下载 另外一套我比较喜欢的皮肤这里也推荐一下吧: ...
- U-Boot添加menu命令的方法及U-Boot命令执行过程
转;http://chenxing777414.blog.163.com/blog/static/186567350201141791224740/ 下面以添加menu命令(启动菜单)为例讲解U-Bo ...