MobileSafeNotes Day06

##Day06##

#6.1外拨电话显示号码归属地# (重点,复习代码注册广播接受者)

前边实现了来电显示归属地的操作,但是别忘了还有这样一个操作,即拨打电话时,显示号码归属地
思考,既然要在外拨电话显示归属地,那首先要去监听一下外拨电话的事件,监听外拨电话的事件,一般我们都是通过广播实现的,


1.清单文件注册
	在清单文件中注册的广播接受者,在程序一安装,广播接受者就会生效,一直有效,程序员无法控制广播的开始和结束
2.代码注册广播接受者
	注册广播接受者代码所在的程序运行,广播接受者才会生效,杀死进程,广播接受者就会失效,同时我们是可以去注销广播接受者的,程序员是可以去控制广播接受者的打开和关闭

代码注册广播接受者步骤
1.在addressservice中,创建一个广播接受者
	/**
	 * 外拨电话的广播接受者
	 * @author Administrator
	 *
	 */
	public class MyOutGoingCall extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			
		}
		
	}
2.在oncreate方法中new一个广播接受者,同时创建一个intentfilter,并注册广播接受者
	//注册外拨电话的广播接受者
	//需要哪些元素,需要权限:<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
	//1.广播接受者
	myOutGoingCall = new MyOutGoingCall();
	//2.广播的过滤条件
	IntentFilter intentFilter = new IntentFilter();
	intentFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");//设置广播接受者要接受的广播事件
	//3.注册广播接受者
	registerReceiver(myOutGoingCall, intentFilter);
3.在ondestory方法中注销广播接受者
	//注销广播接受者
	unregisterReceiver(myOutGoingCall);
4.在广播接受者的onreceive方法中执行相应的操作
	@Override
	public void onReceive(Context context, Intent intent) {
		//获取外拨电话的号码,获取广播接受者执行操作的数据
		String resultData = getResultData();
		//查询号码归属地
		String queryAddress = addressDao.queryAddress(resultData, context);
		if (!TextUtils.isEmpty(queryAddress)) {
			showToast(queryAddress);
		}
	}

#6.2归属地提示框风格# (重点关注setSingleChoiceItems方法)

1.复制Settingview的自定义控件,布局文件,并修改(将checkbox改成imagview),同时删除关于checkbox及自定义属性的相关操作,在settingclickview中的view.inflate中加载布局文件
2.在设置中心使用
	<cn.itcast.mobilesafexian02.ui.SettingClickView
    android:id="@+id/scv_setting_changedbg"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    ></cn.itcast.mobilesafexian02.ui.SettingClickView>

3.在代码中使用
	/**
	 * 更改归属地提示框风格
	 */
	private void changedBg() {
		final String[] items={"半透明","活力橙","卫士蓝","金属灰","苹果绿"};
		//初始化自动控件中的控件的值
		scv_setting_changedbg.setTitle("归属地提示框风格");
		scv_setting_changedbg.setDes(items[sp.getInt("which", 0)]);//根据保存的选项的索引,去动态获取items相应的文本
		scv_setting_changedbg.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				AlertDialog.Builder builder = new Builder(SettingActivity.this);//必须告诉dialog应该显示到那个activity中
				//设置图标
				builder.setIcon(R.drawable.ic_launcher);
				//设置标题
				builder.setTitle("归属地提示框风格");
				//设置单选按钮
				//items : 单选按钮的文本的数组
				//checkedItem : 当前选中的选项位置
				//listener : onclickListener
				builder.setSingleChoiceItems(items, sp.getInt("which", 0), new DialogInterface.OnClickListener() {
					//which : 点击选中的选择项的索引
					@Override
					public void onClick(DialogInterface dialog, int which) {
						//保存选中的选项
						Editor edit = sp.edit();
						edit.putInt("which", which);
						edit.commit();
						//设置自定义控件的描述信息
						scv_setting_changedbg.setDes(items[which]);
						//隐藏对话框
						dialog.dismiss();
					}
				});
				//设置取消按钮
				builder.setNegativeButton("取消", null);//null其实就相当于不给按钮设置点击事件,在dialog中会默认执行dialog.dimiss();
				//显示dialog
				builder.show();
			}
		});
	}
4.根据保存的选项索引,设置addressservice中的showToast方法view的背景
	a.
		int[] bgcolor = new int[] { 
			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 };
	b.
		//设置view对象的背景,根据保存的选项索引从bgcolor图片数组中获取相应的图片
		view.setBackgroundResource(bgcolor[sp.getInt("which", 0)]);

#6.3更改归属地提示框显示位置# (params.x的含义)

	//设置控件的显示位置, |  表示前后两个属性都有效果,两个相对应的属性,默认是左边和上边
     params.gravity = Gravity.LEFT | Gravity.TOP;
     params.x = 100;//x表示的不是坐标,而是距离边框的距离,根据Gravity来设置的,LEFT就是距离左边框的距离,ringht就是距离右边框的距离
     params.y = 100;//y和x是相同的含义,根据top或者bottom来时设置距离顶部或者底部的距离

#6.4归属地提示框位置界面#

1.在设置中心中添加归属地提示框位置的条目
2.创建DragViewActivity,清单文件配置,创建界面
	<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"
	    android:orientation="vertical" >
	
	    <LinearLayout
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:background="@drawable/call_locate_orange"
	        android:gravity="center_vertical"
	        android:orientation="horizontal" >
	
	        <ImageView
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content"
	            android:src="@drawable/ic_launcher" />
	
	        <TextView
	            android:id="@+id/tv_toastcustom_address"
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content"
	            android:text="双击居中"
	            android:textColor="#ffffff"
	            android:textSize="18sp" />
	    </LinearLayout>
	    <!-- layout_alignParentBottom : 在父控件的下方 -->
	    <TextView 
	        android:layout_width="match_parent"
	        android:layout_height="wrap_content"
	        android:text="按住提示框拖到任意位置\n按手机返回键立即生效"
	        android:layout_alignParentBottom="true"
	        android:textSize="18sp"
	        android:textColor="#000000"
	        android:gravity="center"
	        android:background="@drawable/call_locate_blue"
	        android:padding="10dp"
	        />
	</RelativeLayout>

#6.5随着手指移动而移动# (重点!!!)

1.给控件设置触摸监听事件
	ll_dragview_toast.setOnTouchListener(new OnTouchListener() {
		//v : 控件
		//event : 当前执行的事件
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			//获取当前的事件
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				//按下的事件
				System.out.println("按下了.....");
				break;
			case MotionEvent.ACTION_MOVE:
				//移动的事件
				System.out.println("移动了.....");
				break;
			case MotionEvent.ACTION_UP:
				//抬起的事件
				System.out.println("抬起了.....");
				break;
			}
			//True if the listener has consumed the event, false otherwise.
			//true:消费了,执行了,false:不执行
			//返回true表明我们down已经执行完了,可以执行移动和抬起的事件了
			return true;
		}
	});
2.随着手指移动而移动,按照步骤来
		//设置成成员变量是为了在移动的事件能够进行使用
		private int startX;
		private int startY;

		//v : 控件
		//event : 当前执行的事件
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			//获取当前的事件
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				//按下的事件
				System.out.println("按下了.....");
				//1.记录开始的x和y的坐标
				startX = (int) event.getRawX();
				startY = (int) event.getRawY();
				break;
			case MotionEvent.ACTION_MOVE:
				//移动的事件
				System.out.println("移动了.....");
				//2.获取新的位置的x和y的坐标
				int newX = (int) event.getRawX();
				int newY = (int) event.getRawY();
				//3.计算移动的偏移量
				int dX = newX-startX;
				int dY = newY-startY;
				//4.让控件移动相应的偏移量,重新绘制控件
				int l = ll_dragview_toast.getLeft();//获取控件距离屏幕左边框的距离,原控件(没移动前)的距离
				int t = ll_dragview_toast.getTop();//获取控件距离屏幕顶部的距离
				//移动相应的偏移量
				l+=dX;
				t+=dY;
				//r:表示控件右边框距离屏幕左边框的距离
				ll_dragview_toast.layout(l, t, l+ll_dragview_toast.getWidth(), t+ll_dragview_toast.getHeight());//重新绘制控件
				//5.更新开始的坐标
				startX = newX;
				startY = newY;
				break;
			case MotionEvent.ACTION_UP:
				//抬起的事件
				System.out.println("抬起了.....");
				break;
			}
			//True if the listener has consumed the event, false otherwise.
			//true:消费了,执行了,false:不执行
			//返回true表明我们down已经执行完了,可以执行移动和抬起的事件了
			return true;
		}

#6.6动态设置号码归属地提示框位置#

	1.在ontouch的抬起事件中保存控件的坐标
		//获取坐标
		int x = ll_dragview_toast.getLeft();//获取移动过的控件的距离屏幕左边框的距离
		int y = ll_dragview_toast.getTop();
		//保存控件的坐标,而不是手指的坐标
		Editor edit = sp.edit();
		edit.putInt("x", x);
		edit.putInt("y", y);
		edit.commit();
	2.在addressService的显示归属地提示框的方法中根据保存的坐标设置控件的坐标
		 //设置控件的显示位置,   |  表示前后两个属性都有效果,两个相对应的属性,默认是左边和上边
     	params.gravity = Gravity.LEFT | Gravity.TOP;
     	params.x = sp.getInt("x", 100);//x表示的不是坐标,而是距离边框的距离,根据Gravity来设置的,LEFT就是距离左边框的距离,ringht就是距离右边框的距离
    	params.y = sp.getInt("y", 100);//y和x是相同的含义,根据top或者bottom来时设置距离顶部或者底部的距离

#6.7回显位置# (重点)

android中所有控件,正常显示,分为三步骤
	1.测量控件的宽高,oncreate执行完,测量控件才会完成
	2.分配控件位置
	3.绘制控件

回显操作
	1.获取保存的x和y的坐标
		int x = sp.getInt("x", 0);
		int y = sp.getInt("y", 0);
		System.out.println("x:"+x+"   y:"+y);
	2.获取控件的属性,并设置相应的属性
		RelativeLayout.LayoutParams params = (LayoutParams) ll_dragview_toast.getLayoutParams();//获取控件的属性参数
		//设置控件距离父控件左边框的距离
		params.leftMargin = x;
		//设置控件距离父控件顶部的距离
		params.topMargin = y;
		//重新设置控件的属性
		ll_dragview_toast.setLayoutParams(params);

控件变形
	原因:因为保存的控件的坐标是左上角的坐标,所以当控件移出屏幕在进行回显操作时,在重新绘制控件时发现现存的空间放不下控件,所以控件自作聪明对自己进行压缩处理,以便让用户能过完全看到自己
 解决控件移出屏幕的问题
	1.在移动的事件中,绘制控件之前
		//判断控件是否移出屏幕
		if (l < 0 || r > width || t < 0 || b > height - 25) {
				break;
		}
	  因为需要用到屏幕的宽高,所以
	2.获取屏幕宽高
		//获取屏幕宽度
		WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
		//int width = windowManager.getDefaultDisplay().getWidth();
		DisplayMetrics outMetrics = new DisplayMetrics();//白纸
		windowManager.getDefaultDisplay().getMetrics(outMetrics);//给白纸指定宽高
		width = outMetrics.widthPixels;
		height = outMetrics.heightPixels;

#6.8多击事件#(重点,原理)

1.单击事件 : 一组事件的集合,比如按下+抬起表示一个事件
2.触摸事件 : 单个事件,比如按下就是按下事件,抬起就是抬起事件

单击事件和触摸事件一起使用的时候,触摸事件要返回return  false,不能返回return true;因为先执行的触摸事件,在执行单击事件,触摸事件中返回fasle表示事件拦截,但是在单击事件中按下的事件其实是单击事件的前半部分,所以为了单击事件的成功,所以在单击事件中把按下的事件处理了

所有根据触摸控件有关的操作,都是先执行触摸事件,在执行其他事件,
其实所有的触摸控件的操作,内部实现都是通过触摸事件来实现的

具体实现
	long[] mHits = new long[2];
	/**
	 * 双击居中事件
	 */
	private void doubleClick() {
		ll_dragview_toast.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				//拷贝数组的操作
				System.arraycopy(mHits, 1, mHits, 0, mHits.length-1);
		        mHits[mHits.length-1] = SystemClock.uptimeMillis();//获取距离开机时间的毫秒值(手机睡眠的时间)
		        if (mHits[0] >= (SystemClock.uptimeMillis()-500)) {//判断是否是第三次点击
		        	//双击居中,因为点击控件进行操作的,所以控件已经有了,那就可以去执行重新绘制操作
		        	int l = (width - ll_dragview_toast.getWidth())/2;
		        	int t = (height - ll_dragview_toast.getHeight() -25)/2;
		        	ll_dragview_toast.layout(l, t, l+ll_dragview_toast.getWidth(), t+ll_dragview_toast.getHeight());
		        	//保存坐标,为回显做准备
		        	Editor edit = sp.edit();
					edit.putInt("x", l);
					edit.putInt("y", t);
					edit.commit();
		        	
		        }
			}
		});
	}

#6.9细节的处理#

1.在布局文件中设置两个一模一样的textview,分别显示在父控件的上方和下方
2.在移动事件进行判断
				//2.判断是否大于屏幕高度的一半
				if (top > height/2) {
					//下方隐藏,上方显示
					tv_dragview_bottom.setVisibility(View.INVISIBLE);
					tv_dragview_top.setVisibility(View.VISIBLE);
				}else{
					//上方隐藏,下方显示
					tv_dragview_top.setVisibility(View.INVISIBLE);
					tv_dragview_bottom.setVisibility(View.VISIBLE);
				}
3.回显时也进行判断
	//设置回显时textivew的显示和隐藏
	if (y > height/2) {
		//下方隐藏,上方显示
		tv_dragview_bottom.setVisibility(View.INVISIBLE);
		tv_dragview_top.setVisibility(View.VISIBLE);
	}else{
		//上方隐藏,下方显示
		tv_dragview_top.setVisibility(View.INVISIBLE);
		tv_dragview_bottom.setVisibility(View.VISIBLE);
	}

#6.10电话界面控件随着手指移动而移动# (重点)

TYPE_PRIORITY_PHONE : 优先于电话的类型
必须添加一个权限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

1.在addressService中给view增加触摸事件,修改移动事件中操作
				params.x += dX;
				params.y += dY;

				// 实现坐标和控件实际对应,防止控件坐标移出屏幕,如果params.x移出屏幕,那就设置params.x极限值
				if (params.x < 0) {
					params.x = 0;
				}
				if (params.x > width - view.getWidth()) {
					params.x = width - view.getWidth();
				}
				if (params.y < 0) {
					params.y  = 0;
				}
				if (params.y > height - view.getHeight() -25 ) {
					params.y = height - view.getHeight() -25;
				}
				//更新控件
				windowManager.updateViewLayout(view, params);
2.修改控件的params属性设置,将flags中的不可触摸注释,同时修改type为TYPE_PRIORITY_PHONE,同时增加权限 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
	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_PRIORITY_PHONE;// 控件的类型,显示窗体上的类型,toast天生没有触摸事件

#6.11小火箭#

1.触摸事件
	iv_rocket.setOnTouchListener(new OnTouchListener() {
		// 设置成成员变量是为了在移动的事件能够进行使用
		private int startX;
		private int startY;

		// v : 控件
		// event : 当前执行的事件
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			//开启动画
			animationDrawable.start();//在低版本中不太兼容
			// 获取当前的事件
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				// 按下的事件
				System.out.println("按下了.....");
				// 1.记录开始的x和y的坐标
				startX = (int) event.getRawX();
				startY = (int) event.getRawY();
				break;
			case MotionEvent.ACTION_MOVE:
				// 移动的事件
				System.out.println("移动了.....");
				// 2.获取新的位置的x和y的坐标
				int newX = (int) event.getRawX();
				int newY = (int) event.getRawY();
				// 3.计算移动的偏移量
				int dX = newX - startX;
				int dY = newY - startY;
				// 4.让控件移动相应的偏移量,重新绘制控件
				int l = iv_rocket.getLeft();// 获取控件距离父控件左边框的距离,原控件(没移动前)的距离
				int t = iv_rocket.getTop();// 获取控件距离父控件顶部的距离
				// 移动相应的偏移量
				l += dX;
				t += dY;
				int r = l + iv_rocket.getWidth();
				int b = t + iv_rocket.getHeight();
				// r:表示控件右边框距离屏幕左边框的距离
				iv_rocket.layout(l, t, r, b);// 重新绘制控件

				// 5.更新开始的坐标,为了让每次都是新的位置
				startX = newX;
				startY = newY;
				break;
			case MotionEvent.ACTION_UP:
				// 抬起的事件
				//判断是否是底部中间的位置
				if (iv_rocket.getTop() > 290  && iv_rocket.getLeft() > 100 && iv_rocket.getLeft() < 200) {
					sendRocket();
					iv_rocket_m.startAnimation(alphaAnimation);//开始执行动画
					iv_rocket_t.startAnimation(alphaAnimation);
				}
				break;
			}
			// True if the listener has consumed the event, false otherwise.
			// true:消费了,执行了,false:不执行
			// 返回true表明我们down已经执行完了,可以执行移动和抬起的事件了
			return true;
		}
	});

2.发送小火箭
	/**
	 * 发射小火箭
	 */
	protected void sendRocket() {
		// 执行操作的时候是在线程中当执行逐渐减少操作的

		new Thread() {
			public void run() {
				for (int i = 0; i < 40; i++) {
					SystemClock.sleep(10);
					// 运行在主线程,内部封装了一个handler
					runOnUiThread(new Runnable() {

						@Override
						public void run() {
							int top = iv_rocket.getTop() - 15;// 是在不断的减小距离顶部的距离
							// 重新绘制控件
							iv_rocket.layout(iv_rocket.getLeft(), top,
									iv_rocket.getLeft() + iv_rocket.getWidth(),
									top + iv_rocket.getHeight());
						}
					});
				}
				runOnUiThread(new Runnable() {
					
					@Override
					public void run() {
						iv_rocket_t.clearAnimation();//消除动画
						iv_rocket_m.clearAnimation();
					}
				});
			};
		}.start();
	}
	
3.帧动画和渐变动画
	帧动画
		1.res -> anim -> xxx.xml
		<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
		    android:oneshot="false"><!-- oneshot : 是否执行一次   true:执行一次  false:循环执行 -->
		    <item android:drawable="@drawable/desktop_rocket_launch_1" android:duration="200" />
		    <item android:drawable="@drawable/desktop_rocket_launch_2" android:duration="200" />
		</animation-list>
		2.控件使用
		android:background="@anim/rocket"
		3.代码中使用
		animationDrawable = (AnimationDrawable) iv_rocket.getBackground();
		//开启动画
		animationDrawable.start();//在低版本中不太兼容
	渐变动画
		1.创建一个渐变动画
			alphaAnimation = new AlphaAnimation(0, 1);
			alphaAnimation.setDuration(2000);
		2.开启动画
			iv_rocket_m.startAnimation(alphaAnimation);//开始执行动画
		3.清除动画
			iv_rocket_t.clearAnimation();//消除动画
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值