1、General
抽象类StateTracker
每个按钮实现一个StateTracker的子类(亮度按钮特殊处理不实现该类):
WifiStateTracker
BluetoothStateTracker
GpsStateTracker
SyncStateTracker
2、创建流程:
1、将PowerSave widget 拖出来后,系统调用回调onUpdate
// Update each requested appWidgetId
RemoteViews view = buildUpdate(context);
for (int i = 0; i <appWidgetIds.length; i++) {
appWidgetManager.updateAppWidget(appWidgetIds[i], view);
}
appWidgetManager.updateAppWidget(appWidgetIds[i],view)为调用系统函数不做解释。
BuildUpdate:
1、创建一个RemoteViews 指定layout
views =new RemoteViews(context.getPackageName(),R.layout.widget);
2、设置按键响应
views.setOnClickPendingIntent(R.id.btn_wifi,getLaunchPendingIntent(context,BUTTON_WIFI))
views.setOnClickPendingIntent(R.id.btn_wifi,getLaunchPendingIntent(context,BUTTON_BT))
………………..
3、获取各种Wifi BT等状态,更新图标
updateButtons
4、Done
updateButtons
sWifiState.setImageViewResources(context,views);
sBluetoothState.setImageViewResources(context,views);
……
setImageViewResources为抽象父类实现。
1、 获取按钮和按钮下面指示器的id
getButtonId()、getIndicatorId()此两者子类负责实现,提供给父类id信息
2、 获取指示器的位置(左中右),父类默认为中、wifi为重写该方法,返回左
3、 getTriState获取开、关、切换中三种状态 该方法调用子类的getActualState方法,负责返回所代表部件的状态。PS: getActualState返回的是五种状态之一:开、关、正在开、正在关、未知状态(异常)。getTriState将五种状态转换成三种状态,以供指示器显示。
4、 根据以上三步收集的信息 调用views.setImageViewResource绘出按钮以及指示器
创建流程至此结束。
2、widget如何响应按钮事件
由于widget里面是RemoteViews,所以没有OnClickListener,只能通过intent发送给Host来执行
先来看下创建是设置的getLaunchPendingIntent,
launchIntent.addCategory(Intent.CATEGORY_ALTERNATIVE);
launchIntent.setData(Uri.parse("custom:" + buttonId));
PendingIntent pi =PendingIntent.getBroadcast(context, 0 /* no requestCode */,
launchIntent, 0 /* no flags*/);
可以看出,intent携带了buttonId的数据来区别到底是哪个按钮事件,然后getBroadcast表明,按键事件将通过broadcast事件来通知widget。在onReceive中
else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)){
Uri data = intent.getData();
int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
if(buttonId == BUTTON_WIFI) {
sWifiState.toggleState(context);
}
else if (buttonId == BUTTON_BRIGHTNESS) {
toggleBrightness(context);
}else if (buttonId == BUTTON_SYNC) {
sSyncState.toggleState(context);
}
}
ToggleState
1、获取当前部件的状态。getTriState,根据当前状态确定是否需要进行状态切换
2、如果需要状态切换,调用子类的requestStateChange,子类负责切换所代表部件的状态。由于切换状态需要与硬件、网络交互。耗时长,所以内部使用AsyncTask进行异步切换
3、widget如何感知外部状态的变化
当用户在设置里面更改了Wifi、BT等状态,widget如何能及时知道,并修改按钮的图片
当外部状态变化时,widget的onReceive将收到消息,如果该部件不是通过broadcast来发送消息的,那么就注册一个Observer来监视该部件的变化(如亮度按钮)
根据intent识别是哪个部件发生变化
String action =intent.getAction();
if(WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
sWifiState.onActualStateChange(context,intent);
} else if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
sBluetoothState.onActualStateChange(context, intent);
}
………………..
updateWidget(context);
子类调用onActualStateChange,由于是外部发生变化,子类不需要其他操作,所以onActualStateChange实现没做什么事情。
updateWidget(context);内部调用buildUpdate来更新按钮状态