一个完全摆脱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属性 ...
随机推荐
- nyoj(表达式求值)
描述 ACM队的mdd想做一个计算器,但是,他要做的不仅仅是一计算一个A+B的计算器,他想实现随便输入一个表达式都能求出它的值的计算器,现在请你帮助他来实现这个计算器吧. 比如输入:"1+2 ...
- CSS 笔记——选择器
1. 选择器 (1)类型选择器(标签选择器) 基本语法 E { sRules } 使用说明 类型选择器.以文档对象(Element)类型作为选择器. 选择面较大,适合做某种标签元素外观的常规设置. 代 ...
- 【枚举】【并查集】Gym - 101243F - Vitamins
题意:有n片药,有三种颜色,白色比红色重,红色比蓝色重,给你一些它们之间的重量关系,比如1>3,2=4之类,问你它们的颜色,如果没法判断的输出?. 先并查集把等于号全缩起来,然后按照大于号建图, ...
- sql 空值设置默认值
ifnull(a.discountsign, ') AS "discountsign"
- [转]115个Java面试题和答案——终极列表(上)
本文我们将要讨论Java面试中的各种不同类型的面试题,它们可以让雇主测试应聘者的Java和通用的面向对象编程的能力.下面的章节分为上下两篇,第一篇将要讨论面向对象编程和它的特点,关于Java和它的功能 ...
- hibernate处理视图问题(记录)
Mark,在使用hibernate处理视图的时候.因为视图没有主键,这个用Myeclipse自动生成的POJO类就有两个.一个类名.java,一个是类名Id.java,而映射文件只有一个.因此造成一个 ...
- iOS 日志管理异常捕获组件LFLogManager
一.功能 1.分级打印保存 解决一大堆重要.不重要打印信息在控制台混为一团的尴尬局面.可设置仅打印某级别及以上的信息.分为5类打印: DDLogError(@"打印并保存特别重要信息&quo ...
- <摘录>MBR和分区表
MBR 意思是主引导记录.位于硬盘的0柱面.0磁头.1扇区. MBR 大小为1个扇区,512字节. 下面是MBR的结构: 偏移 构 ...
- Run-Time Check Failure #0
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is ...
- [Android实例] 推荐给你们一个好用的ListView、RecyclerView适配器
https://github.com/vihuela/RecyclerViewHelpper 如果用过RecyclerView的人都知道,高度不会包裹,然后写法好像也不是很简洁,甚至点击事件不好设 置 ...