Android项目实战_手机安全卫士拦截骚扰
###1.骚扰拦截需求分析
1.界面
1.1 黑名单列表界面
1.2 添加黑名单界面
2.功能
2.1 黑名单的添加、删除
2.2 拦截电话
2.3 拦截短信
###2.黑名单数据库的创建
1.分析需要的字段
id 主键自增长,phone 电话号码,mode 拦截模式
2.创建数据库打开类BlackNumberDBOpenHelper,继承SQLiteOpenHelper
public class BlackNumberDBOpenHelper extends SQLiteOpenHelper {
public BlackNumberDBOpenHelper(Context context) {
super(context, "address.db", null, 1);
}
//数据库第一次被创建的时候调用,适合初始化数据库的表结构
@Override
public void onCreate(SQLiteDatabase db) {
//id 主键自增长, phone 电话号码 mode 拦截模式
db.execSQL("create table blacknumber (_id integer primary key autoincrement, phone varchar(20),mode varchar(2))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
3.创建数据库数据操作类BlackNumberDao,实现增删改查方法
###3.增删改查单元测试
1.创建测试类TestBlackNumberDao,继承AndroidTestCase,测试增删改查方法
2.在清单文件中对测试类进行如下配置:
2.1 manifest节点下添加
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.hb.mobilesafe" >
</instrumentation>
2.2 application节点下添加
<uses-library android:name="android.test.runner"/>
###4.android:layout_weight的用法
1.android:layout_weight:权重比例,将剩余空间按比例分配。只可在线性布局中使用
2.指定android:layout_weight属性后,
如果线性布局是水平的,View的宽度=原有宽度+线性布局剩余空间的占比
如果线性布局是竖直的,View的高度=原有高度+线性布局剩余空间的占比
3.Android官方推荐,在使用android:layout_weight时,
如果线性布局是水平的,要将android:layout_width设置为0dp
如果线性布局是竖直的,要将android:layout_height设置为0dp
###5.9patch图片(.9.png拼接图片)

1号黑色条位置向下覆盖的区域表示图片横向拉伸时,只拉伸该区域
2号黑色条位置向右覆盖的区域表示图片纵向拉伸时,只拉伸该区域
3号黑色条位置向左覆盖的区域表示图片纵向显示内容的区域
4号黑色条位置向上覆盖的区域表示图片横向显示内容的区域
###6.shape图形
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
<!-- 矩形|椭圆|线|环 -->
android:shape=["rectangle" | "oval" | "line" | "ring"] >
<!-- 圆角 -->
<corners
android:radius="integer"
android:topLeftRadius="integer"
android:topRightRadius="integer"
android:bottomLeftRadius="integer"
android:bottomRightRadius="integer" />
<!-- 固定色 -->
<solid
android:color="color" />
<!-- 渐变色 -->
<gradient
android:type=["linear" | "radial" | "sweep"]
//linear 线性渐变,这是默认设置
//radial 放射性渐变,以开始色为中心
//sweep 扫描线式的渐变。
android:startColor="color" //颜色值 起始颜色
android:endColor="color" //颜色值 结束颜色
android:centerColor="integer" //渐变中间颜色,即开始颜色与结束颜色之间的颜色
android:angle="integer" //渐变角度
android:centerX="integer" //渐变中心X点坐标的相对位置
android:centerY="integer" //渐变中心Y点坐标的相对位置
android:gradientRadius="integer" //渐变色半径.当 android:type="radial" 时才使用。单独使用 android:type="radial"会报错 />
<!-- 边框 -->
<stroke
android:width="integer"
android:color="color"
android:dashWidth="integer"
android:dashGap="integer" />
<!-- 边距 -->
<padding
android:left="integer"
android:top="integer"
android:right="integer"
android:bottom="integer" />
<!-- 大小 一般不指定-->
<size
android:width="integer"
android:height="integer" />
</shape>
###2.挂断电话
//加入权限
<uses-permission android:name="android.permission.CALL_PHONE" />
/**
* 挂断电话
*/
public void endCall() {
//系统内部调用方式
//ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
try {
//ServiceManager获取不到,需要反射调用
Class clazz = getClassLoader().loadClass("android.os.ServiceManager");
Method method = clazz.getDeclaredMethod("getService", String.class);
IBinder iBinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);
//挂断电话需要用到AIDL,拷贝需要用到的AIDL文件,ITelephony.aidl和NeighboringCellInfo.aidl,包名要与原包名一致
ITelephony iTelephony = ITelephony.Stub.asInterface(iBinder);
iTelephony.endCall();
} catch (Exception e) {
e.printStackTrace();
}
}
###3.删除呼叫记录
//加入权限
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
/**
* 删除黑名单号码的呼叫记录
* @param incomingNumber 来电黑名单号码
*/
public void deleteCallLog(final String incomingNumber) {
final ContentResolver resolver = getContentResolver();
//CallLog.Calls.CONTENT_URI 等价于 Uri.parse("content://call_log/calls");
final Uri uri = Uri.parse("content://call_log/calls");
//利用内容观察者 观察呼叫记录的数据库,如果生成了呼叫记录就立刻删除呼叫记录
resolver.registerContentObserver(uri, true, new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
//当内容观察者观察到数据库的内容变化的时候调用的方法.
super.onChange(selfChange);
resolver.delete(uri, "number=?", new String[]{incomingNumber});
}
});
}
###4.拦截短信
1.注册在清单文件中的广播接收者,无论应用是否启动都会接收到广播,想用开关控制拦截短信的功能,则在代码中注册短信广播接收者
receiver = new InnerSmsReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(receiver, filter);
private class InnerSmsReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.i(Tag,"服务内部广播接受者接收到了短信");
Object[] objs = (Object[]) intent.getExtras().get("pdus");
for(Object obj: objs){
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) obj);
String body = smsMessage.getMessageBody();
if(body.contains("fapiao")){
//你的头发票亮的很 分词技术
Log.i(Tag,"发现发票垃圾短信,拦截");
abortBroadcast();
return;
}
String sender = smsMessage.getOriginatingAddress();
String mode = dao.find(sender);
if("2".equals(mode)||"3".equals(mode)){
Log.i(Tag,"发现黑名单短信,拦截");
abortBroadcast();
}
}
}
}
2.onDestroy中注销
unregisterReceiver(receiver);
receiver = null;
###5.导入已存在的数据库
//assert资产目录里面的文件会原封不动的打包到apk里,不生成id
/**
* 拷贝归属地的数据库
*/
private void copyAddressDB() {
File file = new File(getFilesDir(), "address.db");
//判断数据库文件是否存在
if (file.exists() && file.length() > 0) {
Log.i(TAG, "数据库存在,无需拷贝");
} else {
new Thread() {
public void run() {
// 把asset资产目录里面的数据库文件(在apk里面的)拷贝到手机系统里面
try {
InputStream is = getAssets().open("address.db");
File file = new File(getFilesDir(), "address.db");
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
}
###6.查询号码归属地
/**
* 查询手机号码的归属地信息
* @param mobilenumber
* @return
*/
public static String findLocation(String mobilenumber) {
String path = "/data/data/com.hb.mobilesafe/files/address.db";
//打开已存在的数据库
SQLiteDatabase db = SQLiteDatabase.openDatabase(path, null,
SQLiteDatabase.OPEN_READONLY);
Cursor cursor = db.rawQuery(
"select location from data2 where id = (select outkey from data1 where id = ?)",
new String[]{mobilenumber.substring(0, 7)});
String location ="";
if(cursor.moveToNext()){
location = cursor.getString(0);
}
cursor.close();
db.close();
return location;
}
###7.判断一个号码是否是手机号码
// ^1[34578]\d{9}$
// ^ 匹配输入字符串的开始位置。
// [] 字符集合。匹配所包含的任意一个字符。
// \d 匹配一个数字字符。
// {} n 是一个非负整数。匹配确定的 n 次。
// $ 匹配输入字符串的结束位置。
boolean result = number.matches("^1[34578]\\d{9}$");
###8.给EditText添加文本变化监听器
// 给文本输入框注册一个内容变化的监听器.
et_number.addTextChangedListener(new TextWatcher() {
//当文本变化之前调用的方法,s为改变前字符串,可获取被替换内容
//在s中从start开始的count个字符即将被after个字符替换
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
//当文本变化之后调用的方法,s为改变后字符串,可获取替换内容
//在s中从start开始的before个字符刚刚被count个字符替换
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (s.length() >= 10) {
String location = AddressDBDao.findLocation(s.toString());
tv_location.setText("归属地为:" + location);
}
}
//当文本变化之后调用的方法,s为改变后字符串,操作s可直接改变EditText内容,EditText内容改变会继续调用beforeTextChanged和onTextChanged方法
//s中有内容被改变
@Override
public void afterTextChanged(Editable s) {
}
});
每次输入都会调用3个方法,调用顺序为beforeTextChanged-->onTextChanged-->afterTextChanged
###9.CharSequence、String、Editable
1.CharSequence:接口,只有length()、charAt(int index)、subSequence(int start, int end)、toString()四个方法
2.String:实现了CharSequence接口,具有很多操作字符串的方法,不可修改
3.Editable:实现了CharSequence接口,具有增删改等修改的方法,可修改
###10.动画插值器Interpolator
1.interpolator定义一个动画的变化率(the rate of change)。这使得基本的动画效果(alpha, scale, translate, rotate)得以加速,减速,重复等。
2.常用Interpolator
LinearInterpolator 以常量速率改变
AccelerateInterpolator 加速
DecelerateInterpolator 减速
AccelerateDecelerateInterpolator 先加速后减速
AnticipateInterpolator
OvershootInterpolator
AnticipateOvershootInterpolator
BounceInterpolator 弹跳
CycleInterpolator 循环播放特定的次数,速率改变沿着正弦曲线
###11.监听外拨电话
1.添加权限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
2.注册广播接收者
receiver = new OutCallReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
registerReceiver(receiver, filter);
private class OutCallReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String number = getResultData();
String address = AddressDBDao.findLocation(number);
Toast.makeText(ShowAddressService.this, address, 1).show();
}
}
3.释放资源
@Override
public void onDestroy() {
unregisterReceiver(receiver);
receiver = null;
super.onDestroy();
}
###12.通过WindowManager添加自定义View到窗体
1.添加权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
2.获取WindowManager
WindowManager mWM = (WindowManager) getSystemService(WINDOW_SERVICE);
3.自定义View
View mView;
4.设置布局参数
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
5.添加到窗体
mWM.addView(view, mParams);
6.移除View
mWM.removeView(view);
代码:
package com.hb.mobilesafe.activities; import java.util.List; import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast; import com.hb.demo_mobilesafe.R;
import com.hb.mobilesafe.bean.BlackInfo;
import com.hb.mobilesafe.dbdao.BlackNumberDao; public class BlackNumberActivity extends Activity {
private ImageView iv_empt;
private BlackNumberDao dao;
private List<BlackInfo> list;
private ListView lv_blacknumber;
private MyAdapter adapter;
private BlackInfo info;
private LinearLayout ll_progressbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_blacknumber);
lv_blacknumber=(ListView) findViewById(R.id.lv_blacknumber);
iv_empt=(ImageView) findViewById(R.id.iv_empt);
ll_progressbar=(LinearLayout) findViewById(R.id.ll_progressbar);
dao=new BlackNumberDao(this);
ll_progressbar.setVisibility(View.VISIBLE);
new Thread(){
public void run() {
SystemClock.sleep(2500);
runOnUiThread(new Runnable() {
public void run() {
ll_progressbar.setVisibility(View.INVISIBLE);
updateUI();
adapter=new MyAdapter();
lv_blacknumber.setAdapter(adapter);
} });
};
}.start(); }
private void updateUI() {
list = dao.queryAll();
if(list.size()>0){
iv_empt.setVisibility(View.INVISIBLE);
}else{
iv_empt.setVisibility(View.VISIBLE);
}
}
private class MyAdapter extends BaseAdapter{ @Override
public int getCount() { return list.size();
}
@SuppressWarnings("null")
@SuppressLint("ViewHolder") @Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view;
ViewHolder holder = null;
if(convertView!=null){
view=convertView;//复用历史缓存抄作
holder=(ViewHolder) view.getTag();
}else {
holder=new ViewHolder();
view=View.inflate(BlackNumberActivity.this, R.layout.blacknumber_item, null);
holder.tv_number=(TextView) view.findViewById(R.id.tv_number);
holder.tv_mode=(TextView) view.findViewById(R.id.tv_mode);
holder.iv_del=(ImageView) view.findViewById(R.id.iv_delete);
view.setTag(holder);
} /**
* 删除黑名单
*/
holder.iv_del.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
String num = list.get(position).getNumber();
dao.delete(num);
list.remove(num);
updateUI();
adapter.notifyDataSetChanged();
}
});
String mode = list.get(position).getMode();
if(mode.equals("1")){
holder.tv_mode.setText("电话拦截");
}else if(mode.equals("2")){
holder.tv_mode.setText("短信拦截");
}else{
holder.tv_mode.setText("全部拦截");
}
holder.tv_number.setText(list.get(position).getNumber());
return view;
} @Override
public Object getItem(int position) {
return null;
} @Override
public long getItemId(int position) {
return 0;
} }
class ViewHolder{
TextView tv_number;
TextView tv_mode;
ImageView iv_del; } public void addnumber(View view){
Intent intent=new Intent(BlackNumberActivity.this, AddNumberActivity.class);
startActivityForResult(intent, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(data!=null){
boolean result = data.getBooleanExtra("addnumber", false);
if(result){
Toast.makeText(BlackNumberActivity.this, "添加成功!", 0).show();
String number = data.getStringExtra("number");
String mode = data.getStringExtra("mode");
info = new BlackInfo();
info.setNumber(number);
info.setMode(mode);
list.add(0, info);//减少数据库查询的操作
updateUI();
adapter.notifyDataSetChanged();
}else{
Toast.makeText(BlackNumberActivity.this, result+"添加失败!", 0).show();
} }
}
}
package com.hb.mobilesafe.service; import java.lang.reflect.Method; import com.android.internal.telephony.ITelephony;
import com.hb.mobilesafe.dbdao.BlackNumberDao; import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
import android.util.Log; public class BlackNumberSafeService extends Service {
private static final String TAG = "BlackNumberSafeService";
private TelephonyManager tm;
private CallStatic listener;
private BlackNumberDao dao;
private SmsReceiver smsreceiver; @Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() { Log.i(TAG, "黑名单服务已经开启了");
System.out.println("黑名单服务已经开启了");
tm=(TelephonyManager) getSystemService(TELEPHONY_SERVICE);
listener=new CallStatic();
dao=new BlackNumberDao(this);
tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); smsreceiver=new SmsReceiver(); IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(smsreceiver, filter);
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "黑名单服务已经销毁");
System.out.println("黑名单服务已经销毁");
//销毁时关闭监听
tm.listen(listener, PhoneStateListener.LISTEN_NONE);
listener=null; //注销广播
unregisterReceiver(smsreceiver);
smsreceiver=null;
}
private class CallStatic extends PhoneStateListener { @Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state) {
//空闲状态
case TelephonyManager.CALL_STATE_IDLE:
break;
//响铃状态
case TelephonyManager.CALL_STATE_RINGING:
String mode = dao.query(incomingNumber);
if("1".equals(mode) || "3".equals(mode)){
System.out.println("电话已经响铃");
//挂断电话
endcall();
//删除记录
delteCallLog(incomingNumber);
}
break;
//关闭状态
case TelephonyManager.CALL_STATE_OFFHOOK:
break; }
} }
private void endcall() {
//ServiceManager获取不到,需要反射调用
try {
Class<?> clazz=getClassLoader().loadClass("android.os.ServiceManager");
Method method=clazz.getDeclaredMethod("getService", String.class);
IBinder binder=(IBinder) method.invoke(null, Context.TELEPHONY_SERVICE); //挂断电话需要用到AIDL,拷贝需要用到的AIDL文件,ITELEPHONY.AIDL和NeighboringcellInfo.aidl包名需要和原名一致
ITelephony iTelephony=ITelephony.Stub.asInterface(binder);
iTelephony.endCall();
System.out.println("电话挂断了");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
* @param 删除电话记录
*/
private void delteCallLog(final String incomingNumber) {
final ContentResolver resolver = getContentResolver();
final Uri parse = Uri.parse("content://call_log/calls");
//利用内容观察者,观察呼叫数据库的内容,如果有记录就删除
resolver.registerContentObserver(parse, true,new ContentObserver(new Handler()) { @Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//当内容观察者观察到数据库的内容变化时调用的方法
resolver.delete(parse, "number=?", new String[]{incomingNumber});
System.out.println("删除电话记录");
} } );
}
/**
* @pargram 短信拦截
*/
private class SmsReceiver extends BroadcastReceiver{ @Override
public void onReceive(Context context, Intent intent) {
Object[] objects=(Object[]) intent.getExtras().get("pdus");
for (Object object : objects) {
SmsMessage sms = SmsMessage.createFromPdu((byte[]) object);
String address = sms.getOriginatingAddress();
String mode = dao.query(address);
if("2".equals(mode) || "3".equals(mode)){
System.out.println("123456789");
abortBroadcast();
Log.i(TAG, "短信已经拦截了"); }
}
} } }
Android项目实战_手机安全卫士拦截骚扰的更多相关文章
- Android项目实战_手机安全卫士home界面
# 安全卫士主页面# ###1.GridView控件 1.与ListView的使用方式差不多,也要使用数据适配器,通过设置android:numColumns控制显示几列 2.通过指定android: ...
- Android项目实战_手机安全卫士splash界面
- 根据代码的类型组织包结构 1. 界面 com.hb.mobilesafe.activities 2. 服务 com.hb.mobilesafe.services 3. 业务逻辑 com.hb.mo ...
- Android项目实战_手机安全卫士程序锁
###1.两个页面切换的实现1. 可以使用Fragment,调用FragmentTransaction的hide和show方法2. 可以使用两个布局,设置visibility的VISIABLE和INV ...
- Android项目实战_手机安全卫士手机防盗界面
#安全卫士手机防盗# ###1.Activity的任务栈 1.类似一个木桶,每层只能放一个木块,我们放入木块和取出木块的时候只能从最上面开始操作 ###2.Android中的坐标系和android:content(内容),并在子View中添加把手和内容的布局```java <Sli ...
- Android项目实战_手机安全卫士进程管理
###1.设备进程信息获取获取设备运行进程 ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVI ...
- Android项目实战--手机卫士开发系列教程
<ignore_js_op> banner131010.jpg (71.4 KB, 下载次数: 0) 下载附件 保存到相册 2 分钟前 上传 Android项目实战--手机卫士01- ...
随机推荐
- HDU 4451 容斥原理
题目大意: n件衣服,m条裤子,k双鞋子进行搭配 妈妈指明了哪些衣服和裤子不能搭配,哪些裤子和鞋子不能搭配,问最后有几种搭配方法 先假设都能搭配 n*m*k 每次遇到衣服和裤子不能搭的,就要减一次k, ...
- kafka 在阿里云部署
https://blog.csdn.net/chenyulancn/article/details/79499401 https://www.cnblogs.com/yangtianle/p/8761 ...
- Android: MediaRecorder start failed
在某些机型上,MediaRecorder在调用start方法时,会出现start failed的错误,有一种可能是setVideoFrameRate导致的.要解决这个问题,只需要注释掉这条语句就可以了 ...
- Linux自己主动挂载第二块硬盘分区
改动/etc/fstab文件,如果挂载到/data文件夹.在最后一行加上 /dev/sdb1 /data ext4 defaults 1 1 最后结果例如以下: # # /etc/fstab # Cr ...
- 21行python代码实现拼写检查器
引入 大家在使用谷歌或者百度搜索时,输入搜索内容时,谷歌总是能提供很好的拼写检查,比方你输入 speling,谷歌会立即返回 spelling. 前几天,看到http://norvig.com/spe ...
- using gdb to debug c program
#include <stdio.h> static void display(int i, int *ptr); int main(void) { int x = 5; int *xptr ...
- java线程和线程池的使用
java线程和线程池 一.创建多线程的方式 java多线程非经常见.怎样使用多线程,怎样创建线程.java中有两种方式,第一种是让自己的类实现Runnable接口.另外一种是让自己的类继承Thread ...
- Android高仿UC浏览器和360手机卫士消息常驻栏(通知栏)
之前网上看了下自己定义消息栏,通知栏,了解到了Notification这个控件.发现UC浏览器等都是这样的类型,今天写个demo实现下.如图: 当中每一个button都有不同的功能.代码例如以下: p ...
- LeetCode 771. Jewels and Stones (宝石与石头)
题目标签:Hash Table 这一题很简单,题目给了两个string:J 和 S. 只要把J 里面的 char 放入HashSet,再遍历S找出有多少个石头是宝石. Java Solution: R ...
- 微信JS-SDK怎样使用
前两天要用到微信JS库的的一句话--wx.closeWindow();可是整个调用过程有点儿泪奔了.. .. 尽管开发人员平台上说的清清楚楚,可是使用起来就是not ok! 一,绑定域名 登录到微信公 ...