【Android界面实现】AppWidght全面学习之电量监控小部件的实现具体解释
版权声明:本文为博主原创文章,未经博主同意不得转载。
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全面学习之电量监控小部件的实现具体解释的更多相关文章
- Android界面View及ViewGroup学习 《转载》
View及ViewGroup类关系 Android View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的. View ...
- 家庭版记账本app进度之关于android界面布局的相关学习
1.线性布局(linearlayout)是一种让视图水平或垂直线性排列的布局线性布局使用<LinearLayout>标签进行配置对应代码中的类是android.widget.LinearL ...
- ArcGIS API for JavaScript 4.2学习笔记[18] 搜索小部件
这个例子很简单,作为开学后(暴露出学生党的本质)的开胃菜是再合适不过了. 不过,博主提前警告一下:接下来的例子会相当的长.烦.难.我还会用"引用"-"函数参数骨架&quo ...
- ArcGIS API for JavaScript 4.2学习笔记[19] 搜索小部件——使用更多数据源
上一篇中提到,空间搜索小部件是Search这个类的实例化,作为视图的ui属性添加进去后,视图就会出现搜索框了. 这节的主体代码和上篇几乎一致,区别就在上篇提及的sources属性. 先看看结果: 由于 ...
- Android界面布局基本知识简述
Android手机操作系统在模拟器中进行相关的编写,可以帮助我们实现各种功能需求.尤其是在界面的操作方面显得更为突出.在这里我们就可以对Android界面布局的相关操作来对这方面的知识进行一个深入的了 ...
- android的helloworld工程目录学习
android的helloworld工程目录学习 Android工程的主要目录有src.gen.Android X.X.bin.res等文件夹. 1. Src文件夹 Src文件夹包含java源 ...
- android界面设计之布局管理
谈到android界面设计,各种布局样式不得不提!传统的布局方式有6种,我们会一一介绍. 在android studio2.2版本之后出现了一款超棒的布局方式,真正意义上的所见即所得,后面我们也会讲到 ...
- Android开发技术周报183学习记录
Android开发技术周报183学习记录 教程 Android性能优化来龙去脉总结 记录 一.性能问题常见 内存泄漏.频繁GC.耗电问题.OOM问题. 二.导致性能问题的原因 1.人为在ui线程中做了 ...
- 让你大开眼界的10款Android界面设计
根据调查显示, iOS与Android的市场份额差距正越来越大.Android设备正在成为手机应用市场的主力军.如何从设计层面创造一个优美的app界面来吸引用户已然成为广大App开发者们必做的功课之一 ...
随机推荐
- codeforces 521a//DNA Alignment// Codeforces Round #295(Div. 1)
题意:如题定义的函数,取最大值的数量有多少? 结论只猜对了一半. 首先,如果只有一个元素结果肯定是1.否则.s串中元素数量分别记为a,t,c,g.设另一个串t中数量为a',t',c',g'.那么,固定 ...
- Golang中defer、return、返回值之间执行顺序的坑
原文链接:https://studygolang.com/articles/4809 Go语言中延迟函数defer充当着 cry...catch 的重任,使用起来也非常简便,然而在实际应用中,很多go ...
- SourceTree
MAC上最好的GIT免费GUI工具是SourceTree(没有之一).此外,最好的GIT代码开源网站是GitHub,最好的GIT代码私有库是BitBucket https://www.sourcetr ...
- sql 2005 代码导入excel数据
select * into bm from OpenDataSource( 'Microsoft.ACE.OLEDB.12.0', 'Data Source="G:\bm.xls" ...
- Java中List的排序方法
方法一:实现Comparable接口 package com.java.test; public class Person implements Comparable<Person> { ...
- iOS UI-UIPickerView(拾取器)、UIWebView(网页视图)和传值方式
// // ViewController.m // IOS_0107_finalToolClass // // Created by ma c on 16/1/7. // Copyright (c) ...
- dubbo监控中心搭建
从网上下载了一个dubbo监控中心,地址忘了,文件名是dubbo-monitor-simple-2.5.3-assembly.tar.gz. 修改监控中心配置文件如下: dubbo.container ...
- HeaderExchangeClient
HeaderExchangeClient 注释是DefaultMessageClient,类中定义了心跳定时器HeaderExchangeChannel 发送请求HeaderExchangeHandl ...
- Deep Belief Network简介——本质上是在做逐层无监督学习,每次学习一层网络结构再逐步加深网络
from:http://www.cnblogs.com/kemaswill/p/3266026.html 1. 多层神经网络存在的问题 常用的神经网络模型, 一般只包含输入层, 输出层和一个隐藏层: ...
- 用正则表达式匹配用rdf3x处理过后的TTL格式文档
1.比如下面这个用rdf3x处理过后的TTL文档片段: 注意缩进的是两个空格 <http://rdf.ebi.ac.uk/resource/chembl/target/CHEMBL2363853 ...