版权声明:本文为博主原创文章,未经博主同意不得转载。

https://blog.csdn.net/bz419927089/article/details/35791047

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

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

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhb2thaXFpYW5nMTk5Mg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

以下是在桌面的显示效果

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhb2thaXFpYW5nMTk5Mg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

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

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

这个界面有两组应用,一个是精选应用。这个是软件自带的推荐启动界面,我们点击预览,能够打开相应的界面。

以下的是其它应用,是指眼下手机上带的其它应用,我们点击预览按钮。也能够跳转到相应的软件启动界面。当我们点击的条目的时候,就相当于是改动了下次的启动软件,那么当我们下次点击桌面的小电池的时候。进入的就不是默认的设置界面了,而是我们设置好的启动软件的界面。

假设我们想更换启动软件,我们仅仅须要将小电池删除。然后又一次加入一次就可以重置。

好了,介绍完了这款软件的功能,以下我们就须要了解这样一款软件是怎样开发的了。

首先,我们看一下整个project的文件夹,让大家有个简单的认识

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

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-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" /> <application
android: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-data
android:name="android.appwidget.provider"
android:resource="@xml/battery_widget_info" />
</receiver> <service
android:name="com.bwx.qs.battery.BatteryService"
tools:ignore="ExportedService" >
<intent-filter>
<action android:name="com.bwx.qs.battery.BatteryService" />
</intent-filter>
</service> <activity
android:name=".SettingsActivityList"
android:screenOrientation="portrait" />
<activity
android: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-data
android: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 {
@Override
public 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);
}
// 更新AppWidget
BatteryWidget.updateWidgets(context, mBatteryChargeLevel,
mChargerConnected);
}
} /**
* 屏幕状态监听者,用于屏幕状态发生改变时,将电量的广播接收者进行注冊与注销操作,节省电量损耗
*
* @author Zhao KaiQiang
*
* @Time 2014-6-27 下午2:48:58
*/
private class ScreenStateService extends BroadcastReceiver { private BatteryStateReceiver mBatteryStateReceiver; @Override
public 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;
} } @Override
public 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.1
if (sdkVersion >= 7) {
Boolean bool = (Boolean) PowerManager.class.getMethod(
"isScreenOn").invoke(pm);
return bool.booleanValue();
} else {
// < 2.1
Field 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"
> <ImageView
android: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" /> <TextView
android: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"
/> <ImageView
android: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>

布局非常easy,就是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.RemotableViewMethod
public void setImageLevel(int level) {
mLevel = level;
if (mDrawable != null) {
mDrawable.setLevel(level);
resizeFromDrawable();
}
}

这种方法应该仅仅是在RemoteViews才干够使用,布局文件里,battery是这样设置的

<ImageView
android: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" > <item
android:drawable="@drawable/ic_2_8"
android:maxLevel="1"
android:minLevel="0"/>
<item
android:drawable="@drawable/ic_2_7"
android:maxLevel="15"
android:minLevel="2"/>
<item
android:drawable="@drawable/ic_2_6"
android:maxLevel="20"
android:minLevel="16"/>
<item
android:drawable="@drawable/ic_2_5"
android:maxLevel="30"
android:minLevel="21"/>
<item
android:drawable="@drawable/ic_2_4"
android:maxLevel="40"
android:minLevel="31"/>
<item
android:drawable="@drawable/ic_2_3"
android:maxLevel="60"
android:minLevel="41"/>
<item
android:drawable="@drawable/ic_2_2"
android:maxLevel="80"
android:minLevel="61"/>
<item
android:drawable="@drawable/ic_2_1"
android:maxLevel="100"
android:minLevel="81"/> </level-list>

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

背景图片例如以下

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

实现这个功能的类是SettingActivityList。以下是代码实现

/**
* 设置启动应用
*
*/
public class SettingsActivityList extends ExpandableListActivity implements
OnClickListener { 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> { @Override
protected 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>

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

到此为止。我们就完毕了整个小软件的介绍。这个软件尽管不大。只是确实有非常多的地方是第一次接触。扩展眼界了。

点击下载

【Android界面实现】AppWidght全面学习之电量监控小部件的实现具体解释的更多相关文章

  1. Android界面View及ViewGroup学习 《转载》

    View及ViewGroup类关系 Android View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的. View ...

  2. 家庭版记账本app进度之关于android界面布局的相关学习

    1.线性布局(linearlayout)是一种让视图水平或垂直线性排列的布局线性布局使用<LinearLayout>标签进行配置对应代码中的类是android.widget.LinearL ...

  3. ArcGIS API for JavaScript 4.2学习笔记[18] 搜索小部件

    这个例子很简单,作为开学后(暴露出学生党的本质)的开胃菜是再合适不过了. 不过,博主提前警告一下:接下来的例子会相当的长.烦.难.我还会用"引用"-"函数参数骨架&quo ...

  4. ArcGIS API for JavaScript 4.2学习笔记[19] 搜索小部件——使用更多数据源

    上一篇中提到,空间搜索小部件是Search这个类的实例化,作为视图的ui属性添加进去后,视图就会出现搜索框了. 这节的主体代码和上篇几乎一致,区别就在上篇提及的sources属性. 先看看结果: 由于 ...

  5. Android界面布局基本知识简述

    Android手机操作系统在模拟器中进行相关的编写,可以帮助我们实现各种功能需求.尤其是在界面的操作方面显得更为突出.在这里我们就可以对Android界面布局的相关操作来对这方面的知识进行一个深入的了 ...

  6. android的helloworld工程目录学习

    android的helloworld工程目录学习 Android工程的主要目录有src.gen.Android X.X.bin.res等文件夹. 1.     Src文件夹 Src文件夹包含java源 ...

  7. android界面设计之布局管理

    谈到android界面设计,各种布局样式不得不提!传统的布局方式有6种,我们会一一介绍. 在android studio2.2版本之后出现了一款超棒的布局方式,真正意义上的所见即所得,后面我们也会讲到 ...

  8. Android开发技术周报183学习记录

    Android开发技术周报183学习记录 教程 Android性能优化来龙去脉总结 记录 一.性能问题常见 内存泄漏.频繁GC.耗电问题.OOM问题. 二.导致性能问题的原因 1.人为在ui线程中做了 ...

  9. 让你大开眼界的10款Android界面设计

    根据调查显示, iOS与Android的市场份额差距正越来越大.Android设备正在成为手机应用市场的主力军.如何从设计层面创造一个优美的app界面来吸引用户已然成为广大App开发者们必做的功课之一 ...

随机推荐

  1. codeforces 521a//DNA Alignment// Codeforces Round #295(Div. 1)

    题意:如题定义的函数,取最大值的数量有多少? 结论只猜对了一半. 首先,如果只有一个元素结果肯定是1.否则.s串中元素数量分别记为a,t,c,g.设另一个串t中数量为a',t',c',g'.那么,固定 ...

  2. Golang中defer、return、返回值之间执行顺序的坑

    原文链接:https://studygolang.com/articles/4809 Go语言中延迟函数defer充当着 cry...catch 的重任,使用起来也非常简便,然而在实际应用中,很多go ...

  3. SourceTree

    MAC上最好的GIT免费GUI工具是SourceTree(没有之一).此外,最好的GIT代码开源网站是GitHub,最好的GIT代码私有库是BitBucket https://www.sourcetr ...

  4. sql 2005 代码导入excel数据

     select * into bm from OpenDataSource( 'Microsoft.ACE.OLEDB.12.0', 'Data Source="G:\bm.xls" ...

  5. Java中List的排序方法

    方法一:实现Comparable接口 package com.java.test; public class Person implements Comparable<Person> { ...

  6. iOS UI-UIPickerView(拾取器)、UIWebView(网页视图)和传值方式

    // // ViewController.m // IOS_0107_finalToolClass // // Created by ma c on 16/1/7. // Copyright (c) ...

  7. dubbo监控中心搭建

    从网上下载了一个dubbo监控中心,地址忘了,文件名是dubbo-monitor-simple-2.5.3-assembly.tar.gz. 修改监控中心配置文件如下: dubbo.container ...

  8. HeaderExchangeClient

    HeaderExchangeClient 注释是DefaultMessageClient,类中定义了心跳定时器HeaderExchangeChannel 发送请求HeaderExchangeHandl ...

  9. Deep Belief Network简介——本质上是在做逐层无监督学习,每次学习一层网络结构再逐步加深网络

    from:http://www.cnblogs.com/kemaswill/p/3266026.html 1. 多层神经网络存在的问题 常用的神经网络模型, 一般只包含输入层, 输出层和一个隐藏层: ...

  10. 用正则表达式匹配用rdf3x处理过后的TTL格式文档

    1.比如下面这个用rdf3x处理过后的TTL文档片段: 注意缩进的是两个空格 <http://rdf.ebi.ac.uk/resource/chembl/target/CHEMBL2363853 ...