现在的位置: 首页 > 综合 > 正文

黑马程序员Android学习笔记——金山卫士项目——第四天

2017年03月27日 ⁄ 综合 ⁄ 共 10558字 ⁄ 字号 评论关闭

主要学习内容:

1)号码归属地查询UI设计

2)号码归属地查询的原理

3)号码归属地查询的代码实现

5)输入框抖动和震动效果

6)来电、去电号码归属地的显示

7)代码注册广播接受者的实现

8)自定义吐司显示号码归属地【实现号码归属地显示效果:加背景、加图片、加文字

首先看一下UI实现的效果:

                              
                                

                                         
                                         

通过UI效果可以清楚看到我们实现了哪些内容:第一幅图片的查询是查询过程中就在查数据库,动态加载电话号码信息;设置号码归属地是可选的;来电归属地显示效果通过自定义吐司实现不同效果。

查询原理:

第一种:联网查询;

第二种:把数据库 放在本地;

 

------1、百度上输入:手机号码归属地

-----2、数据库的来源,可以在淘宝上购买;

  买的数据不一定是Android下用的数据库;

  如果在Android上使用得自己做一个;把数据写到Android的数据库里;

 

号码归属地的查询实现

1、把数据库拷贝到assets目录并创建包com.itheima.mobilesafe.db.dao 包创建该类

   file:///android_aset/address.db 这种无法访问

   知识回顾:

WebView还可以加载图片String str = "file:///android_asset/icon.png";

在APP刚启动的时候,自动加载数据,将assets中的数据库复制到数据库。

private void copyDb() {
//只要你拷贝了一次,我就不要你再拷贝了
		try {
			File file=new File(getFilesDir(), "address.db");
			if(file.exists()&&file.length()>0){
				//正常了,不需要拷贝了
			    Log.i(TAG,"正常了,不需要拷贝了");
			}else{
				InputStream is=getAssets().open("address.db");
				
				FileOutputStream fos=new FileOutputStream(file);
				byte[] buffer=new byte[1024];
				int len=0;
				while(is.read(buffer)!=-1){
					fos.write(buffer,0,len);
				}
				
				is.close();
				fos.close();
				
				
				} 
			}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

关于电话号码的常识可以到百度百科中搜集资料,非常多的介绍。

关于数据库查询归属地的工具代码如下

package com.example.mobilesafe.db.dao;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class NumberAddressQueryUtils {
	private static String path="data/data/com.example.mobilesafe/files/address.db";
	                            
	/*
	 * 产一个号码进来,返回一个归属地回去
	 */
	public static String queryNumber(String number){
		String address=number;
		//path  把address.db这个数据库拷贝到data/data/包名/files/address.db
		SQLiteDatabase database=SQLiteDatabase.openDatabase(path, null,SQLiteDatabase.OPEN_READONLY);
		//手机号码 13  14  15 16  18
		//手机号码正则表达式
		if(number.matches("^1[34568]\\d{9}$")){
			//手机号码
			//Cursor cursor=database.rawQuery("select location from data2 where id=(select outkey from data1 where id=?)", new String[]{number.substring(0, 7)});
			Cursor cursor = database
					.rawQuery(
							"select location from data2 where id = (select outkey from data1 where id = ?)",
							new String[] { number.substring(0, 7) });
			while(cursor.moveToNext()){
				String location=cursor.getString(0);
		        address=location;
		        System.out.println("address"+address);
			}
			cursor.close();
		}else{
			//其它的电话号码
			switch(number.length()){
			case 3:
				address="匪警号码";
				break;
			case 4:
				address="模拟器";
				break;
				//10086
			case 5:
				address="客服电话";
				break;
			case 7:
				address="本地号码";
				break;
			case 8:
				address="本地号码";
				break;
				
				default:
					//处理长途电话
					if(number.length()>10&&number.startsWith("0")){
						Cursor cursor = database
								.rawQuery(
										"select location from data2 where area=?)",
										new String[] { number.substring(1, 3) });
						while(cursor.moveToNext()){
							String location=cursor.getString(0);
							address=  location.substring(0, location.length()-2);
						}
						cursor.close();
						//0855-353253445
						 cursor = database
								.rawQuery(
										"select location from data2 where area=?)",
										new String[] { number.substring(1, 4) });
						while(cursor.moveToNext()){
							String location=cursor.getString(0);
							address=  location.substring(0, location.length()-2);
						}
					}
					break;
			}
		}
		return address;
	}
}

实现输入框输入部分位数号码只能查找该号码归属地:输入框是有状态事件的

private EditText ed_phone;

ed_phone.addTextChangedListener(new TextWatcher() {
			/*
			 * 文本發生變化的時候回調
			 */
			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {
				// TODO Auto-generated method stub
				if(s.length()>=3){
					//查詢數據庫
					String address=NumberAddressQueryUtils.queryNumber(s.toString());
					result.setText(address);
				}
			}
			/*
			 * 文本發生變化之前變化(non-Javadoc)
			 * @see android.text.TextWatcher#beforeTextChanged(java.lang.CharSequence, int, int, int)
			 */
			@Override
			public void beforeTextChanged(CharSequence s, int start, int count,
					int after) {
				// TODO Auto-generated method stub
				
			}
			
			/*
			 * 文本發生變化之後回調(non-Javadoc)
			 * @see android.text.TextWatcher#afterTextChanged(android.text.Editable)
			 */
			@Override
			public void afterTextChanged(Editable s) {
				// TODO Auto-generated method stub
				
			}
		});	

输入框或者其它组件震动与抖动效果实现

抖动和振动效果_32

-----1、进入模拟器的APIDemo展示点击输入框振动效果(views/animation/shake);

      //shake 动摇;摇动;震动;握手的意思

------2、找系统sdk\samples\android-16\ApiDemos 并导入工程,如果有错误就解决;

-----3、找代码搜索代码

  功能--》布局文件---》代码

-----3、找到抖动代码移植;并演示;

-----4、介绍插入器(interpolator

------5、看APIDemoviews/animation/interpolators 的各种动画

   Accelerate 加速动画  decelerate 减速动画 

Interpolator类似一个函数,计算动画如何播放

          /**
	 * 系统提供的振动服务
	 */
	private Vibrator vibrator;

vibrator=(Vibrator) getSystemService(VIBRATOR_SERVICE);

如果输入查询号码为空时查询,实现震动效果:

if(TextUtils.isEmpty(phone)){
			Toast.makeText(this, "号码为空", 0).show();
			Animation shake = AnimationUtils.loadAnimation(this, R.anim.shake);
			ed_phone.startAnimation(shake);
			//当电话号码为空的时候就去震动手机提醒用户
			vibrator.vibrate(1000);
		/*	long[] pattern={200,200,300,300,1000,2000};
			vibrator.vibrate(pattern, -1);*/
			
			return;
}

震动权限:

<uses-permission android:name="android.permission.VIBRATE"/>

来电号码归属地的显示

引入:1、演示打进电话,说明系统的拨号不能改。2、展示土司;

-----1、创建后台监听来电服务AddressService 并在功能清单文件注册;

检查是否有权限:

<uses-permission android:name="android.permission.READ_PHONE_STATE" /> 

-----1、在AddressService 服务里面注册来电状态

(TelephoneManager)
   tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

-------3、自定义监听类MyPhoneStateListener

private class MyListenerPhone extends PhoneStateListener{

		@Override
		public void onCallStateChanged(int state, String incomingNumber) {
		//state:状态,incomingNumber:来电号码
			super.onCallStateChanged(state, incomingNumber);
            switch(state){
            case TelephonyManager.CALL_STATE_RINGING:   //来电铃声想起
            	//查询数据库的操作
            	String address=NumberAddressQueryUtils.queryNumber(incomingNumber);
            	//Toast.makeText(getApplicationContext(), address, 1).show();
            	myToast(address);
            	break;
            case TelephonyManager.CALL_STATE_IDLE: //电话的空闲状态
            	//把这个View移出
            	if(view!=null){
            		wm.removeView(view);
            	}
            	
            	
            	break;
            	
            	default:
            		break;
            }
		}
		
	}

取消监听:

tm.listen(listener, PhoneStateListener.LISTEN_NONE);
listener = null;

-------3、在SettingActivity设置中心里配置设置,当点击开启的时候就启动服务,否则相反。然后演示;

 

    布局文件:

<com.itheima.mobilesafe.ui.SettingItemView
        android:id="@+id/siv_show_address"
        itheima:title="设置号码归属地显示"
        itheima:desc_on="号码归属地显示已经打开"
        itheima:desc_off="号码归属地显示已经关闭"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

SettingActivity里的代码:

//设置号码归属地显示

//设置归属地显示控件
	    siv_show_address=(SettingItemView) findViewById(R.id.siv_show_address);
	    showAddress=new Intent(this,AddressService.class);
	    boolean isServiceRunning=ServiceUtils.isServiceRunning(SettingActivity.this, "com.example.mobilesafe.service.AddressService");
	    if(isServiceRunning){
	    	//监听来电服务是否开启的
	    	siv_show_address.setChecked(true);
	    	
	    }else{
	    	siv_show_address.setChecked(false);
	    }
	    
	    
	    siv_show_address.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if(siv_show_address.isChecked()){
					//变为非选中状态
					siv_show_address.setChecked(false);					
					stopService(showAddress);
				}else{
					//选中状态
					siv_show_address.setChecked(true);
					startService(showAddress);
				}
				
			}
		});

------3、创建一个类ServiceStatusUtils 里面的方法isServiceRunning()校验检查一个服务是否开启;

public class ServiceUtils {
	/*
	 * 某个服务是否还活着
	 */
	public static boolean isServiceRunning(Context context,String serviceName){
		//校验服务是否还活着
		ActivityManager am=(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
		List<RunningServiceInfo> infos=am.getRunningServices(100);
		for(RunningServiceInfo info:infos){
			String name=info.service.getClassName();
			if(serviceName.equals(name)){
				return true;
			}
		}
		return false;
	}
}

去电号码归属地的显示

------1、创建广播接收者OutCallReceiver 并注册;监听电话打出去的广播,需要意图;

public class OutCallReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub

		//这就是我们拿到的拨出去的电话号码
		String phone=getResultData();
		//查询数据库
		String address=NumberAddressQueryUtils.queryNumber(phone);
		
		Toast.makeText(context, address, 1).show();
	
	}

}


<receiver  android:name="com.itheima.mobilesafe.receiver.OutCallReceiver">
  <intent-filter >
  <action android:name="android.intent.action.NEW_OUTGOING_CALL"></action>
     </intent-filter>
 </receiver>

------2、需要监听具体意图和权限;

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

代码注册广播接收者

 

 

------1、演示功能:演示勾选的来电去电、 进程杀掉时的来电去电;

 

------2、四大组件 :

   activity 、 service content provider

   Broadcast Receiver

 都需要在功能清单文件注册。

Broadcast Receiver不仅可以在功能清单文件注册还可以用代码注册;

 

-------3.移植到AddressService 里和服务生命周期一样。并且演示。

 注册在onCreate()

//代码注册一个广播接收者

receiver = new OutCallReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.NEW_OUTGOING_CALL")registerReceiver(receiver, filter);
 取消在onDestroy():

 //代码取消注册一个广播接收者

 unregisterReceiver(receiver);
 receiver = null;

自定义土司显示归属地

演示:目前的土司的缺陷,比如:无法控制消失、界面丑

-----1、看Toast的源代码

-----2、查看toast布局文件的背景目录:\sdk\platforms\android-16\data\res\values\themes.xml

 

-----3、实现代码 

private WindowManager wm;
public void showMyToast(String address) {
view = new TextView(this);
view.setTextSize(20);
view.setTextColor(Color.RED);
view.setText(address);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
    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;
wm.addView(view, params);
}

------3、演示发现无法把土司一直停留页面;

 

------4、处理电话挂断情形

case TelephonyManager.CALL_STATE_IDLE:
if(view != null){
 wm.removeView(view);
 view = null;
}
break;

更改归属地的背景风格

准备:安装市场类似软件(金山软件)开启另外一个模拟器

-------1、创建布局文件toast_address.xml  图片(ic_menu_call

-------2、借用市场软件的图片背景(call_locate_gray

 

<LinearLayout 
    android:background="@drawable/call_locate_gray"
    android:gravity="center_vertical"
    android:orientation="horizontal" >
 
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_menu_call" />
 
    <TextView
        android:id="@+id/tv_location"
        android:text="归属地”
        android:textSize="20sp" 
        android:textColor="#000000"
        />
 
</LinearLayout>
<span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">------3、演示金山的软件的</span>

-------4、基于ui_setting_item_view自定义点击条目(金山里去找\res\\drawable\jiantou1_pressed.png

-------5、基于SettingItemView 自定义SettingClickView 并只处理标题和内容描述内容;

-------6、在SettingActivity 处理点击事件;

//设置归属地的背景
scv_changebg = (SettingClickView) findViewById(R.id.scv_changebg);
final String [] items = {"半透明","活力橙","卫士蓝","金属灰","苹果绿"};
scv_changebg.setTitle("归属地提示框风格");
int which = sp.getInt("which", 0);
scv_changebg.setDesc(items[which]);
scv_changebg.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new Builder(SettingActivity.this);
builder.setTitle("归属地提示框风格");
int which = sp.getInt("which", 0);
builder.setSingleChoiceItems(items, which, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Editor editor = sp.edit();
editor.putInt("which", which);
scv_changebg.setDesc(items[which]);
editor.commit();
dialog.dismiss();
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
});

----------7、在实现具体的逻辑

   int which = sp.getInt("which", 0);
//	"半透明","活力橙","卫士蓝","金属灰","苹果绿"
int [] bgs = {R.drawable.call_locate_white,R.drawable.call_locate_orange
,R.drawable.call_locate_blue,R.drawable.call_locate_gray,R.drawable.call_locate_green};
view = (View) View.inflate(this, R.layout.toast_address, null);
view.setBackgroundResource(bgs[which])

抱歉!评论已关闭.