前几天翻看之前下载的各种资料,无意中发现了一款AppWidght应用的源代码,想起之前一直想研究这块,却一直没机会,于是花费了两天时间,把这款桌面电量监控小插件的实现研究了一下,收获颇丰,特此把学到的东西与大家分享。明天就是苦逼的信息论的期末考试了,我是一点看不懂,唉,就这样吧,重修再说吧,我们换个好心情,看一下这款小软件是如何实现的。

虽然这个小软件实现的不错,但是代码质量我却不敢恭维,费了好大劲,才把很多没用的代码和文件剔除,并且对一些实现进行了优化,话不多说,咱们先来看看效果图饱饱眼福。

首先,这是AppWidght显示界面的效果,

下面是在桌面的显示效果

当我们点击这个小电池的时候,会进入下面的界面

在这个界面,我们可以设置以后点击小电池进入的软件,相当于一个快捷图标的功能,当然,若我们不设置,则会默认进入这个界面

下面是当我们点击“要加载的软件”的时候,显示的界面

这个界面有两组应用,一个是精选应用,这个是软件自带的推荐启动界面,我们点击预览,可以打开对应的界面。下面的是其他应用,是指目前手机上带的其他应用,我们点击预览按钮,也可以跳转到对应的软件启动界面,当我们点击的条目的时候,就相当于是修改了下次的启动软件,那么当我们下次点击桌面的小电池的时候,进入的就不是默认的设置界面了,而是我们设置好的启动软件的界面。如果我们想更换启动软件,我们只需要将小电池删除,然后重新添加一次即可重置。

好了,介绍完了这款软件的功能,下面我们就需要了解这样一款软件是如何开发的了。

首先,我们看一下整个工程的目录,让大家有个简单的认识

首先介绍一下各个数字代表的文件的功能

1.后台服务类,用于在后台运行,监听电量的变化情况,当电量发生变化的时候,通知小插件进行电量信息的更新

2.桌面布局更新的帮助类,里面封装了修改桌面电量文字图片显示的方法

3.继承自AppWidgetProvider,这是每个Widght必有的类,里面封装了Widght初始化、更新、销毁等方法,实际上AppWidgetProvider继承自BroadcastReceiver,就是说,桌面插件的状态变化,也是通过广播的形式进行的

4.点击桌面小电池进入的界面

5.设置启动软件的界面

6.不同电量下不同的显示图片

7.桌面小部件和Activity的布局文件

8.arrays存放了精选应用的信息,下面详细介绍

9.用于配置Widght的xml文件,每个Widght都必须有

10.清单文件

好了,介绍完各个文件的基本功能,下面我们就要开始介绍Widght的具体实现了。

首先,我们若要实现一个Widght,我们必须有一个类,继承自AppWidgetProvider,在这里,就是我们的BatteryWidgetProvider类,下面贴代码

BatteryWidgetProvider.java

/*** 实现AppWidgetProvider* * @Time 2014-6-27 下午2:21:02*/
public class BatteryWidgetProvider extends AppWidgetProvider {// 后台服务名private static final String BATTERY_SERVICE_ACTION = "com.bwx.qs.battery.BatteryService";// 当AppWidget被添加到桌面,开始初始化,开启后台监听服务public void onEnabled(Context context) {Intent intent = new Intent(BATTERY_SERVICE_ACTION);context.startService(intent);}// 当AppWidget被用户从界面移除,会调用这个方法public void onDisabled(Context context) {// 停止后台监听服务Intent intent = new Intent(BATTERY_SERVICE_ACTION);context.stopService(intent);// 删除本地的用户配置文件SharedPreferences preferences = context.getSharedPreferences(BatteryWidget.PREFS, Context.MODE_PRIVATE);preferences.edit().remove(BatteryWidget.PREF_ACTIVITY_NAME).commit();}// 当电量发生变化的时候,会调用这个方法,更新界面public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {BatteryService.requestWidgetUpdate(context);}
}

这个类里面有三个很重要的方法,具体功能注释很清楚,就不过多解释了。

除了要实现这样一个类,我们还需要使用xml文件,对我们的Widght进行一些参数的配置,在我们这个项目里面,就是battery_widget_info.xml,下面是代码

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"android:configure="com.bwx.qs.battery.QuickBatteryActivity"android:initialLayout="@layout/battery_widget"android:minHeight="72px"android:minWidth="72px"android:updatePeriodMillis="86400000" ></appwidget-provider>

configure属性是设置点击后显示的Activity信息

initialLayout是设置Widght的布局文件,就是我们在桌面显示的小电池

minHeight、minWidth是设置显示的大小,72px对应的是一个单元格

updatePeriodMillis设置的是更新间隔,为了节省电量,最小间隔是30分钟,小于30分钟按照30分钟计算,我这里设置的是一天,设置为0则为不更新

现在我们的配置文件也有了,我们还需要做什么呢?因为BatteryWidgetProvider继承自AppWidgetProvider,所以也属于一个广播接收者,那么我们就需要在清单文件中进行配置,我们的清单文件的配置信息如下

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.bwx.qs.battery"android:versionCode="1"android:versionName="1" ><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="19" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_icon"android:label="@string/txt_quick_battery" ><receiver android:name=".BatteryWidgetProvider" ><intent-filter><action android:name="android.appwidget.action.APPWIDGET_UPDATE" /></intent-filter><meta-dataandroid:name="android.appwidget.provider"android:resource="@xml/battery_widget_info" /></receiver><serviceandroid:name="com.bwx.qs.battery.BatteryService"tools:ignore="ExportedService" ><intent-filter><action android:name="com.bwx.qs.battery.BatteryService" /></intent-filter></service><activityandroid:name=".SettingsActivityList"android:screenOrientation="portrait" /><activityandroid:name=".QuickBatteryActivity"android:label="@string/txt_quick_battery"android:screenOrientation="portrait"android:theme="@android:style/Theme.NoTitleBar" ><intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><data android:mimeType="com.bwx.qs.battery/widget" /></intent-filter></activity></application></manifest>

我们先不关注其他信息,我们只关注下面这块

<receiver android:name=".BatteryWidgetProvider" ><intent-filter><action android:name="android.appwidget.action.APPWIDGET_UPDATE" /></intent-filter><meta-dataandroid:name="android.appwidget.provider"android:resource="@xml/battery_widget_info" /></receiver>

在这里完成了provider的注册,同时,还需要添加意图过滤器

<intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>

还有一点很重要,就是我们在meta-data节点,将我们的配置信息与BatteryWidgetProvider联系了起来,这样,我们就完成了一个Widght最基本的设置,剩下的就是我们业务方法的完成。

前面说道,在BatteryWidgetProvider的onEnabled方法中,我们开启了后台服务,那么,在后台的服务里面,我们又完成了哪些功能呢?

下面是BatteryService的代码

/*** 后台电量监听服务* * @Time 2014-6-27 下午2:40:03*/
public class BatteryService extends Service {private int mBatteryChargeLevel = -1;//充电器是否连接private boolean mChargerConnected;private ScreenStateService mScreenStateReceiver;public static final String EXT_UPDATE_WIDGETS = "updateWidgets";private static final String TAG = "BatteryService";/*** 电池状态的广播接受者* * @Time 2014-6-27 下午2:42:21*/private class BatteryStateReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();// 如果电池的电量发生变化if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {int rawlevel = intent.getIntExtra("level", -1);int scale = intent.getIntExtra("scale", -1);int level = 0;if (rawlevel >= 0 && scale > 0) {level = (rawlevel * 100) / scale;}mBatteryChargeLevel = level;mChargerConnected = intent.getIntExtra("plugged", 0) > 0&& level < 100;Log.d(TAG, "battery state: level=" + level + ", charging="+ mChargerConnected);}// 更新AppWidgetBatteryWidget.updateWidgets(context, mBatteryChargeLevel,mChargerConnected);}}/*** 屏幕状态监听者,用于屏幕状态发生改变时,将电量的广播接收者进行注册与注销操作,节省电量损耗* * @author Zhao KaiQiang* * @Time 2014-6-27 下午2:48:58*/private class ScreenStateService extends BroadcastReceiver {private BatteryStateReceiver mBatteryStateReceiver;@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (Intent.ACTION_SCREEN_ON.equals(action)) {registerBatteryReceiver(true, context);} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {registerBatteryReceiver(false, context);}}public void registerBatteryReceiver(boolean register, Context context) {// 屏幕亮的时候执行if (register && mBatteryStateReceiver == null) {// 实例化BatteryStateReceiver,并且对BatteryStateReceiver进行注册mBatteryStateReceiver = new BatteryStateReceiver();IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);context.registerReceiver(mBatteryStateReceiver, filter);// 屏幕关闭的时候,将广播接收者注销,节省电量,并将mBatteryStateReceiver置为null} else if (mBatteryStateReceiver != null) {context.unregisterReceiver(mBatteryStateReceiver);mBatteryStateReceiver = null;}}// 对屏幕状态的广播接收者进行注册与注销操作public void registerScreenReceiver(boolean register, Context context) {if (register) {IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction(Intent.ACTION_SCREEN_OFF);context.registerReceiver(this, filter);} else {registerBatteryReceiver(false, context);context.unregisterReceiver(this);}}}//开启服务public void onStart(Intent intent, int startId) {if (mScreenStateReceiver == null) {mScreenStateReceiver = new ScreenStateService();mScreenStateReceiver.registerScreenReceiver(true, this);if (isScreenOn(this)) {mScreenStateReceiver.registerBatteryReceiver(true, this);}}Bundle ext = intent.getExtras();if (ext != null && ext.getBoolean(EXT_UPDATE_WIDGETS, false)) {BatteryWidget.updateWidgets(this, mBatteryChargeLevel,mChargerConnected);}}// 当服务被销毁的时候,注销所有的广播接收者public void onDestroy() {if (mScreenStateReceiver != null) {mScreenStateReceiver.registerScreenReceiver(false, this);mScreenStateReceiver = null;}}@Overridepublic IBinder onBind(Intent intent) {return null;}// 开启后台监听服务public static void requestWidgetUpdate(Context context) {Intent serviceIntent = new Intent(context, BatteryService.class);serviceIntent.putExtra(EXT_UPDATE_WIDGETS, true);context.startService(serviceIntent);}// 判断当前屏幕状态private static boolean isScreenOn(Context context) {PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);int sdkVersion = Build.VERSION.SDK_INT;try {// >= 2.1if (sdkVersion >= 7) {Boolean bool = (Boolean) PowerManager.class.getMethod("isScreenOn").invoke(pm);return bool.booleanValue();} else {// < 2.1Field field = PowerManager.class.getDeclaredField("mService");field.setAccessible(true);Object service = field.get(pm);Long timeOn = (Long) service.getClass().getMethod("getScreenOnTime").invoke(service);return timeOn > 0;}} catch (Exception e) {Log.e(TAG, "cannot check whether screen is on", e);return true;}}
}

因为后台服务类是功能的主要实现类,因此代码比较多,大家慢慢看,我稍微介绍下。

在后台服务类里面,定义了两个广播接收者,一个是监听屏幕状态的ScreenStateService,一个是监听电池电量状态的BatteryStateReceiver。

在开启后台服务之后,在onStart方法中,进行了ScreenStateService的广播注册,同时,若屏幕状态为亮,则也将BatteryStateReceiver进行注册。在BatteryStateReceiver的onReceive方法中,若发生了电量变化的事件,则调用BatteryWidget.updateWidgets()方法,对桌面的小电池的背景图片和文字进行修改,具体实现,我们一会在看。值得注意的是,在获取屏幕状态的isScreenOn方法中,使用到了反射机制,来获取当前屏幕状态,还是第一次见。

当服务开启之后,若屏幕亮着,就会注册两个广播接收者,若有电量修改,对桌面的电池图片进行修改。那么,当Widght被从桌面移除的时候,会调用什么方法呢?

在BatteryWidgetProvider的onDisabled方法,就是处理当Widght被用户从桌面移除的时候的处理逻辑,在这个方法里面,将我们的后台服务stop同时删除我们的配置文件,这个配置文件是用来记录我们设置的启动软件的,暂时还没介绍到。在服务的stop发生了什么呢?通过代码可以发现,在服务的stop方法中,完成了两个广播接收者的注销操作。

到目前为止,整个软件的实现思路应该已经很清楚了。用户添加Widght-->开启后台服务-->注册屏幕监听和电量监听事件-->电量发生变化-->通知桌面Widght更新电量显示的图片和文字。

那么,下面我们要介绍的,就是如何实现的Widght的图片和文字的更改。

首先,我们看一下小电池的布局是怎么实现的

battery_appearance.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"><ImageViewandroid:id="@+id/battery"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:clickable="true"android:layout_marginTop="1dp"android:contentDescription="@string/todo"android:src="@xml/level_icons" /><TextViewandroid:id="@+id/capacity_center"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@null"android:layout_gravity="center"android:textColor="#FF6EB4"android:textSize="16sp"android:textStyle="bold"/><ImageViewandroid:id="@+id/lightning"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="top|right"android:contentDescription="@string/todo"android:src="@drawable/ic_lightning"android:visibility="visible" /></FrameLayout>

布局很简单,就是FrameLayout包裹着的三个控件,battery用于显示背景电量图片,capacity_center用于显示电量文本,lightning用于显示小闪电

指的注意的是,小电池的布局被单独抽取了出来,因为在两个地方用到了这个布局,一个是battery_widget.xml(这个是Widght在桌面的显示布局),一个是configuration.xml(这个是点击Widght之后的显示首页面,效果图中左上角的小电池用到了小电池的布局)。

下面,我们重点看一下,到底是如何修改Widght的布局显示的。负责修改布局的是BatteryWidget类,下面是代码

/*** 电量小部件类* * @Time 2014-6-27 下午3:52:30*/
public class BatteryWidget {//用于在sharedpreferences中存放配置信息public static final String PREFS = "common";public static final String PREF_PACKAGE_NAME = "package";public static final String PREF_CLASS_NAME = "class";public static final String PREF_ACTIVITY_NAME = "name";private static PendingIntent pendingIntent;private static final String MIME = "com.bwx.qs.battery/widget";// 根据chargeLevel,改变显示的图片public static void updateWidgets(Context context, int chargeLevel,boolean chargerConnected) {// 当chargeLevel<10,组拼成0X的形式String level = chargeLevel < 10 ? "0" + chargeLevel : String.valueOf(chargeLevel);// 创建RemoteViews控件RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.battery_widget);pendingIntent = getPendingIntent(context);views.setOnClickPendingIntent(R.id.battery, pendingIntent);//设置显示图片views.setInt(R.id.battery, "setImageLevel", chargeLevel);views.setTextViewText(R.id.capacity_center, level);// 当电量是100时,隐藏小闪电和电量文字views.setViewVisibility(R.id.capacity_center,chargeLevel < 100 ? View.VISIBLE : View.GONE);views.setViewVisibility(R.id.lightning, chargerConnected ? View.VISIBLE: View.GONE);AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);ComponentName componentName = new ComponentName(context,BatteryWidgetProvider.class);widgetManager.updateAppWidget(componentName, views);}private static PendingIntent getPendingIntent(Context context) {SharedPreferences prefs = context.getSharedPreferences(PREFS,Context.MODE_PRIVATE);String name = prefs.getString(PREF_ACTIVITY_NAME, null);if (name == null) {Intent intent = new Intent(Intent.ACTION_VIEW);intent.setType(MIME);return PendingIntent.getActivity(context, 0, intent, 0);} else {String className = prefs.getString(PREF_CLASS_NAME, null);String packageName = prefs.getString(PREF_PACKAGE_NAME, null);Intent intent = new Intent();intent.setClassName(packageName, className);return PendingIntent.getActivity(context, 0, intent, 0);}}}

在这里更改Widght的显示,需要用到RemoteViews类,在这段代码里面,有两个地方需要说一下,一个是

views.setOnClickPendingIntent(R.id.battery, pendingIntent);

这句话就是给battery对应的Imageview设置点击跳转事件,但是这里用的不是Intent对象,而是一个PendingIntent对象。PendingIntent对象是对intent的包装,是延迟的intent,在getPendingIntent方法中,根据配置文件中是否有已有的配置信息来判断到底是往哪里跳转。若有,则跳转到对应的软件界面,若没有,则发送一个隐式意图,将默认的界面打开。

另外一个,则是

views.setInt(R.id.battery, "setImageLevel", chargeLevel);

这句代码的意思就是,根据chargeLevel的大小,给battery对应的Imageview设置对应的图片。在这里有个参数 "setImageLevel",这里又使用到了反射,setImageLevel是Imageview的方法,下面是源代码中的解释

 /*** Sets the image level, when it is constructed from a * {@link android.graphics.drawable.LevelListDrawable}.** @param level The new level for the image.*/@android.view.RemotableViewMethodpublic void setImageLevel(int level) {mLevel = level;if (mDrawable != null) {mDrawable.setLevel(level);resizeFromDrawable();}}

这个方法应该只是在RemoteViews才可以使用,布局文件中,battery是这样设置的

<ImageViewandroid:id="@+id/battery"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:clickable="true"android:layout_marginTop="1dp"android:contentDescription="@string/todo"android:src="@xml/level_icons" />

src属性直接设置的level_icons文件,那么在这个文件里面,是怎么实现的呢?

level_icons.xml

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android" ><itemandroid:drawable="@drawable/ic_2_8"android:maxLevel="1"android:minLevel="0"/><itemandroid:drawable="@drawable/ic_2_7"android:maxLevel="15"android:minLevel="2"/><itemandroid:drawable="@drawable/ic_2_6"android:maxLevel="20"android:minLevel="16"/><itemandroid:drawable="@drawable/ic_2_5"android:maxLevel="30"android:minLevel="21"/><itemandroid:drawable="@drawable/ic_2_4"android:maxLevel="40"android:minLevel="31"/><itemandroid:drawable="@drawable/ic_2_3"android:maxLevel="60"android:minLevel="41"/><itemandroid:drawable="@drawable/ic_2_2"android:maxLevel="80"android:minLevel="61"/><itemandroid:drawable="@drawable/ic_2_1"android:maxLevel="100"android:minLevel="81"/></level-list>

在这个文件里面,定义了传入不同数值范围时,显示的图片,因此,使用这种方式,就完成了桌面Widght的图片背景的替换,我可是第一次见到这样使用。

背景图片如下

现在终于实现了背景图片的更换了,那么下面,我们就介绍最后一个功能的实现了,那就是设置启动软件的实现。

实现这个功能的类是SettingActivityList,下面是代码实现

/*** 设置启动应用* */
public class SettingsActivityList extends ExpandableListActivity implementsOnClickListener {private PackageManager mPackageManager;private ExpandableListAdapter mAdapter;private ArrayList<FeaturedActivity> mFeaturedActivities = new ArrayList<FeaturedActivity>();private String[] mFeaturedClassNames;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mAdapter = new ExpandableListAdapter(getLayoutInflater());setListAdapter(mAdapter);mPackageManager = getPackageManager();new CollectActivitiesTask().execute();getExpandableListView().setItemsCanFocus(true);}// 应用分组的内部类private class Group {private int titleTextId;private ArrayList<ResolveInfo> children;Group(int _titleTextId) {titleTextId = _titleTextId;children = new ArrayList<ResolveInfo>();}}// 精选应用的内部类private class FeaturedActivity {private String className;private String packageName;FeaturedActivity(String _className, String _packageName) {className = _className;packageName = _packageName;}}// ExpandableListView的适配器class ExpandableListAdapter extends BaseExpandableListAdapter {private ArrayList<Group> mGroups;private LayoutInflater mInflater;public ExpandableListAdapter(LayoutInflater inflater) {mInflater = inflater;mGroups = new ArrayList<Group>();init();}//将xml文件中的精选应用的信息加载进来private void init() {String[] params;FeaturedActivity activity;ArrayList<String> classNames = new ArrayList<String>();String[] activities = SettingsActivityList.this.getResources().getStringArray(R.array.featured_activities);for (int i = 0; i < activities.length; i++) {params = TextUtils.split(activities[i], "/");mFeaturedActivities.add(activity = new FeaturedActivity(params[1], params[0]));classNames.add(activity.className);}mFeaturedClassNames = classNames.toArray(new String[classNames.size()]);Arrays.sort(mFeaturedClassNames);}public Object getChild(int groupPosition, int childPosition) {return mGroups.get(groupPosition).children.get(childPosition);}public long getChildId(int groupPosition, int childPosition) {return childPosition;}public View getChildView(int groupPosition, int childPosition,boolean isLastChild, View convertView, ViewGroup parent) {if (convertView == null) {convertView = mInflater.inflate(R.layout.activity_item, parent,false);}Group group = mGroups.get(groupPosition);ActivityInfo activityInfo = group.children.get(childPosition).activityInfo;ImageView icon = (ImageView) convertView.findViewById(R.id.icon);TextView text1 = (TextView) convertView.findViewById(R.id.text1);Button button = (Button) convertView.findViewById(R.id.button1);button.setEnabled(true);button.setTag(activityInfo);button.setOnClickListener(SettingsActivityList.this);View item = convertView.findViewById(R.id.item);item.setTag(activityInfo);item.setOnClickListener(SettingsActivityList.this);icon.setImageDrawable(activityInfo.loadIcon(mPackageManager));text1.setText(activityInfo.loadLabel(mPackageManager));return convertView;}public int getChildrenCount(int groupPosition) {return mGroups.get(groupPosition).children.size();}public Object getGroup(int groupPosition) {return mGroups.get(groupPosition);}public int getGroupCount() {return mGroups.size();}public long getGroupId(int groupPosition) {return groupPosition;}public View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {if (convertView == null) {convertView = mInflater.inflate(android.R.layout.simple_expandable_list_item_1, parent,false);}Group group = mGroups.get(groupPosition);((TextView) convertView.findViewById(android.R.id.text1)).setText(group.titleTextId);return convertView;}public boolean hasStableIds() {return true;}public boolean isChildSelectable(int groupPosition, int childPosition) {return true;}}// 异步任务,用于获取手机的应用信息class CollectActivitiesTask extends AsyncTask<Void, Group, Void> {@Overrideprotected Void doInBackground(Void... params) {// 获取精选应用组信息Group group = new Group(R.string.txt_recommended);Intent intent = new Intent();FeaturedActivity activity;for (int i = 0; i < mFeaturedActivities.size(); i++) {activity = mFeaturedActivities.get(i);intent.setClassName(activity.packageName, activity.className);List<ResolveInfo> infos = mPackageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);for (ResolveInfo info : infos) {group.children.add(info);}}// 添加数据并更新if (group.children.size() > 0) {mAdapter.mGroups.add(group);mAdapter.notifyDataSetChanged();}// 获取其他应用组信息group = new Group(R.string.txt_other);Intent queryIntent = new Intent(Intent.ACTION_MAIN);queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);List<ResolveInfo> list = mPackageManager.queryIntentActivities(queryIntent, 0);Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager));String className;for (ResolveInfo item : list) {className = item.activityInfo.name;int index = Arrays.binarySearch(mFeaturedClassNames, className);if (index < 0) {group.children.add(item);}}// 添加数据并更新if (group.children.size() > 0) {mAdapter.mGroups.add(group);mAdapter.notifyDataSetChanged();}return null;}}// 点击事件public void onClick(View view) {// 如果选择的是item条目,则将选择的启动软件的配置信息保存if (view.getId() == R.id.item) {ActivityInfo activityInfo = (ActivityInfo) view.getTag();SharedPreferences prefs = getApplication().getSharedPreferences(PREFS, MODE_PRIVATE);prefs.edit().putString(PREF_CLASS_NAME, activityInfo.name).putString(PREF_PACKAGE_NAME, activityInfo.packageName).putString(PREF_ACTIVITY_NAME,activityInfo.loadLabel(mPackageManager).toString()).commit();finish();} else {// 如果选择是预览按钮,就打开对应软件ActivityInfo activityInfo = (ActivityInfo) view.getTag();String packageName = activityInfo.packageName;String className = activityInfo.name;Intent intent = new Intent();intent.setClassName(packageName, className);startActivity(intent);}}
}

这些代码应该不算很难懂,因为经过我整理了 = =。。之前的代码看的我都蛋疼,代码格式各种混乱!

这个界面内容分了两个部分,一个是精选应用,一个是其他应用,精选应用是固定的,有一个单独的xml文件负责存储这些应用的信息,arrays.xml,代码如下

<?xml version="1.0" encoding="utf-8"?>
<resources><string-array name="featured_activities"><item>com.bwx.bequick/com.bwx.bequick.ShowSettingsActivity</item><item>com.android.settings/com.android.settings.BatteryInfo</item><item>com.android.settings/com.android.settings.fuelgauge.PowerUsageSummary</item><item>com.android.settings/com.android.settings.battery_history.BatteryHistory</item><item>com.android.settings/com.android.settings.UsageStats</item><item>com.android.settings/com.android.settings.TestingSettings</item><item>com.android.settings/com.android.settings.RadioInfos</item></string-array></resources>

这里面存放的这些页面的包名和类名,然后在代码中用/进行了分解。

到此为止,我们就完成了整个小软件的介绍。这个软件虽然不大,不过确实有很多的地方是第一次接触,扩展眼界了。

AppWidght全面学习之电量监控小部件的实现详解相关推荐

  1. 【Android界面实现】AppWidght全面学习之电量监控小部件的实现详解

    前几天翻看之前下载的各种资料,无意中发现了一款AppWidght应用的源代码,想起之前一直想研究这块,却一直没机会,于是花费了两天时间,把这款桌面电量监控小插件的实现研究了一下,收获颇丰,特此把学到的 ...

  2. android--电量监控小部件

    原文转自:http://blog.csdn.net/zhaokaiqiang1992/article/details/35791047 前几天翻看之前下载的各种资料,无意中发现了一款AppWidght ...

  3. 笔记 | 百度飞浆AI达人创造营:深度学习模型训练和关键参数调优详解

    笔记 | 百度飞浆AI达人创造营:深度学习模型训练和关键参数调优详解 针对特定场景任务从模型选择.模型训练.超参优化.效果展示这四个方面进行模型开发. 一.模型选择 从任务类型出发,选择最合适的模型. ...

  4. 深度学习模型训练和关键参数调优详解

    深度学习模型训练和关键参数调优详解 一.模型选择 1.回归任务 人脸关键点检测 2.分类任务 图像分类 3.场景任务 目标检测 人像分割 文字识别 二.模型训练 1.基于高层API训练模型 加载数据集 ...

  5. python逢7跳过_python实现逢七拍腿小游戏的思路详解

    逢七拍腿游戏 几个小朋友在一起玩逢七拍腿的游戏,从1开始数数,当数到7的倍数或者尾号是7时,拍一下腿.现在从1数到99,假设每个人都没有错,计算一下共要拍腿几次? 第一种实现思路:通过在for循环语句 ...

  6. 深度学习之目标检测(十一)--DETR详解

    深度学习之目标检测(十一)-- DETR详解 目录 深度学习之目标检测(十一)-- DETR详解 1. 前言 2. DETR 框架 2.1 CNN Backbone 2.2 Transformer E ...

  7. python拍七游戏代码_python实现逢七拍腿小游戏的思路详解

    逢七拍腿游戏 几个小朋友在一起玩逢七拍腿的游戏,从1开始数数,当数到7的倍数或者尾号是7时,拍一下腿.现在从1数到99,假设每个人都没有错,计算一下共要拍腿几次? 第一种实现思路:通过在for循环语句 ...

  8. 【Azure 架构师学习笔记】-Azure Data Factory (4)-触发器详解-事件触发器

    本文属于[Azure 架构师学习笔记]系列. 本文属于[Azure Data Factory]系列. 接上文[Azure 架构师学习笔记]-Azure Data Factory (3)-触发器详解-翻 ...

  9. 大数据单机学习环境搭建(5)Hive建表DDL详解

    专题:大数据单机学习环境搭建和使用 1. Hive建表简单示例 1.1.Hive建表语句 1.2.表详细信息 1.3.数据展示 2. Hive建表语法详解 3.拓展1:复杂数据分割 4.拓展2:事务表 ...

最新文章

  1. 报名丨西山金融科技产业创新论坛邀您参会
  2. mysql查询不同老师所教不同课程_mysql学习训练记录及笔记(二)
  3. Array with Odd Sum(CF-1296A)
  4. Windows编译libcaffe时报cudnn.hpp(114): too few arguments in function call错误
  5. jetty9优化的两处地方
  6. python从零学——scrapy初体验
  7. 红黑树与平衡二叉树_大佬用这近百张图来给我解释红黑树,看完直接跪了!
  8. Linux——tmux和vim常用命令总结(必会)
  9. 【Altera SoC体验之旅】+ 正式开启OpenCL模式
  10. 商用密码产品认证-数字证书认证系统
  11. win10备份为wim_玩转一键自动还原,强大你的win10系统
  12. 第11章 枚举与泛型
  13. Graph U-Nets [gPool gUnpool] 图分类 节点分类 图池化 ICML 2019
  14. 计算机快速启动BIOS,掌握50个电脑BIOS启动快捷键,再也不为装系统发愁了!
  15. Java到底能干什么?
  16. 计算机应用基础课程作业2016,2016浙大远程教育计算机应用基础作业
  17. 输出复杂的菱形(续)
  18. Python防微信撤回
  19. Java doc转docx
  20. 【ICCV2019 reid】Self-training with progressive augmentation for unsupervised cross-domain person reid

热门文章

  1. 爬虫之bs4、xpath数据解析(案例—scrapy获取菜鸟HTML页面数据)
  2. oracle数据库系统电子版,Oracle数据库系统原理(第2版)
  3. 计算机文化基础形考作业5客观题答案,电大计算机本科形考
  4. 马斯克晒出Twitter系统架构图
  5. 林冲说道 酷狗软件下载
  6. 无线网络安全之ARP欺骗攻击
  7. USB Type-C转HDMI+USB3.0+PD快充方案扩展坞设计方案资料|带PD快充USB-C转HDMI+USB3.0多功能扩展坞电路参考|多功能扩展坞设计资料
  8. 2022高压电工考试题库及答案
  9. 盘点了109个金融行业活动案例,找到了最常用的10种
  10. 基于SSM家庭理财管理系统的设计与实现【毕业设计项目】