MobileSafeNotes Day02

##Day02##

#2.1实现TextView滚动效果# ***

打开HomeActivity.java,找到布局文件setContentView(R.layout.activity_home);
ctrl+左键进入activity_home.xml布局文件,在GridView上方添加一个TextView,如下方式1:

第一种方式:在布局文件中实现

	<TextView 
	        android:layout_width="match_parent"
	        android:layout_height="wrap_content"
	        android:text="手机卫士,真64核杀毒引擎,超神速度,打开7次可以召唤神龙,辅助杀毒!!!"
	        android:singleLine="true"
	        android:ellipsize="marquee"
			android:marqueeRepeatLimit="marquee_forever"
	        android:focusableInTouchMode="true"
	        android:focusable="true"
	        />
	<!-- 

		singleLine : 单行显示 (效果是后边有省略号,隐藏了后边的内容,要想全部显示,使用ellipsize滚动显示)
		ellipsize : marquee 滚动
		ellipsize的五个属性:	
		none :  效果是后边没有省略号,省略了后面的内容
	    start :  效果是前面有省略号,隐藏了前面的内容  
	    middle : 效果是中间有省略号,隐藏了中间的内容
	    end :  效果是后边有省略号,隐藏了后面的内容
	    marquee : 效果是滚动,获取了焦点之后才能滚动
	    android:marqueeRepeatLimit="marquee_forever" : 永远滚动  相当于 marquee_forever = -1 
	    focusableInTouchMode : 触摸获取焦点
	    focusable :是否拥有焦点功能 true:可以获取 false: 不可以 android中默认的滚动次数是3
	    
	    -->

注意:
button继承自TextView,所以TextView中拥有的属性button也会拥有,但button拥有的属性,TextView不一定拥有,比如,textview本身没有焦点的功能,所以在textView中添加focusableInTouchMode(触摸获取焦点)之后还要添加focusable(是否拥有焦点功能)属性,而button本身拥有焦点功能,所以不需要在添加focusable
	

第二种方式:自定义textiview实现自动获取焦点(自定义控件 比较牛的一种方式)  **

目的:textview本身没有焦点功能,所以自定义一个textView控件,让它主动去获取焦点,来实现textView的滚动效果
步骤:创建一个自定义控件(Home_textView.java)让它继承自textView

	public class Home_TextView extends TextView {

		//有三个构造方法	

		//1.有一个参数的构造方法  (在代码中使用的时候调用)
		public Home_TextView(Context context) {
			super(context);
		//new一个textview时,需要一个context,所以调用的是这个一个参数的构造方法
	//		TextView textView = new TextView(context);
		}

		//2.有两个参数的构造方法 (在布局文件使用的时候调用,反射转化成了相应的代码,在代码中new,AttributeSet参数:会保存控件的所有属性)
		public Home_TextView(Context context, AttributeSet attrs) {
			super(context, attrs);
			// TODO Auto-generated constructor stub
		}

	//3.有三个参数的构造方法 (在布局文件使用的时候调用,比两个参数的多了样式文件(defStyle),一般不用)
		public Home_TextView(Context context, AttributeSet attrs, int defStyle) {
			super(context, attrs, defStyle);
			// TODO Auto-generated constructor stub
		}

		//是否获取焦点(主动去获取焦点,来实现textView的滚动效果)
		@Override
		public boolean isFocused() {
			//true:获取焦点   false:不获取焦点
			return true;
		}
	}

如何使用已经定义好的 Home_TextView控件:
 选中Home_TextView-->右键Copy Qualified Name-->在activity_home.xml文件中,用拷贝的全类名替换目标textview的名称即可

注:
1.布局文件中的所有控件,都可以用代码来实现,也就是在安卓开发中,完全可以不写布局文件,直接用代码写。
用代码写布局文件:可以防止别人反编译看到你的布局文件,但坏处是用代码去写布局文件很繁琐,时间成本会很高
2:布局文件其实是辅助程序运行的,在运行时,通过反射转化成相应的代码,即布局文件中的操作,都是转化成代码运行的
3:在values下的styles.xml文件中定义一下,就可以使用defStyle样式文件了

#2.2给条目增加点击事件# ***

整个主界面完成后,开始实现每个模块的功能,首先给设置中心添加一个“是否提示更新”的功能,	

条目:主界面上的九个模块 比如设置中心就是一个条目

给条目增加点击事件,其实就是给gridview增加条目点击事件

在HomeActivity.java中增加如下代码:

gv_home_gridview.setOnItemClickListener(new OnItemClickListener() {
		//parent :(通过debug查看)其实就是一个gridview
		//view : 代表就是条目的view对象 其实就是一个linearlayout
		//position :条目的位置
		//id : 条目的id
		@Override
		public void onItemClick(AdapterView<?> parent, View view,
				int position, long id) {
			//判断当前点击的条目的位置
			switch (position) {
			case 0: //手机防盗
				//判断是否已经设置过密码,没有,弹出设置密码对话框,有,弹出输入密码对话框
				if (TextUtils.isEmpty(sp.getString("password", ""))) {
					showSetPassWordDialog();
				}else{
					showEnterPassWordDialog();
				}
				break;
			case 8: //设置中心(从主界面跳转到设置中心界面)
				Intent intent = new Intent(HomeActivity.this,SettingActivity.class);
				startActivity(intent);
				break;
			}
		}
	});

	在new Intent 中需要SettingActivity.class 所以new一个SettingActivity.java文件(继承自Activity),并重写onCreate方法
	在清单文件中配置<activity android:name=".settingActivity"></activity>

新建SettingActivity.java如下:
public class SettingActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//加载布局
		setContentView(R.layout.activity_setting);
	}
}

新建一个activity_setting.xml布局,以便完成加载布局
activity_setting.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:itcast="http://schemas.android.com/apk/res/cn.itcast.mobilesafexian02"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<TextView
    android:id="@+id/textView1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="设置中心" 
    android:textSize="25sp"
    android:gravity="center_horizontal"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:background="#8866ff00"
	/>
<cn.itcast.mobilesafexian02.ui.SettingView
    android:id="@+id/sv_setting_update"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    itcast:title="提示更新"
    itcast:des_on="打开提示更新"
    itcast:des_off="关闭提示更新"
    ></cn.itcast.mobilesafexian02.ui.SettingView>

#2.3自定义组合控件# ***
完成了条目点击事件,并且跳转到设置中心界面后,在设置中心添加提示更新的条目

设置中心肯定不只有提示更新这一条条目,随着后期的开发,会有很多的条目,假如又来了一个和提示信息样式一样的条目,无经验的开发师肯定会复制一份提示更新条目的布局文件,改改文字完事,但是如果有七八个条目(很多了),这样复制,布局文件会很大  android系统在渲染界面的时候,渲染的就是布局文件(xml文件),所以布局文件越大,渲染的速度就会变卡,变得越慢,所以一般都不会这么干,在java中重复的代码是可以向上抽取的,同样在android中也是可以向上抽取的,把它抽取到一个自定义控件中去使用,叫做自定义组合控件


注意:
这时不再像上边自定义控件只继承textView,因为它是组合了多个自定义控件,所以如果是相对布局就继承relativeLayout,如果是线性布局就继承Linearlayout

注意:
要实现效果中的一条线,可以在布局文件中添加一个view标签
<view 
	android:layout_width = "match_parent"
	android:layout_height = "0.5dp"  //线的粗细
	android:background="#770000"	//线的颜色
	android:layout_below="@id/tv_setting_des" //指定线在描述信息的下方
	android:layout_marginTop="5dp" //指定线条距离描述信息的外上边距			
/>

创建并使用自定义组合控件步骤:
建一个示例工程,将布局文件中重复的代码拷贝到示例工程的布局文件中,然后再创建自定义组合控件settingView.java,继承自relativeLayout,然后和自定义控件一样在settingView.java中实现三个方法(右键-->source-->generate Constructors from Superclass),代码如下:
	

public class SettingView extends RelativeLayout {

		//在代码中使用的时候调用
		public SettingView(Context context) {
			super(context);
			//实现不管采用哪种方式使用自定义组合控件,都能调用init()方法来添加布局文件
			init();
			
		}
		//在布局文件使用的时候调用 ,多了样式文件
		public SettingView(Context context, AttributeSet attrs, int defStyle) {
			super(context, attrs, defStyle);
			
			init();
			
		}
		//在布局文件使用的时候调用
		public SettingView(Context context, AttributeSet attrs) {
			super(context, attrs);
			
			init();
			
		}
		/**
		 * 添加布局文件
		 */
		private void init(){
			//添加布局文件
			//1.创建一个textView  参数getcontext()在自定义控件或是单元测试中使用的比较多,它返回的也是一个全局的context和getApplicationcontext一样
	//		TextView textView = new TextView(getContext());

			//2.给textview设置文本 
	//		textView.setText("这是一个自定义组合控件的textview");

			//第一种方式(/爹有了,直接找孩子,亲生的模式)
			//将布局文件转化成view对象
	//		View view = View.inflate(getContext(), R.layout.settingview, null);
	//		//3.添加
	//		this.addView(view);//给这个相对布局(继承自的相对布局)添加一个textview
			//layoutParams : 用代码给控件设置属性,表示要设置控件(子控件)在自定义控件(父控件)中的属性
	//		this.addView(child, params);
			//第二种方式(孩子有了,直接找爹,喜当爹模式,不使用,不设置属性时,父控件会使用原控件(即子控件)的属性)
			//root:给view对象找一个父控件(相当于把一个view对象设置到父控件(this)中,即将view对象添加到了自定义控件中)
			View.inflate(getContext(), R.layout.settingview, this);
		}
	}

#2.4自定义控件的点击事件# **

设置安全卫士是否提示更新的功能:

1.将自定义组合控件移植到手机卫士(将布局文件settingview.xml拷贝到手机卫士的layout中,再将自定义组合控件settingview.java拷贝到cn.itcast.mobilesafexian02.ui包下,将R文件选择导成cn.itcast.mobilesafexian02包,这样才能找到属性)
2.在自定义组合控件中添加改变控件状态的方法,来方便我们改变自定义控件中控件的值(即选择打开提示信息和关闭提示信息的两个描述)

注意:因为描述信息和checkbox控件是在自定义组合控件当中的,我们用的是自定义组合控件,没有办法拿到描述信息和checkbox控件,初始化的时候,只能初始化自定义控件,没有办法初始化自定义控件中的textview和checkbox控件,所以要想改变textvie控件和checkbox控件,必须让自定义组合控件提供一些方法,供我们方便的改变控件的值,所以要在自定义组合控件settingview.java中进行操作。代码如下:


	初始化描述信息和checkbox控件
	tv_setting_title = (TextView) view.findViewById(R.id.tv_setting_title);
	tv_setting_des = (TextView) view.findViewById(R.id.tv_setting_des);
	cb_setting_isupdate = (CheckBox) view.findViewById(R.id.cb_setting_isupdate);

	/**
	 * 设置标题
	 * @param title
	 * 参数title表示:传一个标题过来,即要设置标题,先传一个标题过来
	 */
	public void setTitle(String title){
		//给标题设置内容,内容为传递过来的title(从哪传递过来的?settingActivity.java中初始化自定义组合控件中的控件的值)
		tv_setting_title.setText(title);
	}
	/**
	 * 设置描述信息
	 * @param des
	 * 参数des同上
	 */
	public void setDes(String des){
		//给描述信息设置内容,内容为传递过来的des(从哪传递过来的?settingActivity.java中初始化自定义组合控件中的控件的值)
		tv_setting_des.setText(des);
	}
	/**
	 * 设置checkbox的状态
	 * @param isChecked
	 * 参数isChecked同上
	 */
	public void setChecked(boolean isChecked){
		//给checkbox设置状态,状态为传递过来的状态(从哪传递过来的?settingActivity.java中初始化自定义组合控件中的控件的值)
		cb_setting_isupdate.setChecked(isChecked);
	}
	/**
	 * 获取checkbox的状态
	 * @return
	 * 打开、关闭提示是由checkbox的状态来决定的,即选中,表示打开提示信息,未选中,表示关闭提示信息,所以还需要这个方法,来获取checkbox的状态  (在settingActivity.java中创建方法对checkbox的状态进行点击监听)
	 */
	public boolean isChecked(){
		//获取checkbox的状态
		return cb_setting_isupdate.isChecked();
	}

3.在settingActivity.java中使用相应的方法
	
	//将自定义组合控件初始化出来
	sv_setting_update = (SettingView) findViewById(R.id.sv_setting_update);

	//初始化自定义组合控件中的控件的值
	sv_setting_update.setTitle("提示更新");
	sv_setting_update.setDes("打开提示更新");
	sv_setting_update.setChecked(true);
	
	//创建方法对checkbox的状态进行点击监听
	sv_setting_update.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			if (sv_setting_update.isChecked()) {
				//关闭提示更新(isChecked(),即true,再点击的时候就是关闭)
				sv_setting_update.setDes("关闭提示更新");
				sv_setting_update.setChecked(false);
			}else{
				//打开提示更新(没有isChecked(),即flase,再点击的时候就是打开)
				sv_setting_update.setDes("打开提示更新");
				sv_setting_update.setChecked(true);
			}
		}
	});

问题:在效果中点击checkbox,提示信息没有变
	原因是checkbox本身具有获取焦点和点击的事件,所以在点击checkbox时,将把整个自定义组合控件的焦点和点击事件都给获取拦截了,所以创建的对checkbox的状态进行点击监听的方法是没有执行的。
解决办法:
在settingview.xml中的checkbox中添加两个属性:clickable  focusable
	clickable : 设置是否可以点击   true:可以    false:不可以
	focusable :设置是否可以获取焦点 true:可以 false:不可以

#2.5屏蔽自动更新# **

刚才已经实现了自定义控件的点击事件:
问题1:
现在在设置中心选择关闭提示更新,然后再返回主界面后,第二次进入设置中心时,提示更新又变成打开的了
问题2:在设置中心选择关闭提示更新后,退出应用,再次打开应用时,更新版本对话框还会弹出

提示更新应该是全局有效的,并且控件更新版本的对话框是否弹出

解决:
	1.保存用户更改的状态:在settingActivity.java中用sharedpreferences进行保存
	2.在splashActivity.java中找到update方法,在update方法里面连接了服务器,查看是否有最新版本,有的话弹出更新版本对话框,如果用户关闭了对话框,就没有必要连接服务器查看是否有最新版本,所以从SharedPreferences中取出数据

1.在settingactivity.java中保存提示更新状态,并设置回显
	
	private SharedPreferences sp;
	// ① 获取一个SharedPreferences
	//name:是保存信息的xml名称 mode:是一个权限 (在高版本中不管设置读或写,最终都是私有的权限)
	sp = getSharedPreferences("config", MODE_PRIVATE);

	//初始化自定义组合控件中的控件的值
	sv_setting_update.setTitle("提示更新");
	// ⑤ 根据保存(提交)的状态设置初始化的状态
	//defValue : 缺省值  没有update的时候使用的值
	if (sp.getBoolean("update", true)) {
		//当为true时,打开提示更新
		sv_setting_update.setDes("打开提示更新");
		sv_setting_update.setChecked(true);	
	}else{
		//当为false时,关闭提示更新
		sv_setting_update.setDes("关闭提示更新");
		sv_setting_update.setChecked(false);
	}
	
	sv_setting_update.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			// ②  使用SharedPreferences,在关闭提示信息更新的时候,获取一个edit
			Editor edit = sp.edit();
			if (sv_setting_update.isChecked()) {
				//关闭提示更新
				sv_setting_update.setDes("关闭提示更新");
				sv_setting_update.setChecked(false);
				// ③ 然后在edit中保存一下 提示更新的状态 
				//参数key:设置一下保存信息的name  value: 信息的值 关闭即为false
				edit.putBoolean("update", false);
				// ④ 将edit提交一下
				//edit.apply();//可以保存到文件中,但是仅限于9版本之上,9版本之下是保存到内存中的(在清单文件中查看minSdkVersion="8",所以这个方法在此不适用,所以用edit.commit()方法提交)
			}else{
				//打开提示更新
				sv_setting_update.setDes("打开提示更新");
				sv_setting_update.setChecked(true);
				// ③ 然后在edit中保存一下 提示更新的状态 
				//参数key:设置一下保存信息的name  value: 信息的值 打开即为true
				edit.putBoolean("update", true);
			}
			// ④ 将edit提交一下
			edit.commit();
		}
	});
2.在splashactiivty中根据保存的提示更新状态设置提醒用户更新版本的对话框是否弹出
	
	//创建一个SharedPreferences
	private SharedPreferences sp;
	// ① 获取一个SharedPreferences
	sp = getSharedPreferences("config", MODE_PRIVATE);
	//拿出保存的提示更新的状态,如果没有的话,缺省值为true
	if (sp.getBoolean("update", true)) {
		//如果缺省值为true,弹出更新的对话框
		update();
	}else{
		//不能让主线程睡两秒钟,原因:主线程做一些渲染界面的操作(不渲染是不会出现界面的),布局文件中控件都是通过主线程渲染出来,让主线程睡两秒就没有办法渲染界面,但是activity我们还是打开的,当activity默认没有布局文件时候就是空白界面(我们需要的是欢迎界面,即让黑马程序员界面显示两秒钟),所以需要new一个子线程(此时主线程和子线程是并行运行的,并不影响渲染)
		new Thread(){
			public void run() {
				//睡两秒钟
				SystemClock.sleep(2000);
		//如果缺省值为false,跳转主界面
				enterHome();	
			};
		}.start();
	}

#2.6自定义属性# **

屏蔽自动更新的操作完成后,打开settingActivity.java重新审视代码中的 ⑤ 根据保存(提交)的状态设置初始化的状态,在里边通过自定义组合控件提供的一些方法来更改自定义组合控件中一些控件的值
新需求:在settingActivity.java中采用上边的方式去设置自定义组合控件中一些控件的值(即在代码中设置),很不方便,现在要在布局文件activity_setting.xml中找到自定义组合控件的控件,在里面直接设置属性itcast:title="提示更新",此时就用到了自定属性

首先查看系统是如何定义属性的:
查看系统属性文件:sdk\platforms\android-16\data\res\values\attrs.xml

自定义属性步骤:
1.在values目录下建一个attrs.xml文件  即res -> values -> attrs.xml(固定写法)
	模仿系统的操作来定义我们的属性

	<resources>
		//名称为我们自定义控件的全类名,不用全类名,只用SettingView也可以,个人习惯而已
	     <declare-styleable name="cn.itcast.mobilesafexian02.ui.SettingView">
	         <attr name="title" format="string" /> <!-- title:代表标题  format : 格式 类型-->
	         <attr name="des_on" format="string" /><!-- des_on : 代表打开提示更新-->
	         <attr name="des_off" format="string" /><!-- des_off : 代表关闭提示更新-->
	     </declare-styleable>
	</resources>

自定义属性定义成功后可以到R文件中查看,此时会看到已经自动创建出了attr来,里边是我们自定义的各种属性名称的唯一标识


2.到布局文件activity_setting.xml中使用自定义属性

 命名空间:android为系统属性定义了一个命名空间,自定义属性时必须加一个命名空间(将前边的android改为itcast即可)
在android中有title这个属性,但是没有des_on,des_off属性(自己定义的),此时需要将命名空间尾部的android改名为包名即可(包名在清单文件中package拷贝即可),代表的意思是要找自定义属性时,是直接到系统的attrs.xml中找的,不是去android下的attrs.xml中找

	命名空间:xmlns:itcast="http://schemas.android.com/apk/res/cn.itcast.mobilesafexian02"

	<cn.itcast.mobilesafexian02.ui.SettingView
        android:id="@+id/sv_setting_update"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
		<!-- 此便为自定义属性-->
        itcast:title="提示更新"
        itcast:des_on="打开提示更新"
        itcast:des_off="关闭提示更新"
    ></cn.itcast.mobilesafexian02.ui.SettingView>

3.在自定义组合控件settingview.java代码中使用,使自定义属性有意义
	使用两个参数的构造函数

		//在布局文件使用的时候调用
		public SettingView(Context context, AttributeSet attrs) {
			super(context, attrs);
			
			init();

			// 方式一:通过getAttributeCount()方法获取属性的值   
			//不能获取特定属性的值(所以在此处不适用)
			// ① AttributeSet保存有控件的所有属性,通过AttributeSet获取控件的所有属性
	//		int count = attrs.getAttributeCount();//获取控件属性的个数
	//		System.out.println("属性个数:"+count);
	//		for (int i = 0; i < count; i++) {
	//			//获取相应的属性的值
	//			System.out.println(attrs.getAttributeValue(i));
	//		}


			//方式二:通过命名空间和属性的名称获取属性的值  使用getAttributeValue方法
			//参数namespace : 命名空间
			//参数name : 属性的名称
			String title = attrs.getAttributeValue("http://schemas.android.com/apk/res/cn.itcast.mobilesafexian02", "title");
			des_on = attrs.getAttributeValue("http://schemas.android.com/apk/res/cn.itcast.mobilesafexian02", "des_on");
			des_off = attrs.getAttributeValue("http://schemas.android.com/apk/res/cn.itcast.mobilesafexian02", "des_off");
			
			// ② 使用获取到的属性的值
			//设置标题控件的值
			tv_setting_title.setText(title);
			//设置描述信息的值
			   //获取checkbox的状态 
			if (isChecked()) {
				tv_setting_des.setText(des_on);
			}else{
				tv_setting_des.setText(des_off);
			}
			
		}
	在setChecked方法中增加设置描述信息的值
		/**
		 * 设置checkbox的状态
		 * @param isChecked
		 */
		public void setChecked(boolean isChecked){
			cb_setting_isupdate.setChecked(isChecked);
			//相当于将sv_setting_update.setDes("打开提示更新");封装到了这个方法中 
			//自定义属性好处:1.写法变简单了 2.在设置中心当中,以后还要添加相同格式的条目时,直接将布局文件activity_setting.xml中的自定义控件一复制,将title,des_on,des_off的值一改,然后到设置界面SettingActivity.java中像下面4一样一设置,就可以实现相应条目的事件了
			
			//设置描述信息的值
			   //获取checkbox的状态 
			if (isChecked()) {
				tv_setting_des.setText(des_on);
			}else{
				tv_setting_des.setText(des_off);
			}
		}
4.在设置界面settingactivity.java中去除相应的方法(加//即为去除了的)
	原因:
	在 3.在自定义组合控件settingview.java代码中使用/② 使用获取到的属性的值 中设置了属性的值,那么在设置界面settingactivity.java/初始化自定义组合控件中的控件的值 中就没有必要再去设置了
	
	//初始化自定义组合控件中的控件的值

// sv_setting_update.setTitle(“提示更新”);
//根据保存的状态设置初始化的状态
//defValue : 没有update的时候使用的值
if (sp.getBoolean(“update”, true)) {
// sv_setting_update.setDes(“打开提示更新”);
sv_setting_update.setChecked(true);
}else{
// sv_setting_update.setDes(“关闭提示更新”);
sv_setting_update.setChecked(false);
}

	sv_setting_update.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			Editor edit = sp.edit();
			if (sv_setting_update.isChecked()) {
				//关闭提示更新

// sv_setting_update.setDes(“关闭提示更新”);
sv_setting_update.setChecked(false);
edit.putBoolean(“update”, false);
// edit.apply();//可以保存到文件中,但是仅限于9版本之上,9版本之下是保存到内存中的
}else{
//打开提示更新
// sv_setting_update.setDes(“打开提示更新”);
sv_setting_update.setChecked(true);
edit.putBoolean(“update”, true);
}
edit.commit();
}
});

#2.7设置密码对话框# ***

设置中心提示更新的条目实现后,在对手机防盗这个模块进行开发
1.首先,对手机防盗这个item增加一个点击事件,让它跳转到手机防盗页面去
	找到主界面HomeActivity.java,在//判断当前点击的条目的位置 中增加一个case
2.手机防盗不是谁都可以进,所以设置密码 (第一次设置密码,以后为输入密码,正确才能进去,错误进不去)
	在case中添加 showSetPassWordDialog();(设置密码对话框,需要创建)
				showEnterPassWordDialog();(输入密码对话框,需要创建)
	并进行判断
3.设置密码对话框中需要实现确定和取消按钮的功能
	在主界面HomeActivity.java中,找到设置密码对话框,初始化控件 实现按钮功能


/**
 * 设置密码对话框
 */
protected void showSetPassWordDialog() {
	// ① new一个builder 
	AlertDialog.Builder builder = new Builder(this);
	builder.setCancelable(false);//设置对话框不能消失(没点击前)
	
	// ③ 将布局文件转化成了view对象 resource参数:这里需要加载一个布局文件,所以创建一个dialog_setpassword.xml布局文件(线性布局)

	View view = View.inflate(getApplicationContext(), R.layout.dialog_setpassword, null);
	// ④ 初始化控件
	final EditText et_setpassword_password = (EditText) view.findViewById(R.id.et_setpassword_password);
	final EditText et_setpassword_confrim = (EditText) view.findViewById(R.id.et_setpassword_confrim);
	Button btn_ok = (Button) view.findViewById(R.id.btn_ok);
	Button btn_cancel = (Button) view.findViewById(R.id.btn_cancel);
	
	//  ⑥ 实现确定按钮的功能
	btn_ok.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			//1.获取密码输入框输入的内容(内部类使用外部类的操作需要final)
			String password = et_setpassword_password.getText().toString().trim();
			//2.判断输入的密码是否为空
			if (TextUtils.isEmpty(password)) { //null : 没有内存      "" : 有内容但是也没有内存
				Toast.makeText(getApplicationContext(), "请输入密码", 0).show();
				return;
			}
			//3.获取再次输入的密码
			String confrim_password = et_setpassword_confrim.getText().toString().trim();
			//4.判断两次密码输入是否一致
			if (password.equals(confrim_password)) {
				//一致 保存设置的密码 (保存到sharedpreferences中 需要创建)
				Editor edit = sp.edit(); //获取一个eidt
				edit.putString("password", password); //用edit保存密码
				 //提交密码
				edit.commit();
				//点击之后 隐藏对话框
				dialog.dismiss();
				//提醒用户
				Toast.makeText(getApplicationContext(), "密码设置成功", 0).show();
			}else{
				Toast.makeText(getApplicationContext(), "两次密码输入不一致", 0).show();
			}
		}
	});
	
	// ⑤ 实现取消按钮的功能 (选择的是view的OnClickListener()方法)
		//注意:在此处使用的是view接口下的点击监听事件,在splashactivity.java的版本升级功能处使用的是dialog接口下的点击监听事件 原因:在升级提醒对话框那里是通过builder.setPositiveButton去设置按钮的,相当于在dialog中创建了一个按钮,而此处的取消按钮是在dialog_setpassword布局中的,相当于在view对象中创建了一个按钮
	btn_cancel.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			//点击之后隐藏对话框  这里不像版本升级那块,本身有dialog,所以需要new一个dialog
			dialog.dismiss();
		}
	});
	//  ② 用builder设置密码对话框,将view对象填充进来(没有设置密码输入框的方法,用setView相当于设置密码输入框)
	builder.setView(view);
	    //需要new一个dialog(将它变成了成员变量)
	dialog = builder.create();
	//显示设置密码对话框 
	//builder.show();  
	dialog.show();
}

#2.8输入密码对话框# **

设置密码对话框完成后,当再次点击手机防盗时,需要弹出输入密码对话框来输入密码

1.首先找到主界面HomeActivity.java,在//判断当前点击的条目的位置 中case 0 处
	判断是否已经设置过密码,没有,弹出设置密码对话框,有,弹出输入密码对话框
	
2.输入密码对话框中需要实现确定和取消按钮的功能
	在主界面HomeActivity.java中,找到输入密码对话框,初始化控件 实现按钮功能
	(与设置密码对话框相似,可以复制公共代码)


/**
 * 输入密码对话框
 */
protected void showEnterPassWordDialog() {
	// ① new一个builder
	AlertDialog.Builder builder = new Builder(this);
	builder.setCancelable(false); //设置对话框不能消失(没点击前)
	// ③ 将布局文件转化成了view对象 resource参数:这里需要加载一个布局文件,所以创建一个dialog_setpassword.xml布局文件(线性布局)

	View view = View.inflate(getApplicationContext(), R.layout.dialog_enterpassword, null);
	// ④ 初始化控件
	final EditText et_setpassword_password = (EditText) view.findViewById(R.id.et_setpassword_password);
	Button btn_ok = (Button) view.findViewById(R.id.btn_ok);
	Button btn_cancel = (Button) view.findViewById(R.id.btn_cancel);
	//  ⑥ 实现确定按钮的功能
	btn_ok.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			//1.获取密码输入框输入的内容(内部类使用外部类的操作需要final)
			String password = et_setpassword_password.getText().toString().trim();
			//2.判断输入的密码是否为空
			if (TextUtils.isEmpty(password)) {
				Toast.makeText(getApplicationContext(), "请输入密码", 0).show();
				return;
			}
			//3.获取保存的密码
			String sp_password = sp.getString("password", "");
			//4.判断两个密码是否一致
			if (password.equals(sp_password)) {
				//跳转到手机防盗界面
				Intent intent = new Intent(HomeActivity.this,LostFindActivity.class);
				startActivity(intent);
				//隐藏对话框
				dialog.dismiss();
				//提醒用户
				Toast.makeText(getApplicationContext(), "密码正确", 0).show();
			}else{
				Toast.makeText(getApplicationContext(), "密码输入错误", 0).show();
			}
		}
	});
		// ⑤ 实现取消按钮的功能 (选择的是view的OnClickListener()方法)
		//注意:在此处使用的是view接口下的点击监听事件,在splashactivity.java的版本升级功能处使用的是dialog接口下的点击监听事件 原因:在升级提醒对话框那里是通过builder.setPositiveButton去设置按钮的,相当于在dialog中创建了一个按钮,而此处的取消按钮是在dialog_setpassword布局中的,相当于在view对象中创建了一个按钮

	btn_cancel.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			dialog.dismiss();
		}
	});
//  ② 用builder输入密码对话框,将view对象填充进来(没有输入密码输入框的方法,用setView相当于输入密码输入框)
	builder.setView(view);
	dialog = builder.create();
	dialog.show();
}

#2.9兼容低版本# *** 4.1.2 2.3.3
问题:
低版本中UI处理方式和高版本中UI处理方式不一样,androidUI的走向是版本越高,UI风格整体越扁平化 ,
低版本中输入框有一个矩形的背影 高本版中输入框就是一条线

高版本中界面背景是白色的,而在低版本中是黑色的,且在低版本中对话框上部是黑色的凸起
解决:
	1.将高版本中的界面背景改成白色的(在布局文件的Linearlayout中添加android:background="#ffffff") 
	2.在主界面homeactivity.java中找到设置密码对话框,将builder.setView(view);改成dialog.setView(); 
虽然效果一样,但是dialog.setView()有个重载的方法,即dialog.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight, viewSpacingBottom)
viewSpacingLeft:表示距离对话框内左边框的距离 
viewSpacingTop:表示距离对话框内上边框的距离
viewSpacingRight:表示距离对话框内右边框的距离
viewSpacingBottom:表示距离对话框内底边框的距离
都改成0即可

#2.10 MD5# ****

设置密码和输入密码对话框实现后,还要用MD5加密,起到防盗作用
问题:
密码是保存在本地的sharedpreference中的,可以用adb shell-->cd data-->ls-->cd data-->ls-->可以找到应用程序的包名cn.itcast.mobilesafexian02 然后继续 cd cn.itcast.mobilesafexian02-->ls-->里面有一个shared_prefs,再cd shared_prefs-->ls-->cat config.xml-->你会发现里面有
	<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
	<map>
	<boolean name="update" value="false"/>
	<string name="password">123<string>
	</map>
即可知道密码为123

解决:在sharedpreference中保存密码时,不能直接保存成明文,应该用MD5加密,保存成密文
android抓包现象很严重,所以涉及到密码等应该加密

1.md5: 特性 不可逆,即明文转化密文之后,密文是不能转化成明文的
2.MD5加盐:
	以前标准MD5是安全的,现在不在安全了,因为网上出现了很多MD5解析工具,它有一个数据库,里面存储的就是密文+明文的一一映射,拿着MD5密文查询一下对应的密文即可得到对应的明文
	于是出现了‘MD5加盐不规则操作’ 用MD5解析工具是解析不出来的,即使解析成明文,也不是原密码,因为+1(即加盐),是不规则加密

3.银行加密(安全性甚至超过MD5加盐操作):6位数字 
	有一个随机算法,可以计算出一个10-30之间的一个数字 比如计算出21,就会进行21次MD5加密(相当于将每次加密后的密文不断复制加密21次)中间还会有加盐等一些操作,使它变得更复杂
	
	拿着这个加密后的密文再去MD5解析器上解析,此时将会是付费的操作,这就是MD5解析工具的盈利模式,它会将一些难破译,重复次数好几次,或者比较复杂的密码设置成付费的操作来盈利

步骤:
1.创建md5utils.java文件,实现MD5加密
/**
 * md5加密
 * @param password
 * @return
 */
public static String digestPassword(String password){
	//创建一个stringbuilder用于接收将加密的十六进制字符串串联起来
	StringBuilder sb = new StringBuilder();
	try {
		//1.获取数据摘要器
		//参数:加密的方式
		MessageDigest messageDigest = MessageDigest.getInstance("MD5");
		//2.将一个byte数组进行加密,返回一个加密的byte数组
		byte[] digest = messageDigest.digest(password.getBytes());
		//3.遍历加密过的byte数组
		for (int i = 0; i < digest.length; i++) {
			//4.拿byte元素去和int类型的255(十六进制是0xff)与运算,得到int类型的正整数(标准的MD5操作)
			//byte值范围: -128 - 127 说明byte值可能是一个负数
			int result = digest[i] & 0xff;
			//5.因为int类型取值范围比较大,所以转化成十六进制的字符串(integer是int类型的包装类)

// String hexString = Integer.toHexString(result)+1;//不规则加密,加盐
String hexString = Integer.toHexString(result);
if (hexString.length() < 2) {
// System.out.print(“0”);
sb.append(“0”);
}
// System.out.println(hexString);
sb.append(hexString);
//e10adc3949ba59abbe56e057f20f883e
}
//6.将加密的十六进制字符串串联起来 即为明文密码经过MD5加密过的密文值
// System.out.println(sb.toString());
return sb.toString();
} catch (NoSuchAlgorithmException e) {
//找不到加密方式的异常
e.printStackTrace();
return “”;
}
}

2.在Homeactivity.java中保存设置的密码时,改成edit.putString("password",MD5Utils.digestPassword(password));

3.在输入密码后,获取保存的密码验证是否一致时,因为保存的密码已经用MD5设置成了密文,所以输入的密码也需要MD5加密一次,两个密文去比较,如果一致就相同,不一致就不相同 所以//判断两个密码是否一致需要改成:
if(MD5Utils.digestPassword(password).equals(sp_password))

注意:在初次将输入密码改为MD5加密后,以前保存的密码是没有经过MD5加密的,所以会造成错误
解决:打开fileExplorer将data/data/shared_prefs中的config.xml删除即可(在删文件的时候一定要选中左边目标模拟器,否则删不掉)

#2.11显示/隐藏密码# **

MD5加密完成后,现在市面上有的软件上,会在输入密码框的右边有一个图片按钮,点击图片按钮就可显示输入的密码,再次点击图片按钮,就可隐藏输入的密码

步骤:
1.先给输入密码框右边添加一个图片,找到dialog_enterpassword.xml布局文件,添加一个linearlayout,把输入密码框放进去,将密码输入框在右边添加一个imageview

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >
    <!-- layout_weight : 不仅有显示比例操作,还可决定渲染的级别,值越大级别越低 imageview没有设置说明是0,0比1小,渲染级别比1大,所以渲染的时候先渲染imageview,剩下的部分再去渲染edittext,所以imageview能显示出来-->
<EditText
    android:id="@+id/et_setpassword_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
		<!--添加权重属性,设为1 -->
    android:layout_weight="1"
    android:ems="10"
    android:inputType="textPassword" 
    android:textColor="#000000"
    android:hint="请输入密码"
    >
</EditText>
<ImageView 
    android:id="@+id/iv_enterpassword_hide"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/btn_circle_selected"
    android:layout_gravity="center_vertical"
    />
</LinearLayout>


2.在HomeActivity.java中找到//输入密码对话框,
	//找到图片按钮
ImageView iv_enterpassword_hide = (ImageView) view.findViewById(R.id.iv_enterpassword_hide);

//定义计数器
int count =0;
//给图片按钮设置点击事件
iv_enterpassword_hide.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			//隐藏/显示密码操作 
			if (count%2 == 0) {
				//如果count对2求余恒等于0,显示密码 (对2求余是否等于0只有两种可能,正好对应显示隐藏两种状态,还可用true/flase等好几种方式)
				//InputType属性实现了隐藏/显示密码 numberpassword是隐藏密码,它的值是只能输入数字,textpassword是隐藏密码 它的值输入文字,数字,字母等都可以,text是显示密码
				et_setpassword_password.setInputType(1);
			}else{
				//否则隐藏密码
				et_setpassword_password.setInputType(129);
			}
			//计数器累加一下
			count++;
		}
	});

注意:
如何去查看布局文件中属性的值?
	打开sdk/platforms/android-16/data/res/values/attrs.xml ,ctrl+alt+f 搜索inputtype 然后双击inputtype 会发现它的属性none的值为十六进制的0,text的值为十六进制的1 textpassword的值为十六进制的81,用计算器转化成十进制后是129

显示隐藏密码原理:
	实际上是对setInputType属性的值进行动态的修改

#2.12界面跳转逻辑# **

设置密码和输入密码的操作完成后,点击确定密码正确之后,就要跳转到手机防盗页面了
思考:手机防盗页面应该包含两个功能,1.让用户设置手机防盗功能的界面(1欢迎使用手机防盗),2.用户设置了哪些手机防盗功能的界面(手机防盗界面)
	
步骤:
	1.找到HomeActivity.java页面,输入密码对话框这里,判断两个密码一致后,就要跳转到手机防盗界面
		//跳转到手机防盗界面
		Intent intent = new Intent(HomeActivity.this,LostFindActivity.class);
		startActivity(intent);
	2.创建手机防盗页面LostFindActivity.java 并到清单文件中配置<activity android:name=".LostFindActivity"></activity> ,
	在手机防盗页面LostFindActivity.java中重写onCreate()方法 通过onContentView(R.layout.activity_lostFind);加载布局
	3.创建布局文件activity_lostFind.xml
	
4.跳转到手机防盗界面的时候,要判断用户有没有进行过手机防盗设置,如果设置了就显示出,没有设置就跳转到
1欢迎使用手机防盗界面,手机防盗界面LostFindActivity.java代码如下:

public class LostFindActivity extends Activity {
private SharedPreferences sp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sp = getSharedPreferences(“config”, MODE_PRIVATE);
//判断用户是否是第一次进入手机防盗模块,如果是 跳转手机防盗设置向导界面 进行手机防盗功能设置,如果不是 跳转到手机防盗界面显示设置的手机防盗功能
if (sp.getBoolean(“first”, true)) { //参数key: first 还没有设置,先使用一下,到后边会设置 //参数defValues:true 表示是第一次接收
//跳转到功能设置向导界面
Intent intent = new Intent(this,SetUp1Activity.class); //需要设置向导界面的SetUp1Activity.java
startActivity(intent);
finish();
}else{
setContentView(R.layout.activity_lostfind);
}
}
}

5.新建一个设置向导界面SetUp1Activity.java,
并到清单文件中配置 <activity android:name=".SetUp1Activity"></activity>
在设置向导界面SetUp1Activity.java中重写onCreate方法();
在oncreate()方法中加载布局setContentView(R.layout.activity_setup1);

设置向导界面SetUp1Activity.java代码如下
public class SetUp1Activity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_setup1);
	}
}

6.新建布局文件activity_setup1.xml

注意:
	1.运行程序,进入手机防盗功能后,直接进入了设置向导第一个界面
	
	因为在sharedprefenrece中还没有没有保存first,没有保存,说明用户是第一次进入手机防盗,所以跳转到手机防盗设置向导界面,进行手机防盗功能设置
	
	2.进入设置向导第一个界面后,点击返回键时,回到了一个空白界面,然后在点击返回键时,回到了主页面
	
	因为在主界面homeactivity.java中跳转到的是手机防盗界面LostFindActivity.java(Intent intent = new Intent(HomeActivity.this,LostFindActivity.class);)
	而在手机防盗界面lostFindActivity.java中//跳转到功能设置向导界面Intent intent = new Intent(this,SetUp1Activity.class);即又一次跳转,跳转到了设置向导界面setup1Activity.java中,
	跳转了两次,相当于打开了两个activity,从设置向导界面setup1Activity.java退出的时候,是回到了手机防盗界面lostFindActivity.java,而这个手机防盗界面lostFindActivity.java我们还没有加载布局文件,所以会显示空白界面
		
	解决:当跳转到设置向导界面setup1Activity.java之后,用方法finnish();把手机防盗界面lostFindActivity.java消除掉,即可在点击返回键时,直间跳转到主界面homeactivity.java

#2.13设置向导第一个界面&状态选择器# *****

界面跳转逻辑完成之后,可以跳转到设置向导第一个界面setup1Activity.java中,接下来就该对设置向导第一个界面做处理了

1.创建布局文件Activity_setup1.xml()
	android系统中有些图片是可以用,有些图片是不能用,使用系统图片的好处:可以节省工程的体积,所以应该尽量使用系统图片
	

2.状态选择器:特殊的图片,根据不同的状态显示不同的图片,比如按下显示蓝色,抬起显示透明色
	工作后会频繁用到,基本上,只要涉及到一个按钮,这个按钮就是状态选择器
怎么判断一个按钮应该用状态选择器呢?
	美工给你两张基本相似的图片,从名称也可看出,应该就是两个一模一样的图片,有细微的区别,一般都是颜色的变化,从名字也可以看出,哪张应该是按下应该显示的图片,哪张应该是默认的图片,就应该用状态选择器
怎么用状态选择器呢?
	查看sdk-->docs 这里边都是api的镜像网站,也可以翻墙到谷歌官网去看,那里是最新的api,这里只是镜像网站,不过也足够我们使用了,选择index.html,一般用火狐打开,火狐有一个脱机工作,可以减少加载的时间,
	
bitmap:(.png,.jpg,.gif):一般用在显示网络图片,比如网络图片比较大,就用bitmap对它进行一些缩放,改变一下它的帧率分辨率等等

nine-patch:俗称.9图片,是安卓中一种特有的格式,作用:为了防止图片拉伸,要会制作

3.state List:状态选择器,用法:在res下 -> 新建drawable ->新建 button.xml

button.xml中代码如下:
	<selector xmlns:android="http://schemas.android.com/apk/res/android">
	    <item android:state_pressed="true"
	          android:drawable="@drawable/function_greenbutton_pressed" /> <!-- pressed 按下时显示的图片-->
	    <item android:drawable="@drawable/function_greenbutton_normal" /> <!-- default 默认的图片-->
	</selector>

4.找到布局文件中的button按钮,将状态选择器button.xml设置成背景图片即可(创建状态选择器后,任何需要用到的按钮,都可以将状态选择器button.xml设置成背景图片使用)

	android:background="@drawable/button"
  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值