开发自己的山寨Android注解框架

news/2024/7/7 21:25:26

目录

  • 开发自己的山寨Android注解框架

开发自己的山寨Android注解框架

参考

Github黄油刀

Overview

在上一章我们学习了Java的注解(Annotation),但是我想大家可能感觉,虽然理解了也会学会,但是不知道干什么用,那么请继续忍受我这枯燥乏味的文风继续向下看吧。

在下面我们将会模仿(山寨)一把 黄油刀 。

第零步

我想许多 Android Coder 都非常讨厌findViewById 这种操作,既乏味无趣有没有代码的优雅感,实在是让人厌恶至极。 我们著名的黄油刀框架就是为了解决这一问题的。

优雅的黄油刀
class ExampleActivity extends Activity {
//通过注解找到我们想要的控件@BindView(R.id.user) EditText username;@BindView(R.id.pass) EditText password;@BindString(R.string.login_error) String loginErrorMessage;
//绑定OnClick事件@OnClick(R.id.submit) void submit() {// TODO call server...}@Override public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.simple_activity);ButterKnife.bind(this);// TODO Use fields...}
}

优雅的代码扑面而来~

我们就是准备模仿这么一个东西,当然我们要做出来的东西,肯定远不及人家,可能就有人会说,既然已经有了这么好的框架,为什么我们还有做一些重复造轮子的工作呢?

对此我想说,我们不能做一个只会用别人的轮子的开发者,总归我们是要能够造出来自己的轮子的。

第一步,我们要实现的东西

我们在这里要实现两个功能

  1. 通过注解,来替换掉,以前的findViewByid的操作
  2. 通过注解,来完成绑定OnCLick事件的操作。
编程思路

实现该功能的思路如下:

  1. 在Activity/Fragment初始化的时候,进行绑定操作
  2. 绑定操作后,通过遍历所有的字段和方法
  3. 遍历字段,找出所有的被注解标记的字段
  4. 根据注解中的值,找到相应的View,然后赋值给字段
  5. 遍历所有的方法,找出被注解的方法
  6. 通过注解的值找到对应的View
  7. 为找到的View注册事件
  8. 回收占用的资源

第二步,做准备工作

建立项目

首先建立一个项目,我叫他为Finder ,在项目中建立一个FinderLibrary Module, 我们会在这个Model中写代码。

创建我们需要的注解

@BindView 注解,用于绑定视图

/*** 用于绑定视图的注解*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {int value();
}

@ClickEvent注解,用于注册OnClick事件

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickEvent {int[] value();
}

主体代码

Finder类的主要方法

建立一个Find类来写我们的核心代码。下面的代码中列出了finder类中的主要的方法,后面我们会一步步地实现他们。

public final class Finder {/*** 私有化构造方法,禁止从外部创建对象*/private Finder() throws Exception {throw new Exception("Invalidate constructor");}/*** 用于存储所有的已经绑定的对象的集合*/static Map<Unbinder, View> Pot = new HashMap<>();/*** 用于绑定活动*/public static Unbinder bind(@NonNull Activity target) {}/*** 用于绑定碎片*/public static Unbinder bind(Fragment target) {}private static void apply(Unbinder unbinder) {}/*** 绑定View,为标记的字段*/private static void bindView(Object holder, View view) throws Exception {}/*** 绑定OnClick方法*/private static void bindMethod(Object holder, View view) throws Exception {}/*** 为View绑定ONClick事件*/private static void bindOnClickListener(final Object holder, View view, final Method method) {}/*** 从集合释放掉占用的对象*/public static void unbind(Unbinder unbinder) {}
}
Unbinder类

这个类的作用很简单,当我们在Activity/Fragment中绑定了Finder以后,会返回一个此对象,那么当Activity/Fragment 生命周期结束的时候,通过此对象释放掉Finder所占用的资源。

package little_david.finderlibrary;public class Unbinder {public Unbinder(Object c) {this.holder = c;}/*** 当前Unbinde对象的持有者*/Object holder;/*** 解绑操作*/public void unbind() {Finder.unbind(this);}
}
bind方法

在此方法中我们需要获取Activity/Fragment的View,通过调用apply 方法,并将其存储起来。

/*** 用于绑定活动*/
public static Unbinder bind(@NonNull Activity target) {Unbinder unbinder = new Unbinder(target);//获取Activity的顶级布局,并存储Pot.put(unbinder, target.getWindow().getDecorView());apply(unbinder);return unbinder;
}
/*** 用于绑定碎片*/
public static Unbinder bind(Fragment target) {Unbinder unbinder = new Unbinder(target);//获取Fragment的顶级布局,并存储Pot.put(unbinder, target.getView());apply(unbinder);return unbinder;
}
apply方法

通过此方法来调用,最主要的两个核心方法。这个方法并没有什么特殊,只是起到了一个过渡的作用。

private static void apply(Unbinder unbinder) {try {//视图对象View view = Pot.get(unbinder);bindView(unbinder.holder, view);bindMethod(unbinder.holder, view);} catch (Exception e) {e.printStackTrace();}
}
bindView 方法

在此方法中做的事情是,遍历被注解了的字段,然后根据注解中的值,进行反射赋值。

/*** 绑定View,为标记的字段*/
private static void bindView(Object holder, View view) throws Exception {//拿到持有者的ClassClass cls = holder.getClass();//获取到所有的字段Field[] fields = cls.getFields();//遍历字段for (Field field : fields) {//过滤没有被@BindView标记的字段if (!field.isAnnotationPresent(BindView.class))continue;//获取我们注解的详细的对象,并赋值BindView bindView = field.getAnnotation(BindView.class);int viewResId = bindView.value();View targetView = view.findViewById(viewResId);//为字段赋值field.set(holder, targetView);}
}
bindMethod

在此方法中遍历所有的方法,找出被注解标识的方法并且满足我们要求的方法,作为View的OnClick事件的处理方法。

/*** 绑定OnClick方法*/
private static void bindMethod(Object holder, View view) throws Exception {Class cls = holder.getClass();Method[] methods = cls.getMethods();for (Method method : methods) {//过滤没有被注解的方法if (!method.isAnnotationPresent(ClickEvent.class))continue;//获取方法参数Class[] parameterClsArray = method.getParameterTypes();//根据方法参数过滤,过滤被注解了但是不合法的方法if (parameterClsArray.length != 1 || parameterClsArray[0] != View.class)continue;//绑定点击事件ClickEvent event = method.getAnnotation(ClickEvent.class);/*** 因为@ClickEvent注解是支持多选的,所以我们需要遍历所有的值来进行设置OnClick事件操作* */for (int resId : event.value()) {View eventView = view.findViewById(resId);bindOnClickListener(holder, eventView, method);}}
}/*** 为View绑定ONClick事件*/
private static void bindOnClickListener(final Object holder, View view, final Method method) {view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {try {//执行方法method.invoke(holder, v);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}});
}
unbind

此方法的左右就是释放掉所占用的资源。

/*** 从集合释放掉占用的对象*/
public static void unbind(Unbinder unbinder) {unbinder.holder = null;Pot.remove(unbinder);
}

第三步, 实验我们自己框架

应用于Activity

public class MainActivity extends AppCompatActivity {@BindView(R.id.btn1)Button btn1;@BindView(R.id.btn2)Button btn2;private Unbinder mUnbinder;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//进行绑定操作mUnbinder = Finder.bind(this);btn1.setText("Hello world!");}/*** 可以绑定多个View*/@ClickEvent({R.id.btn1, R.id.btn2})public void btn1Click(View view) {Toast.makeText(this, "My id is: " + view.getId(), Toast.LENGTH_SHORT).show();}/*** 进行解绑操作,释放资源*/@Overrideprotected void onDestroy() {mUnbinder.unbind();super.onDestroy();}
}

应用于Fragment

public class TestFragment extends Fragment {private Unbinder mUnbinder;@BindView(R.id.btn3)Button btn3;@BindView(R.id.tvTest)TextView tvTest;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_test, container, false);}/*** 绑定*/@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);mUnbinder = Finder.bind(this);}/*** 释放资源*/@Overridepublic void onDestroyView() {mUnbinder.unbind();super.onDestroyView();}@ClickEvent(R.id.btn3)public void btn3Click(View view) {Toast.makeText(getContext(), "Btn3.text=" + this.btn3.getText().toString(), Toast.LENGTH_SHORT).show();}
}

源码下载

虽然,我们做的这个框架现在还漏洞百出,但是,我们已经跨出了我们造轮子的第一步,总有一天我们也会能够写出非常好的轮子让别人用。

好了,现在本节的内容已经结束了,我想还是比较容易理解的,毕竟没什么难度。源码已经上传至github,欢迎大家吐槽。

https://github.com/1258730808/Finder

转载于:https://www.cnblogs.com/slyfox/p/8677380.html


http://lihuaxi.xjx100.cn/news/238503.html

相关文章

Android发布项目到外部仓库

为什么80%的码农都做不了架构师&#xff1f;>>> 关于android发布仓库到外部项目的文章&#xff0c;目前大概为分两类&#xff1a; 一种是发布到bintray、jcenter上&#xff0c;一种是将项目上传到github&#xff0c;然后利用jitpack自动打包&#xff0c;下面简单说…

哈希函数是什么,在区块链中有什么用

想知道更多关于区块链技术知识&#xff0c;请百度【链客区块链技术问答社区】 链客&#xff0c;有问必答&#xff01;&#xff01;哈希函数是什么&#xff1f; 哈希函数&#xff0c;又叫散列函数、散列算法&#xff0c;是一种从任何一种数据中创建小的数字“指纹”&#xff08;…

Web漏洞扫描(一:利用WVS进行漏洞扫描)

任务一、利用WVS进行漏洞扫描 1.1、Acunetix WVS 的下载与安装&#xff1b; 1.1.1、WVS的安装&#xff08;按照图中的指示执行&#xff09;&#xff1b; 1.1.2、勾选“Create a desktop shortcut”创建桌面图标&#xff0c;然后点击“Next”&#xff0c;选择“Install”&#…

如何在JavaScript中检测用户的首选配色方案

by Oskar Hane由Oskar Hane 如何在JavaScript中检测用户的首选配色方案 (How to detect a user’s preferred color scheme in JavaScript) In recent versions of macOS (Mojave) and Windows 10, users have been able to enable a system level dark mode. This works well…

以太坊系列之账户管理

想知道更多关于区块链技术知识&#xff0c;请百度【链客区块链技术问答社区】 链客&#xff0c;有问必答&#xff01;&#xff01;账户 以太坊有两种账户类型&#xff1a; 外部账户&#xff08;EOA&#xff09; 合约账户 所有账户的状态代表以太坊网络的状态&#xff0c;以太坊…

玩转“网上邻居”之网络配置(二)

接上篇二、混合系统网络目前实际应用中单一系统的对等网络已不是主流&#xff0c;主要是由于这种网络性能较低&#xff0c;缺乏很好看安全及网络管理能力。目前在各企事业单位中普遍应用的还是基于多系统的域结构混合网络。但因目前主流应用的系统比较多&#xff0c;所以各种网…

win2d 图片水印

win2d 图片水印 本文告诉大家如何使用 win2d 给图片加上水印。 安装 首先需要使用 Nuget 安装 win2d &#xff0c;安装参见win10 uwp win2d 如果没有更新 dot net core 那么在运行可能会出现下面异常System.TypeLoadException: Requested Windows Runtime type Microsoft.Graph…

Web漏洞扫描(二:Windows server2008 R2操作系统(部署dvwa))

在Windows server 2008 R2系统中部署dvwa; 1、在Windows server 2008虚拟机中配置IIS&#xff1b; 1.1、打开服务器管理器&#xff0c;角色&#xff0c;添加角色&#xff0c;然后点击下一步&#xff1b; 1.2、选择安装“Web 服务器&#xff08;IIS&#xff09;”&#xff0c;…