重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
绑定Android点击事件有三种方式:
创新互联是一家专业提供湛江企业网站建设,专注与成都做网站、成都网站制作、H5网站设计、小程序制作等业务。10年已为湛江众多企业、政府机构等服务。创新互联专业网站设计公司优惠进行中。
一、匿名内部类
1、通过资源ID找到对应的Android控件,比如R.id.button1
Button button1 = (Button)findViewById(R.id.button1);
2、调用setOnClickListener方法,将点击事件与之绑定
button1.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
//这里放点击事件的逻辑
}
});
二、XML申明式
1、在对应的布局文件中添加一个按钮,即Button
2、在Button中添加 onClick="foo()" 属性
3、在对应的Activity中编写对应的foo()方法,如下:
public void foo(View v){
//这里放点击事件的逻辑
//这里的 " v " 是指当前组件,就是你点击的这个按钮
}
三、统一处理式
1、使对应的Activity类实现View.OnClickListener 接口,也就是这样写:
public class xxActivity implement View.OnClickListener {
2、在方法中重载onClick(View v)方法
public void onClick(View v){
3、通过 v.getId() 来判断,来源是哪一个按钮,这里可以放一个switch开关语句,来进行逻辑的分工。
}
}
纯手打,希望上文能对你起到一定的辅助作用。
在Android Studio 2.1 Preview 3之后,官方开始支持双向绑定了。
可惜目前Google并没有在Data Binding指南里面加入这个教程,并且在整个互联网之中只有 这篇文章 介绍了如何使用反向绑定。
在阅读一下文章之前,我假设你已经知道如何正向绑定。
在正向绑定中,我们在Layout里面的绑定表达式是这样的:
当user.name的数据改动时,我们的TextView都会同步改变文字。
现在假设一种情况,当你更换成EditText时,如果你的用户名User.name已经绑定到 EditText 中,当用户输入文字的时候,你原来的user.name数据并没有同步改动,因此我们需要修改成:
看出微小的差别了吗?对,就是"@{}"改成了"@={}",是不是很简单?
同样你也可以在别的View上引用属性:
当CheckBox的状态发生改变的时候,ImageView也会同时发生改变。在复杂情况下,这个特性没什么卵用,因为逻辑部分我们是不建议写在XML中。
开启双向绑定,需要在项目的build.gradle中设置:
同样,你需要在你Module的build.gradle中设置:
我们刚才的例子里面只显示了系统自带的应用,那么如果是自定义控件,或者是我们更细颗粒度的 Observable 呢?等下就揭晓如何自定义自己的双向绑定,我们来看看目前Android支持的控件:
设想一下我们使用了下拉刷新 SwipeRefreshLayout 控件,这个时候我们希望在加载数据的时候能控制refreshing的状态,所以我们加入了 ObservableBoolean 的变量swipeRefreshViewRefreshing来正向绑定数据,并且能够在用户手动下拉刷新的时候同步更新swipeRefreshViewRefreshing数据:
接下来我们需要告诉框架,我们需要将 SwipeRefreshLayout 的isRefreshing的值反向绑定到 swipeRefreshViewRefreshing :
这是一种简单的定义,其中event和method都不是必须的,因为系统会自动生成,写出来是为了更好地了解如何绑定的,可以参考官方文档 InverseBindingMethod 。
当然你也可以使用另外一种写法,并且如果你的值并不是直接对应 Observable 的值的时候,就可以在这里进行转换:
上面的event同样也不是必须的。以上的定义都是为了让我们能够在布局文件中使用"@={}"这个双向绑定的特性。接下来你需要告诉框架如何处理refreshingAttrChanged事件,就像处理一般的监听事件一样:
一般情况下,我们都需要设置正常的OnRefreshListener,所以我们可以合并写成:
现在我们终于可以使用双向绑定的技术啦。但是要注意,需要设置 requireAll = false ,否则系统将识别不了refreshingAttrChanged属性,前文提到的文章例子里并没有设置这个。
在ViewModel中,我们的数据是这样的:
在布局文件中是这样设置的:
最后我们还有一个小问题,就是双向绑定有可能会出现死循环,因为当你通过Listener反向设置数据时,数据也会再次发送事件给View。所以我们需要在设置一下避免死循环:
这样就没问题啦。
最近封装了几个View,都和生命周期有关,比如BannerView、和我们公司的ARView,那他们的生命周期怎么绑定呢?刚开始我是直接告诉别人,你自己在哪个activity使用的,就在哪个activity绑定,结果使用的时候被甲方对接人员打回来说这是低级处理方式,wtm这暴脾气,谁tm低级了,但是没办法别人是甲方只能听他的,直接将生命周期的绑定绑在view里面。下面就介绍几个常用的生命周期绑定。
Application.ActivityLifecycleCallbacks是Android自带的生命周期绑定接口,他有注册与解除方法,当我们进入一个activity的时候就可以注册绑定他了。
简单demo。比如我们要讲一个自定义的View绑定生命周期。
1、先定义一个Application.ActivityLifecycleCallbacks的实现类,为什么这样呢?是因为用户可以要什么选什么,就不用被迫每次都实现全部。
2、定义一个测试用的view,在构造方法绑定注册生命周期,
((Activity)context).getApplication().registerActivityLifecycleCallbacks(lifecycleCallbacks);这句话是注册生命周期当然他也对应了一个((Activity)context).getApplication().unregisterActivityLifecycleCallbacks(lifecycleCallbacks);解绑方法。
在ActivityLifecycleCallbacks 的实现中判断了返回的activity是否为这个view所在的acrivity因为很多activity都会走这个方法,如果不做判断,其他activity的生命周期也会影响这个view,所以加判断是个好习惯。
这次我们模拟一个普通类绑定生命周期这个类名叫Test,哈哈,是不是很高大上?但是写Test类之前先定义我们要绑定的生命周期接口LifecycleListener
这里我们监听下常用的onResume、onPause、onDestroy。
然后是Test类
然后写LifecycleDetector,尝试将test类的实例与fragment绑定
定义没有布局的fragment实例
然后保存fragment和Requestmanager关系
还有一个遗漏的接口
上面这些完成后,就可以使用了,我们随便搞个activity试试名字就叫LeftDemoActivity非常好听了有没有!
当你运行起来的时候,就是成功的时候。
有个东西忘啦,getSnapshot是做保存的,直接把源码拿过来的。
还有其他的方法我没试过,但是这样的生命周期绑定可以解决挺多bug的,玩过哔哩哔哩的应该会遇到他们的一个bug,就是进入一个视频播放页,然后还没加载完成就息屏,他这个时候异步回调回来视频数据了就会在息屏或锁屏界面播出音频来,这是典型的生命周期bug,我遇到过几次了,如果加入这个绑定轻松就能解决问题啦。
我们已经了解了BroadcastReceiver的原理,我们再来看看四大组件之一的Service是怎么启动的,以及怎么运行的原理。
如果遇到什么问题可以来到 本文下进行交流
启动Service的入口就是startService和bindService方法。我们先来看看startService在ContextImpl中做了什么。
文件:/ frameworks / base / core / java / android / app / ContextImpl.java
此时调用的就是AMS的startService方法。
mServices是一个ActiveServices对象。这个对象是在AMS的构造函数中初始化好的。
这里调用了ActiveServices的startServiceLocked。
文件:/ frameworks / base / services / core / java / com / android / server / am / ActiveServices.java
核心流程有如下三个:
注意这里addToStarting是一个比较关键的判断,addToStarting默认为false。
如果此时不是启动前台服务,则需要进一步进行处理。如果ProcessRecord为空或者curProcState大于PROCESS_STATE_RECEIVER这个优先级数值;也就是优先级更小。
为了避免此时App应用是没有任何的前台ui,或者App应用还没有声明。避免有的App通过startService进行应用的包活或者拉起应用。就会进行如下能够存在的最大后台服务数量,则放入mDelayedStartList中进行延时启动后台服务,现在直接返回了。
不然则说明能够允许启动后台服务, 就设置为addToStarting为true。
通过ComponentName也就是包名和类名查找ServiceRecord;通过Intent意图过滤找到ServiceRecord。·
核心方法是bringUpServiceLocked。如果bringUpServiceLocked返回了异常,就返回一个特殊的ComponentName对象。
addToStarting为true,说明此时是一个能够启动的后台服务,则ServiceRecord添加到mStartingBackground中。如果mStartingBackground的数量为0,则直接调用ServiceMap的rescheduleDelayedStartsLocked启动后台服务。
这几个关键的步骤,让我们依次的考察,先来看看scheduleCreateService中做了什么。
如果第一次启动就走第一个if的分支:
能看到和BroadcastReceiver的ANR思路一样,通过一个延时的Handler,如果达到时间了还没有移除这个Handler消息则报ANR异常。
这里根据启动前台和后台分为两种超时时间:
前台分别是10秒,后台服务不属于后台进程组(判断adj是否在SCHED_GROUP_BACKGROUND)是20秒,后台服务属于后台进程组 200秒。
把数据封装成CreateServiceData后,通过Handler调用如下方法:
透三点的核心原理可以看我写的的Application创建和BroadcastReceiver原理两篇文章。来看看Service中都做了什么?
很简单就是保存了传递过来的参数,值得注意的是这个IBinder对象其实就是指ServiceRecord对象。
接着执行Service.onCreate这个空实现的方法。
本质上还是调用了ActiveServices的serviceDoneExecutingLocked方法。
type是SERVICE_DONE_EXECUTING_ANON,所不会做更多的处理。 最后执行了serviceDoneExecutingLocked方法。
这个方法不断的循环遍历ListServiceStartArgs分发SERVICE_ARGS消息,这个消息通过主线程的Looper调用handleServiceArgs。
bindService在开发中用的不是很多,这里稍微提一下他的使用。
首先申明一个ServiceConnection对象,用于绑定服务端的Service。
调用bindService的方法绑定到某个Service中。当服务端的service成功回调onBind方法,我们只需要返回对应的Binder对象。就能使用调用bindService的客户端在ServiceConnection的onServiceConnected的回调中获得Binder对象。
之后就能通过这个Binder调用本进程或者其他进程的的方法了。实际上我们可以把这个过程看成一个多对多的服务-客户端模型。多个客户端通过Binder向多服务端Service通信,每一次我们都可以通过ComponentName判断不同服务返回来的Binder对象。
这里面很简单,和BroadcastReceiver的思路很像。动态注册的BroadcastReceiver会封装成一个ReceiverDispatcher,而这里把ServiceConnection封装成LoadedApk.ServiceDispatcher对象。
并且会把ServiceDispatcher作为value,ServiceConnection作为key缓存一个map中。并且以context为key,把这个临时的map作为value缓存起来。这样一个Context就映射有了多个ServiceDispatcher对象,也就可以注册多个监听被绑定Service状态的监听者了。
CoAP(Constrained Application Protocol)也叫做“受限应用协议”是一种在物联网世界的类web协议,它的详细规范定义在 RFC 7252。参考如下两篇博客学习更多关于CoAP信息。
CoAP简介
CoAP协议使用
Andlink是中国移动提出的一套设备接入和管理的协议,实现方式复杂,在此不做过多描述,该协议可进行设备与网关之间通信,从而实现设备绑定和管控。Andlink采用CoAP或MQTT作为通信协议。
1、APP进入网关绑定页面,确认wifi是否开启(如果未开启,提示用户开启wifi并绑定到与网关相同wifi下
2、进入网关绑定页后,APP在局域网中每隔1秒发送/qlink/searchdevice的广播,同时调用全屋平台startBind接口
3、收到网关反馈,确认产品id与网关一致后,向网关发送配网信息,注意这里"CGW"地址随开发测试环境不同改变
4、网关入网成功后,会发送/qlink/success的广播消息
5、网关绑定成功后,会发送/qlink/regist的广播消息,包含设备ID
6、后台给APP推送一条网关绑定成功的消息,接收到该消息后,跳出绑定成功界面