android 创建桌面小部件widget
1. 创建自定义widget的广播类,继承自 AppWidgetProvider(有了这个广播就会在widgets中能够选择了吗?)这个广播的生命周期主要有五个,在第一个widget拖动到桌面和最后一个widget删除和已经有widget时拖动到桌面的生命周期是不同的
根据对生命周期的分析,在onUpdate中进行初始化,在onDisabled中进行销毁(关闭服务)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ProcessWidgetReceiver extends AppWidgetProvider { private Intent intent; @Override public void onReceive(Context context, Intent intent) { Log.e("John", "ProcessWidgetReceiver" + " # " + "onReceive"); super.onReceive(context, intent); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.e("John", "ProcessWidgetReceiver" + " # " + "onUpdate"); // 初始化的相关工作,开启一个服务, intent = new Intent(context, ProcessWidgetService.class); context.startService(intent); } @Override public void onDeleted(Context context, int[] appWidgetIds) { Log.e("John", "ProcessWidgetReceiver" + " # " + "onDeleted"); super.onDeleted(context, appWidgetIds); } @Override public void onEnabled(Context context) { Log.e("John", "ProcessWidgetReceiver" + " # " + "onEnabled"); super.onEnabled(context); } @Override public void onDisabled(Context context) { Log.e("John", "ProcessWidgetReceiver" + " # " + "onDisabled"); // 销毁的相关工作 context.stopService(intent); super.onDisabled(context); } }
2. 注册widget广播在Manifest中,并且初始化他的布局
<receiver android:name=".receiver.ProcessWidgetReceiver"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/process_clean_widget" /> // 这个是widget的布局文件 </receiver>
"@xml/process_clean_widget"描述了widget的基本布局信息,主要的参数是下面四个
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/process_widget" android:minHeight="72.0dip" android:minWidth="294.0dip" android:updatePeriodMillis="0" />
"@layout/process_widget" 具体的布局,和其他的控件布局形式相同
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/widget_bg_portrait" android:gravity="center_vertical"> <LinearLayout android:layout_width="0.0dip" android:layout_height="fill_parent" android:layout_marginLeft="5.0dip" android:layout_weight="1.0" android:background="@drawable/widget_bg_portrait_child" android:gravity="center_vertical" android:orientation="vertical" android:paddingBottom="3.0dip" android:paddingTop="3.0dip"> <TextView android:id="@+id/process_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10.0dip" android:textAppearance="@style/widget_text" /> <ImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="1.0dip" android:layout_marginTop="1.0dip" android:background="@drawable/widget_bg_portrait_child_divider" /> <TextView android:id="@+id/process_memory" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10.0dip" android:textAppearance="@style/widget_text" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="vertical"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical"> <ImageView android:layout_width="20.0dip" android:layout_height="20.0dip" android:src="@drawable/main_icon" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" android:textColor="@color/textColorPrimary" /> </LinearLayout> <Button android:id="@+id/btn_clear" android:layout_width="90.0dip" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginTop="5.0dip" android:background="@drawable/function_greenbutton_selector" android:text="一键清理" android:textColor="@color/function_greenbutton_textcolor_selector" /> </LinearLayout> </LinearLayout>
3. 实现这个widget的界面更新和按钮的点击事件,注意的是widget是相当于创建在桌面应用中的所以界面的更新和点击事件的实现和普通的不一样,因为widget的操作在一个单独注册的服务中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ProcessWidgetService extends Service { private Timer timer; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { final AppWidgetManager awm = AppWidgetManager.getInstance(getApplicationContext()); // 创建定时器,定时刷新widget timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Log.e("John", "ProcessWidgetService" + " # " + "更新widget"); // 不停更新widget文本内容 ComponentName provider = new ComponentName(getApplicationContext(), ProcessWidgetReceiver.class); RemoteViews views = new RemoteViews(getPackageName(), R.layout.process_widget); views.setTextViewText(R.id.process_count, "正在运行的软件:" + ProcessUtil.getRunningProcess(getApplicationContext(), ProcessUtil.ProcessType.ALL).size() + "个"); long availMem = ProcessUtil.getMemoryInfo(getApplicationContext(), ProcessUtil.MemoryType.AVAILABLE); String memStr = Formatter.formatFileSize(getApplicationContext(), availMem); views.setTextViewText(R.id.process_memory, "可用内存 :" + memStr); // 点击清理内存,通过发出一个广播在另一个广播中进行清理 Intent intent = new Intent(); intent.setAction("com.john.kill_process"); PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent); awm.updateAppWidget(provider, views); // 更新widget } }, 0, 5000); } @Override public void onDestroy() { timer.cancel(); timer = null; super.onDestroy(); } }
通过点击按钮,是发出了一个广播,这个广播在程序中进行注册了,当点击发生后程序接受到广播然后在广播里进行清理进程
<receiver android:name=".receiver.ProcessCleanReceiver"> <intent-filter> <action android:name="com.john.kill_process"/> </intent-filter> </receiver>
清理的广播
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ProcessCleanReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { List<ProcessInfo> runningProcess = ProcessUtil.getRunningProcess(context, ProcessUtil.ProcessType.ALL); Log.e("John", "ProcessCleanReceiver" + " # " + "广播,清理进程 #" + runningProcess.size()); ProcessUtil.killProcesses(context, runningProcess); } }
总结:
1. 初始化widget的广播
1.1 自定义widget的类继承自AppWidgetReceiver
1.2 在Manifest中注册widget
2. 根据Manifest中注册时定义的widget的基本布局文件去定义widget的基本布局参数
3. 根据widget的基本参数文件中定义的layout文件在layout文件夹下面去创建这个layout文件,这个layout文件按照一般的控件布局去定义
4. 在widget广播中开启一个服务,这个服务中开启一个定时器用来更新运行的进程数据(这里是以这个Demo为例,每隔5秒去获取正在运行的进程)
4.1 widget中点击事件是通过点击了然后发出一个广播由当前程序去监听这个广播然后在收到广播后清理进程的操作