您的位置:首页 > 其它

实现金山卫士的小控件(Widget)

2015-08-23 21:53 465 查看
0.思路:反编译金山的apk,在清单文件中根据android.appwidget.action.APPWIDGET_UPDATE找到入口,进而一步步向下

1. 自定义类继承自AppWidgetProvider

public class ProcWidget extends AppWidgetProvider {

}


2. 在AndroidManifest.xml文件中配置,AppWidgetProvider类继承自BroadcastReceiver,因此配置receiver节点

<receiver android:name="com.cbooy.widget.ProcWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>

<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/process_widget_provider" />
</receiver>


3.在res目录下新建xml目录,新建文件process_widget_provider.xml

<?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"
   <!--设置更新时间为0,Google会自行判断,此处时间最小为30分钟,因此设置时间小于30分钟会按照30分钟处理-->
android:updatePeriodMillis="0" />


4. 在layout下新建布局文件 process_widget.xml

<?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"
android:text="正在运行的软件:17" />

<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"
android:text="可用内存:430.22mb" />
</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="#ffffffff" />
</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="@drawable/function_greenbutton_textcolor_selector" />
</LinearLayout>

</LinearLayout>


5. 相关资源文件

  style

<style name="widget_text">
<item name="android:textSize">16.0dip</item>
<item name="android:textColor">#ff000000</item>
</style>


  drawable下资源function_greenbutton_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/function_greenbutton_pressed" />
<item android:state_focused="true" android:drawable="@drawable/function_greenbutton_pressed" />
<item android:drawable="@drawable/function_greenbutton_normal" />
</selector>


  drawable下资源function_greenbutton_textcolor_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="#80ffffff" />
<item android:state_focused="true" android:color="#80ffffff" />
<item android:state_selected="true" android:color="#80ffffff" />
<item android:color="#ffffffff" />
</selector>


  drawable下的图片资源


    










6. 实现修改widget页面的数据,由于widget是显示的桌面程序中,因此与自己的程序属于两个不同的进程,因此修改数据是跨进程的通信

  补全ProcWidget类

public class ProcWidget extends AppWidgetProvider {

@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);

protectServiceRunning(context);
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);

protectServiceRunning(context);
}

@Override
public void onEnabled(Context context) {
super.onEnabled(context);

startRefreshWidgetUIService(context);
}

@Override
public void onDisabled(Context context) {
super.onDisabled(context);

stopRefreshWidgetUIService(context);
}

/**
* 若用户在后台清理掉了此进程,在widget有任何操作时都会调用此方法,因此可以保护服务开启
*
* @param context
*/
private void protectServiceRunning(Context context) {
boolean isRefreshWidgetServiceRunning = ServicesUtil.isServiceRun(
context, RefreshWidgetUIService.class.getName());
if (!isRefreshWidgetServiceRunning) {
startRefreshWidgetUIService(context);
}
}

/**
* 开启widget刷新UI服务
*
* @param context
*/
private void startRefreshWidgetUIService(Context context) {
Intent intent = new Intent(context, RefreshWidgetUIService.class);

context.startService(intent);
}

/**
* 停止widget刷新服务
*
* @param context
*/
private void stopRefreshWidgetUIService(Context context) {
Intent intent = new Intent(context, RefreshWidgetUIService.class);

context.stopService(intent);
}
}


新建服务,来完成数据的刷新功能,要在清单文件中注册

public class RefreshWidgetUIService extends Service {

private Timer timer;

private TimerTask timerTask;

private AppWidgetManager widgetManager;

@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onCreate() {
super.onCreate();

schedultTask();
}

/**
* 本地 调度任务,立即执行,3秒钟执行一次
*/
private void schedultTask() {
timer = new Timer();

timerTask = new TimerTask() {
@Override
public void run() {
refreshWidgetData();
}
};

timer.schedule(timerTask, 0, 3 * 1000);
}

/**
* 刷新数据
*/
private void refreshWidgetData() {
widgetManager = AppWidgetManager.getInstance(RefreshWidgetUIService.this);

// 设置更新的组件
ComponentName componentName = new ComponentName(RefreshWidgetUIService.this,ProcWidget.class);

// 跨进程通信使用的View
RemoteViews views = new RemoteViews(getPackageName(),R.layout.process_widget);

// 进程管理的工具类,提供getRunningProcSize()获取运行进程的个数,getAvaliMem()获取可用内存大小
ProcessManage pm = ProcessManage.buildWithContext(RefreshWidgetUIService.this);

views.setTextViewText(R.id.process_count, new StringBuilder("正在运行的进程:").append(pm.getRunningProcSize()));

views.setTextViewText(R.id.process_memory, new StringBuilder("可用内存:").append(pm.getAvaliMem()));

Intent intent = new Intent();

intent.setAction("com.cbooy.mmap.clean_procs");

// 描述一个动作,此动作由另外的进程来执行,此时是桌面发出来一个广播,来接受响应事件
// FLAG_UPDATE_CURRENT 第二次消息会把第一次的消息给覆盖掉
PendingIntent pendingIntent = PendingIntent.getBroadcast(RefreshWidgetUIService.this, 0, intent , PendingIntent.FLAG_UPDATE_CURRENT);

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

widgetManager.updateAppWidget(componentName, views);
}

@Override
public void onDestroy() {
super.onDestroy();

timer.cancel();

timerTask.cancel();

timer = null;

timerTask = null;
}
}


7.注意事项

App必须安装在手机内存中才可以展示,安装在sdcard中则无法展示,若配有android:installLocation="preferExternal" 则修改,或移动回内存中

8.效果图



注:以上代码有一些细节可能会有出入,都是一些资源文件类的名字,修改很简单,还有一些清单文件的注册请注意
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: