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 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来更新按钮状态