##Day10##
#10.1.widgets控件# (重点)
AppWidgetProvider : 广播接受者
创建步骤
1.第一步创建一个广播接受者AppWidgetProvider
2.清单文件配置
<receiver android:name="cn.itcast.mobilesafexian02.receiver.MyWidgets" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>
3.在res -> xml -> example_appwidget_info.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="272dp"
android:minHeight="72dp"
android:updatePeriodMillis="0"
android:initialLayout="@layout/example_appwidget"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen|keyguard"
>
</appwidget-provider>
<!--
minWidth、minHeight :最小的宽度和高度
updatePeriodMillis : widgets更新时间,毫秒值,但是不能小于30分钟,0其实跟30分钟一样的效果
previewImage : 预览图片,不设置,就会使用应用程序的图标
initialLayout : widgets的布局
configure :可选属性,创建widgets的时候启动的activity
resizeMode : 设置widget显示尺寸的规则,设置屏幕水平和垂直切换到显示widget的风格
widgetCategory : widget可以显示的地方,home_screen:桌面 keyguard:键盘
initialKeyguardLayout : widget在锁屏的时候显示到布局
-->
带@RemoteView注解的控件都是可以显示在widgets里面,没有就不能显示
#10.2widgets生命周期#
第一次创建
08-21 01:30:02.197: I/System.out(1677): onEnabled
08-21 01:30:02.197: I/System.out(1677): onReceive
08-21 01:30:02.207: I/System.out(1677): onUpdate
08-21 01:30:02.207: I/System.out(1677): onReceive
08-21 01:30:04.947: I/System.out(1677): onReceive
第二次创建
08-21 01:30:50.299: I/System.out(1677): onUpdate
08-21 01:30:50.299: I/System.out(1677): onReceive
08-21 01:30:57.099: I/System.out(1677): onReceive
第三次创建
08-21 01:31:26.969: I/System.out(1677): onUpdate
08-21 01:31:26.969: I/System.out(1677): onReceive
08-21 01:31:29.639: I/System.out(1677): onReceive
删除第一个
08-21 01:32:26.990: I/System.out(1677): onDeleted
08-21 01:32:26.990: I/System.out(1677): onReceive
删除第二个
08-21 01:32:54.831: I/System.out(1677): onDeleted
08-21 01:32:54.831: I/System.out(1677): onReceive
删除最后一个
08-21 01:33:18.391: I/System.out(1677): onDeleted
08-21 01:33:18.391: I/System.out(1677): onReceive
08-21 01:33:18.401: I/System.out(1677): onDisabled
08-21 01:33:18.401: I/System.out(1677): onReceive
总结:
1.第一次创建的时候会调用 onEnabled
2.每次创建都会调用 onUpdate
3.不管什么操作都会调用onReceive
4.每次删除都会调用onDeleted
5.删除最后一个调用onDisabled
#10.3拷贝金山卫士控件布局# (反编译)
1.反编译操作
2.根据action找打清单文件中注册的receiver
3.根据resource找到相应的xml文件
4.根据xml文件中的initialLayout找到相应的布局文件
5.修改错误,完成样式
#10.4.更新widgets中的文本# (重点)
1.创建一个widgetsService
2.在其中的oncreate方法中获取widget管理者
//1.获取widgets管理者
appWidgetManager = AppWidgetManager.getInstance(this);
3.更新widgets
/**
* 更新widgets
*/
private void updatesWidgets() {
// new Thread(){
// public void run() {
// while(true){
//
// }
// };
// }.start();
Timer timer = new Timer();
//task : 定时执行的任务
//when : 延迟执行的时间
//period : 执行的间隔时间
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("widgets更新了");
ComponentName provider = new ComponentName(WidgetsService.this, MyWidgets.class);
//RemoteViews 远程布局
//packageName: 包名
//layoutId :widgets的布局文件
RemoteViews views = new RemoteViews(getPackageName(), R.layout.process_widget);
//远程布局是不能findviewbyid初始化控件
//viewId : 要更新控件的id
//text : 要更新的内容
views.setTextViewText(R.id.process_count, "正在运行软件:"+ProcessUtils.getAvailableProcess(WidgetsService.this));
views.setTextViewText(R.id.process_memory, "可用内存:"+Formatter.formatFileSize(WidgetsService.this, ProcessUtils.getAvaliableRam(WidgetsService.this)));
//更新widgets操作
appWidgetManager.updateAppWidget(provider, views);
}
}, 2000, 2000);
}
4.在Mywidgets的onEnabled开启服务,在onDisabled关闭服务
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
System.out.println("onEnabled");
//开启widgets服务
Intent intent = new Intent(context,WidgetsService.class);
context.startService(intent);
}
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
System.out.println("onDisabled");
//关闭服务
Intent intent = new Intent(context,WidgetsService.class);
context.stopService(intent);
}
#10.5widgets点击事件的实现# (重点)
1.在timer中
//pendingIntent : 延迟意图,包装了一下intent,点击按钮的才会去执行清理的操作,不点击就不会去执行这个intent意图
views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);
2.因为需要一个延迟意图
Intent intent = new Intent();
//设置发送的广播事件,action
intent.setAction("aa.bb.cc");
//context : 上下文
//requestCode :请求码
//intent : 要包装的意图
//flags : 执行操作的标签
//发送广播
PendingIntent pendingIntent = PendingIntent.getBroadcast(WidgetsService.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//viewId :执行点击事件的控件的id
//pendingIntent : 延迟意图,包装了一下intent,点击按钮的才会去执行清理的操作,不点击就不会去执行这个intent意图
views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);
//更新widgets操作
appWidgetManager.updateAppWidget(provider, views);
3.因为发送了广播,所以在Oncreate去用代码注册一个广播
a.创建一个广播接受者
private class WidgetReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
}
}
b.在oncreate中注册
widgetReceiver = new WidgetReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("aa.bb.cc");
registerReceiver(widgetReceiver, intentFilter);
c.在ondestory方法中注销
@Override
public void onDestroy() {
super.onDestroy();
if (widgetReceiver != null) {
unregisterReceiver(widgetReceiver);
widgetReceiver = null;
}
}
d.在广播接受者的onreceive方法中执行清理进程的操作
@Override
public void onReceive(Context context, Intent intent) {
killprocess();
}
/**
* 清理进程
*/
public void killprocess() {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
if (!runningAppProcessInfo.processName.equals(getPackageName())) {
am.killBackgroundProcesses(runningAppProcessInfo.processName);
}
}
}
#10.6锁屏清理进程# (重点)
锁屏和解锁的广播接受者是不能在清单文件中注册的,必须使用代码进行注册,避免一些恶意程序的骚扰
1.隐藏系统进程
a.创建一个boolean变量,表示是否隐藏系统进程
//是否显示系统进程
private boolean isShowSystem = true;
b.在Myadapter的getCount方法进行判断
@Override
public int getCount() {
//方便我们从不同的集合中拿出数据
return isShowSystem == true ? userAppInfos.size()+1+systemAppInfos.size()+1 : userAppInfos.size()+1;
}
c.在设置的点击事件中更改boolean变量的值
/**
* 设置
* @param v
*/
public void setting(View v){
//true改为false false改为true
isShowSystem = !isShowSystem;
//更新界面
myAdapter.notifyDataSetChanged();
}
2.锁屏清理进程
a.在服务中创建一个广播接受者
private class ScreenOffReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
killprocess();
//停止更新widgets
stopupdate();
}
}
b.在Oncreate方法中注册广播接受者
//注册锁屏的广播接受者
screenOffReceiver = new ScreenOffReceiver();
//设置过滤条件
IntentFilter screenoffintentfilter = new IntentFilter();
screenoffintentfilter.addAction(Intent.ACTION_SCREEN_OFF);
//注册广播接受者
registerReceiver(screenOffReceiver, screenoffintentfilter);
c.在ondestory方法中注销广播接受者
//注销锁屏的广播接受者
if (screenOffReceiver != null) {
unregisterReceiver(screenOffReceiver);
screenOffReceiver = null;
}
d.在广播接受者的onreceive方法执行操作
@Override
public void onReceive(Context context, Intent intent) {
killprocess();
//停止更新widgets
stopupdate();
}
3.解锁开启更新的操作
a.在服务中创建一个广播接受者
private class ScreenOnReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
updatesWidgets();
}
}
b.在Oncreate方法中注册广播接受者
//注册解锁的广播接受者
screenOnReceiver = new ScreenOnReceiver();
//设置过滤条件
IntentFilter screenonintentfilter = new IntentFilter();
screenonintentfilter.addAction(Intent.ACTION_SCREEN_ON);
//注册广播接受者
registerReceiver(screenOnReceiver, screenonintentfilter);
c.在ondestory方法中注销广播接受者
//注销解锁的广播接受者
if (screenOnReceiver != null) {
unregisterReceiver(screenOnReceiver);
screenOnReceiver = null;
}
d.在广播接受者的onreceive方法执行操作
@Override
public void onReceive(Context context, Intent intent) {
updatesWidgets();
}
#10.7widgets的bug的处理#
bug : 用户从设置中心关闭服务,造成widgets无法更新的问题
updatePeriodMillis 更新时间到了之后就会去调用onUpdate方法
处理方式:将开启服务的操作移植到onUpdate方法
#10.8屏幕适配# (重点,面试)
dp:屏幕像素和屏幕尺寸的比例
px:屏幕像素点
1.使用工具类
public class DensityUtil {
/**
* 根据手机的分辨率从 dip 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density; //通过资源获取屏幕的密度
return (int) (dpValue * scale + 0.5f); //四舍五入 3.7 3 3.7+0.5 = 4.2 4
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
2.使用ScrollView
<!-- ScrollView : 是能让屏幕滚动的控件,但是ScrollView中只能有一个子控件 -->
#10.9创建快捷方式# (重点)
1.可以给软件创建快捷方式
2.可以修改快捷方式的名称
3.可以修改快捷方式的图标
4.设置快方式执行的操作
创建快捷方式的操作
/**
* 创建快捷方式操作
*/
private void shortCut() {
if (sp.getBoolean("firstEnter", true)) {
// 添加快捷方式
// 发送广播
Intent intent = new Intent(
"com.android.launcher.action.INSTALL_SHORTCUT");
// 设置快捷方式的名称
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "手机卫士西安2号");
// 设置快捷方式的图标
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
//隐示意图
Intent intent2 = new Intent();
intent2.setAction("cn.itcast.mobilesafexian02.home");
intent2.addCategory("android.intent.category.DEFAULT");
// 设置快捷方式要执行的操作
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent2);
// 发送广播
sendBroadcast(intent);
//更改保存的值
Editor edit = sp.edit();
edit.putBoolean("firstEnter", false);
edit.commit();
}
}
#10.10监听用户打开的应用程序# (重点)
时时刻刻监听某些操作的行为,watch dog 看门狗
任务栈:管理activity,一个应用就有一个任务栈,打开所有的activity都存放在任务栈
在服务中跳转activity必须给要跳转的activity指明一个任务栈,这样才能跳转
步骤
在服务oncreate方法中执行
//1.进程的管理者
final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
//2.开启线程时时刻刻监听用户打开的应用程序
new Thread(){
public void run() {
while(isTasks){
//3.获取正在的运行任务栈
//maxNum : 获取前几个正在运行的任务栈
List<RunningTaskInfo> runningTasks = am.getRunningTasks(1);
for (RunningTaskInfo runningTaskInfo : runningTasks) {
//4.获取栈底的activity
ComponentName baseactivity = runningTaskInfo.baseActivity;
//runningTaskInfo.topActivity;//获取栈顶的activity
//5.获取应用程序的包名
String packageName = baseactivity.getPackageName();
//6.判断获取包名是否是打开的应用程序的包名,是就显示密码输入界面,不是就不管了
if (packageName.equals("com.android.mms")) {
Intent intent = new Intent(WatchDogService.this,MainActivity.class);
//给跳转的activity指明一个任务栈
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
System.out.println(packageName);
}
SystemClock.sleep(500);
}
};
}.start();
#10.11加锁解锁的操作# (长按事件掌握)
1.在getview的item的布局文件中增加小锁的图标
2.在getview中根据数据库中保存的是否有条目的包名来显示相应的图标
//根据包名去显示应用是否是已经加锁的操作
if (watchDogDao.queryLockApp(appInfo.getPackageName())) {
//显示加锁状态
viewHolder.iv_itemsoftmanager_islock.setImageResource(R.drawable.lock);
}else{
//显示解锁状态
viewHolder.iv_itemsoftmanager_islock.setImageResource(R.drawable.unlock);
}
3.给listview增加长按点击事件
/**
* listview的条目长按点击事件
*/
private void listviewitemLongClick() {
lv_softmanager_applications.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
//1.屏蔽用户程序多少个和系统程序多少个
if (position == 0 || position == userAppInfos.size()+1) {
return true;
}
//2.获取软件信息
//获取条目对应的信息
//判断用户程序是否展示完
if (position <= userAppInfos.size()) {
//获取用户程序
appInfo = userAppInfos.get(position-1);
}else{
//系统程序
appInfo = systemAppInfos.get(position - userAppInfos.size()-2);
}
ViewHolder viewHolder = (ViewHolder) view.getTag();
//加锁解锁的操作,原先加锁,再点击就是解锁,原先解锁,在点击就是加锁
if (watchDogDao.queryLockApp(appInfo.getPackageName())) {
//解锁
watchDogDao.deleteLockApp(appInfo.getPackageName());
//更新界面
viewHolder.iv_itemsoftmanager_islock.setImageResource(R.drawable.unlock);
}else{
//加锁
//自己不能给自己加锁
if (!appInfo.getPackageName().equals(getPackageName())) {
watchDogDao.addLockApp(appInfo.getPackageName());
//更新界面
viewHolder.iv_itemsoftmanager_islock.setImageResource(R.drawable.lock);
}
}
//true if the callback consumed the long click, false otherwise
//true:被消费了,执行了,false:表示被拦截了
return true;
}
});
}
#10.12将监听用户打开的操作移植到手机卫士中#
1.将demo看门狗中watchdogservice操作移植到手机卫士中
2.修改判断用户打开的应用程序是否应该显示输入密码界面的操作
//6.判断包名是否在数据库中
if (watchDogDao.queryLockApp(packageName)) {
//跳转到密码输入界面
Intent intent = new Intent(WatchDogService.this,WatchDogActivity.class);
//给跳转到的activity指定一个任务栈
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
3.在设置中心模仿黑名单操作,进行操作
4.解决进入输入密码界面,点击返回键,有进入输入密码界面的bug
解决:在输入密码界面重写onkeydown方法
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
//跳转到主界面
/**
* act=android.intent.action.MAIN action
cat=[android.intent.category.HOME] category
cmp=com.android.launcher/com.android.launcher2.Launcher
*/
Intent intent = new Intent();
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.HOME");
startActivity(intent);
}
return super.onKeyDown(keyCode, event);
}
#10.13修改输入密码界面#
1.修改布局文件,增加图标和名称
2.在Watchdogservice中跳转界面的时候,将包名传递过去
//6.判断包名是否在数据库中
if (watchDogDao.queryLockApp(packageName)) {
//跳转到密码输入界面
Intent intent = new Intent(WatchDogService.this,WatchDogActivity.class);
//给跳转到的activity指定一个任务栈
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//将包名传递给密码输入界面,用于显示图标和名称
intent.putExtra("packagename", packageName);
startActivity(intent);
}
3.在watchDogActivity中根据包名获取打开应用程序的图标和名称,在oncreate方法执行
//显示打开应用的图标和名称
Intent intent = getIntent();
//1.获取传递过来的包名
String packagename = intent.getStringExtra("packagename");
//2.根据包名获取图标和名称
PackageManager pm = getPackageManager();
try {
//3.根据包名得到ApplicationInfo的信息
ApplicationInfo applicationInfo = pm.getApplicationInfo(packagename, 0);
//4.获取图标
Drawable icon = applicationInfo.loadIcon(pm);
//5.获取名称
String name = applicationInfo.loadLabel(pm).toString();
//6.设置显示数据
iv_watchdog_icon.setImageDrawable(icon);
tv_watchdog_name.setText(name);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}