├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── checkstyle-idea.xml ├── codeStyles │ └── Project.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── vienan.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ ├── app-release.apk │ └── output.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ditclear │ │ └── app │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ditclear │ │ │ └── app │ │ │ ├── MainActivity.java │ │ │ ├── MainActivityKT.kt │ │ │ ├── MainViewModel.java │ │ │ ├── MainViewModelKT.kt │ │ │ ├── aop │ │ │ ├── annotation │ │ │ │ └── SingleClick.java │ │ │ └── aspect │ │ │ │ └── SingleClickAspect.java │ │ │ └── list │ │ │ ├── DummyItem.java │ │ │ └── ListActivity.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_list_white_24dp.png │ │ ├── drawable-mdpi │ │ └── ic_list_white_24dp.png │ │ ├── drawable-xhdpi │ │ └── ic_list_white_24dp.png │ │ ├── drawable-xxhdpi │ │ └── ic_list_white_24dp.png │ │ ├── drawable-xxxhdpi │ │ └── ic_list_white_24dp.png │ │ ├── layout │ │ ├── activity_list.xml │ │ ├── activity_main.xml │ │ ├── activity_main_kt.xml │ │ └── list_item.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── ditclear │ └── app │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── release.jks ├── screenshot.gif ├── screenshot_list.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ditclear/DataBinding-AspectJ/d4315441f571cdfdf16da7f6a24eeee837a9356a/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/vienan.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 35 | 36 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 27 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Android 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 1.8 65 | 66 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 写在前面 2 | 3 | 最近一直在找时间重构代码,每一次重构都能带来许多好处,比如精简代码,提高代码质量,减轻团队之间的问题,当然最重要的就是以后可以偷懒啦。而这次改进也是为了节省时间,提高团队的效率。 4 | 5 | #### 先体验一下效果 6 | 7 | ![1.gif](https://github.com/ditclear/DataBinding-AspectJ/blob/master/screenshot.gif?raw=true) 8 | 9 | 10 | ![2.gif](https://github.com/ditclear/DataBinding-AspectJ/blob/master/screenshot_list.gif?raw=true) 11 | 12 | 13 | #### DataBinding 14 | 15 | 不了解的请百度,google或翻看以前的文章 16 | 17 | #### AspectJ 18 | 19 | 贴一张图: 20 | ![](http://upload-images.jianshu.io/upload_images/3722695-37a95292f78dc566?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 21 | 22 | 简单理解就是在编译期和加载时进行代码注入。通俗点来说就是非侵入的在一段方法前后加入一点自己的逻辑。作用和OKhttp的Interceptor拦截请求有点像。 23 | 24 | 最开始认识到AspectJ的时候是在今年二月份,在github搜索databinding的时候偶遇[**north2016/T-MVP**](https://github.com/north2016/T-MVP),这个项目太赞了,给了我相当多的启发,也算是我android-AOP的启蒙。 25 | 26 | 27 | 28 | **想了解AOP的推荐文章:** 29 | 30 | [安卓AOP三剑客:APT,AspectJ,Javassist](http://www.jianshu.com/p/dca3e2c8608a) 31 | 32 | [专题:AndroidAOP](http://www.jianshu.com/nb/1529181) 33 | 34 | *PS:以前上学的时候学SSH的时候有接触过AOP面向切面编程,虽然都忘了,但是思想还在(大学真该好好学,感谢泡图书馆的日子)* 35 | 36 | ### 防止多次点击 37 | 38 | 现在来说说,防止多次点击。 39 | 40 | 以前使用的是J神的Rxbinding库,结合`throttleFirst`操作符来进行控制。 41 | 42 | 由于使用的是DataBinding库,一般来说事件是绑定在xml当中的,但为了限制频繁触发和使用Rxbinding,还是在代码里进行事件处理。 43 | 44 | ```java 45 | RxView.clicks(view).throttleFirst(600, TimeUnit.MILLISECONDS).subscribe(...); 46 | ``` 47 | 48 | 这样可以处理普通布局中的点击事件,但RecyclerView中的Item就不那么好处理了,需要获取ItemBinding找到view去做以上的操作,很麻烦。 49 | 50 | 为了节约时间(偷懒),提高效率(偷懒),加入AspectJ就势在必行了。 51 | 52 | #### 加入AspectJ,给你的DataBinding插上翅膀 53 | 54 | 如何在项目中使用AspecJ呢? 55 | 56 | 1. 使用大神们提供的脚本,android10写过一个[Android-AOPExample](https://github.com/android10/Android-AOPExample) 57 | 58 | 的库,并提供了一段脚本,可以作为参考。 59 | 60 | 2. 使用沪江的[**gradle_plugin_android_aspectjx**](https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx) 61 | 62 | 本文推荐使用第二种,比较简单方便。 63 | 64 | 接入方法可以看ReadMe或者徐医生的这篇文章[看AspectJ在Android中的强势插入](http://www.jianshu.com/p/5c9f1e8894ec) 65 | 66 | #### 讲重点 67 | 68 | 配置好AspectJ后,我们需要给点击事件加一个切入点,首先添加一个注解 69 | 70 | ```java 71 | @Retention(RetentionPolicy.CLASS) 72 | @Target(ElementType.METHOD) 73 | public @interface SingleClick { 74 | } 75 | ``` 76 | 77 | 然后编写我们的Aspect类(代码来自north2016) 78 | 79 | ```java 80 | /** 81 | * Created by baixiaokang on 16/12/9. 82 | * {link https://github.com/north2016/T-MVP/blob/master/app/src/main/java/com/aop/SingleClickAspect.java} 83 | * 防止View被连续点击,间隔时间600ms 84 | */ 85 | 86 | @Aspect 87 | public class SingleClickAspect { 88 | 89 | public static final String TAG="SingleClickAspect"; 90 | public static final int MIN_CLICK_DELAY_TIME = 600; 91 | static int TIME_TAG = R.id.click_time; 92 | 93 | @Pointcut("execution(@com.ditclear.app.aop.annotation.SingleClick * *(..))")//方法切入点 94 | public void methodAnnotated(){ 95 | 96 | } 97 | 98 | @Around("methodAnnotated()")//在连接点进行方法替换 99 | public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable{ 100 | View view=null; 101 | for (Object arg: joinPoint.getArgs()) { 102 | if (arg instanceof View) view= ((View) arg); 103 | } 104 | if (view!=null){ 105 | Object tag=view.getTag(TIME_TAG); 106 | long lastClickTime= (tag!=null)? (long) tag :0; 107 | if (BuildConfig.DEBUG) { 108 | Log.d(TAG, "lastClickTime:" + lastClickTime); 109 | } 110 | long currentTime = Calendar.getInstance().getTimeInMillis(); 111 | if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {//过滤掉600毫秒内的连续点击 112 | view.setTag(TIME_TAG, currentTime); 113 | if (BuildConfig.DEBUG) { 114 | Log.d(TAG, "currentTime:" + currentTime); 115 | } 116 | joinPoint.proceed();//执行原方法 117 | } 118 | } 119 | } 120 | } 121 | 122 | ``` 123 | 124 | 接下来是使用 125 | 126 | 先看看**MainActivity** 127 | 128 | ```java 129 | public class MainActivity extends AppCompatActivity { 130 | 131 | private ActivityMainBinding mBinding; 132 | 133 | private MainViewModel mViewModel; 134 | 135 | @Override 136 | protected void onCreate(Bundle savedInstanceState) { 137 | super.onCreate(savedInstanceState); 138 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); 139 | mViewModel = new MainViewModel(); 140 | mBinding.setVm(mViewModel); 141 | } 142 | } 143 | ``` 144 | 145 | 很简单,初始化binding,设置了viewModel 146 | 147 | **MainViewModel** 148 | 149 | 150 | 151 | ```java 152 | package com.ditclear.app; 153 | 154 | import android.databinding.BaseObservable; 155 | import android.databinding.ObservableField; 156 | import android.view.View; 157 | 158 | import com.ditclear.app.aop.annotation.SingleClick; 159 | 160 | /** 161 | * 页面描述:viewmodel 162 | * 163 | * Created by ditclear on 2017/8/12. 164 | */ 165 | 166 | public class MainViewModel extends BaseObservable { 167 | 168 | public ObservableField normalText=new ObservableField<>(""); 169 | 170 | public ObservableField hookText=new ObservableField<>(""); 171 | 172 | 173 | /** 174 | * 普通的点击事件 175 | * @param view view 176 | */ 177 | public void onNormalClick(View view) { 178 | 179 | normalText.set(String.format("%s click\n",normalText.get())); 180 | } 181 | 182 | /** 183 | * 防止多次点击 184 | * @param view view 185 | */ 186 | @SingleClick 187 | public void onHookClick(View view) { 188 | hookText.set(String.format("%s click\n",hookText.get())); 189 | } 190 | } 191 | ``` 192 | 193 | 普通点击事件和需要拦截的方法以@SingleClick注解来区分,由于需要在SingleClickAspect类中拦截view 参数,获取点击时间,所以需要传递view参数 194 | 195 | ```java 196 | @Around("methodAnnotated()")//在连接点进行方法替换 197 | public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable{ 198 | View view=null; 199 | for (Object arg: joinPoint.getArgs()) { 200 | if (arg instanceof View) view= ((View) arg); 201 | } 202 | if (view!=null){ 203 | ... 204 | } 205 | } 206 | ``` 207 | 208 | **activity_main.xml** 209 | 210 | ```xml 211 | 217 | 218 |