Android开发实战《手机安全卫士》——3.“手机防盗”模块实现 & .9格式图片说明 &数据库操作 & 平移动画

1.手机防盗——密码加密过程 & 加密后的验证

上一篇博客的最后,我们编写了一个MD5加密工具类,这一节里,我们需要完善密码加密过程以及加密后的验证

  1. 修改MD5Util,为了安全性,这里做了个加盐处理,并且制定返回值,代码如下:
package com.example.mobilesafe.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Util {

    /**
     * 给指定字符串按照MD5算法进行加密
     * @param password 待加密的字符型
     * @return 加密过后的字符串                
     */
    public static String encoder(String password){
        try {
            // 0.加盐处理
            password = password + "password";
            // 1.指定加密算法类型
            MessageDigest digest = MessageDigest.getInstance("MD5");
            // 2.将需要加密的字符串中转换成byte类型的数组,然后进行随机的哈希过程
            byte[] bs = digest.digest(password.getBytes());
            // 3.循环遍历数组,然后让其生成32位字符串,固定写法
            StringBuffer stringBuffer = new StringBuffer();
            for (byte b : bs) {
                int i = b & 0xff;
                // 4.将int类型的i转换成16进制字符
                String hexString = Integer.toHexString(i);
                if (hexString.length() < 2){
                    hexString = "0" + hexString;
                }
                // 5.字符串拼接
                stringBuffer.append(hexString);
                return stringBuffer.toString();
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }
}
  1. 修改HomeActivity,修改showSetPasswordDialog()和showConfirmPasswordDialog()方法,调用MD5工具类对密码进行相应的加密和解密处理,代码如下:
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.mobilesafe.R;
import com.example.mobilesafe.constant.ConstantValue;
import com.example.mobilesafe.utils.MD5Util;
import com.example.mobilesafe.utils.SharedPreferencesUtil;
import com.example.mobilesafe.utils.ToastUtil;

public class HomeActivity extends AppCompatActivity {

    /**
     * 存储标题
     */
    private String[] mTitleStrs;

    /**
     * 存储图像
     */
    private int[] mDrawableIds;

    /**
     * 网格对象
     */
    private GridView gv_home;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        
        // 初始化UI
        initUI();

        // 初始化数据
        initData();
    }

    /**
     * 1.初始化UI
     */
    private void initUI() {
        gv_home = findViewById(R.id.gv_home);
    }

    /**
     * 2.初始化数据
     */
    private void initData() {
        // 1.初始化每个图标的标题
        mTitleStrs = new String[]{"手机防盗","通信卫士","软件管理","进程管理","流量统计","手机杀毒","缓存清理","高级工具","设置中心"};
        // 2.初始化每个图标的图像
        mDrawableIds = new int[]{R.drawable.home_safe,R.drawable.home_callmsgsafe,R.drawable.home_apps,R.drawable.home_taskmanager,R.drawable.home_netmanager,R.drawable.home_trojan,R.drawable.home_sysoptimize,R.drawable.home_tools,R.drawable.home_settings};
        // 3.为GridView设置数据适配器
        gv_home.setAdapter(new MyAdapter());
        // 4.注册GridView中单个条目的点击事件
        gv_home.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                switch (position){
                    case 0:
                        // 手机防盗
                        showDialog();
                        break;
                    case 8:
                        // 设置中心
                        Intent intent = new Intent(getApplicationContext(), SettingActivity.class);
                        startActivity(intent);
                        break;
                    default:
                        break;
                }
            }
        });
    }

    /**
     * 3.自定义的数据适配器类
     */
    class MyAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            // 统计条目的总数
            return mTitleStrs.length;
        }

        @Override
        public Object getItem(int position) {
            // 根据索引获取对象
            return mTitleStrs[position];
        }

        @Override
        public long getItemId(int position) {
            // 获取索引
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // 获取视图
            View view = View.inflate(getApplicationContext(), R.layout.gridview_item, null);
            TextView tv_title = view.findViewById(R.id.tv_title);
            ImageView iv_icon = view.findViewById(R.id.iv_icon);
            tv_title.setText(mTitleStrs[position]);
            iv_icon.setBackgroundResource(mDrawableIds[position]);
            return view;
        }
    }

    /**
     * 4.手机防盗——密码对话框
     */
    private void showDialog() {
        // 1.通过判断本地是否有存储密码来确定显示哪个对话框(sp)
        String password = SharedPreferencesUtil.getString(this, ConstantValue.MOBILE_SAFE_PASSWORD, "");
        if (TextUtils.isEmpty(password)){
            // 2.初始设置密码对话框
            showSetPasswordDialog();
        }else {
            // 3.确认密码对话框
            showConfirmPasswordDialog();
        }
    }

    /**
     * 5.初次设置密码对话框
     */
    private void showSetPasswordDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        final AlertDialog dialog = builder.create();
        final View view = View.inflate(this, R.layout.dialog_set_password, null);
        dialog.setView(view);
        dialog.show();
        Button btn_submit = view.findViewById(R.id.btn_submit);
        Button btn_cancel = view.findViewById(R.id.btn_cancel);
        btn_submit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EditText et_set_password = view.findViewById(R.id.et_set_password);
                EditText et_confirm_password = view.findViewById(R.id.et_confirm_password);
                String password = et_set_password.getText().toString();
                String confirmpassword = et_confirm_password.getText().toString();
                if (!TextUtils.isEmpty(password) && !TextUtils.isEmpty(confirmpassword)){
                    if(password.equals(confirmpassword)){
                        // 进入手机防盗模块
                        Intent intent = new Intent(getApplicationContext(), TestActivity.class);
                        startActivity(intent);
                        dialog.dismiss();
                        // 将密码存储到sp中
                        SharedPreferencesUtil.putString(getApplicationContext(),ConstantValue.MOBILE_SAFE_PASSWORD, MD5Util.encoder(password));
                    }else {
                        // 提示用户确认密码有误
                        ToastUtil.show(getApplicationContext(),"确认密码错误");
                    }
                }else {
                    // 提示用户密码输入有为空
                    ToastUtil.show(getApplicationContext(),"请输入密码");
                }
            }
        });
        btn_cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
    }

    /**
     * 6.再次设置密码对话框
     */
    private void showConfirmPasswordDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        final AlertDialog dialog = builder.create();
        final View view = View.inflate(this, R.layout.dialog_confirm_password, null);
        dialog.setView(view);
        dialog.show();
        Button btn_submit = view.findViewById(R.id.btn_submit);
        Button btn_cancel = view.findViewById(R.id.btn_cancel);
        btn_submit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EditText et_confirm_password = view.findViewById(R.id.et_confirm_password);
                String confirmpassword = et_confirm_password.getText().toString();
                if (!TextUtils.isEmpty(confirmpassword)){
                    // 从sp中获取加密过后的密码,然后将输入的密码同样进行MD5算法,再进行比对
                    String password = SharedPreferencesUtil.getString(getApplicationContext(), ConstantValue.MOBILE_SAFE_PASSWORD, "");
                    if(password.equals(MD5Util.encoder(confirmpassword))){
                        // 进入手机防盗模块
                        Intent intent = new Intent(getApplicationContext(), TestActivity.class);
                        startActivity(intent);
                        dialog.dismiss();
                    }else {
                        // 提示用户确认密码有误
                        ToastUtil.show(getApplicationContext(),"确认密码错误");
                    }
                }else {
                    // 提示用户密码输入有为空
                    ToastUtil.show(getApplicationContext(),"请输入密码");
                }
            }
        });
        btn_cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
    }
}

2.手机防盗——对话框展示样式兼容低版本

大致的密码验证逻辑以及完成了,现在来做进一步的优化:为了在低版本的Android手机上也具有相同的显示效果,我们来做一些相关的处理

核心思路是:

  1. 把根布局的界面背景渲染成白色,替换掉低版本的黑色
  2. 在调用setView(view)这个api时,使用另一个构造方法,即setView(view,0,0,0,0),即将边距全部设置为0

因为当前的Android手机版本都已经很高了(不会低于2.3),所以这个优化仅在此处作为记录,就不实际运用到项目中了

3.手机防盗——设置界面 & 功能列表界面跳转逻辑处理

有关于密码验证的逻辑已经完成了,接下来就需要编写手机防盗内的核心功能页面了,该页面由四个页面组成,如图所示:

  • 页面一:描述功能

在这里插入图片描述

  • 页面二:手机卡绑定(必须绑定才能进入下一个界面)

在这里插入图片描述

  • 页面三:设置安全号码(必须设置才能进入下一个界面)

在这里插入图片描述

  • 页面四:设置完成(只有开启这个选项,前三项的配置才会生效)

在这里插入图片描述

  • 页面五:手机防盗功能列表界面

在这里插入图片描述

当第一次进入手机防盗模块时,界面出现的顺序是:12345,当全部设置完成后,再次进入手机防盗模块,则会直接进入界面5

现在开始编写代码

  1. 修改ConstantValue,新增常量SETUP_OVER,表示手机防盗界面是否全部设置完成,代码如下;
package com.example.mobilesafe.constant;

public class ConstantValue {

    /**
     * 记录更新的状态
     */
    public static final String OPEN_UPDATE = "open_update";

    /**
     * 手机防盗——设置密码的状态
     */
    public static final String MOBILE_SAFE_PASSWORD = "mobile_safe_password";

    /**
     * 手机防盗——四个界面是否设置完成的状态
     */
    public static final String SETUP_OVER = "setup_over";
}

  1. 在activity下新建SetupOverActivity,作为手机防盗中功能列表的Activity,完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;

import com.example.mobilesafe.R;
import com.example.mobilesafe.constant.ConstantValue;
import com.example.mobilesafe.utils.SharedPreferencesUtil;

public class SetupOverActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        boolean setup_over = SharedPreferencesUtil.getBoolean(this, ConstantValue.SETUP_OVER, false);
        if (setup_over){
            // 密码输入成功,并且四个导航界面均设置完成,才停留在设置完成后的功能列表
            setContentView(R.layout.activity_setupover);
        }else {
            // 密码输入成功,若四个导航界面没有全部设置完成,则跳转到第一个导航界面
            Intent intent = new Intent(this, SetupOneActivity.class);
            startActivity(intent);
            finish();
        }
    }
}
  1. 在activity下新建SetupOneActivity,作为手机防盗中设置时出现的第一个Activity,暂时先套用Empty Activity模板即可

4.手机防盗——导航界面的布局编写

上一节我们写了导航界面的Activity,现在我们来完善相应的布局

修改activity_setup_one.xml,添加相应控件,代码如下:

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

    <TextView
        style="@style/TitleStyle"
        android:text="1.欢迎使用手机防盗"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:layout_margin="5dp"
        android:text="您的手机防盗卫士:"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="sim卡变更报警"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="GPS追踪"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="远程数据销毁"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="远程锁屏"/>

</LinearLayout>

5.手机防盗——编写选择器

上一节里,我们完成了第一个界面的部分编写,现在就需要完成另一个部分——灰点指示器

  1. 修改activity_setup_one.xml,完善页面,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.SetupOneActivity">

    <TextView
        style="@style/TitleStyle"
        android:text="1.欢迎使用手机防盗"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:layout_margin="5dp"
        android:text="您的手机防盗卫士:"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="sim卡变更报警"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="GPS追踪"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="远程数据销毁"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="远程锁屏"/>

    <LinearLayout
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:background="@android:drawable/presence_online"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:background="@drawable/setup1"
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <!--  图片选择器,选择时和未选择时显示的图片不同  -->
        <Button
            android:text="下一页"
            android:layout_alignParentRight="true"
            android:layout_alignParentBottom="true"
            android:drawableRight="@drawable/next"
            android:background="@drawable/selector_next_btn_bg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RelativeLayout>

</LinearLayout>
  1. 在drawable下新建selector_next_btn_bg.xml,作为按钮的选择器,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 选中状态:深绿色  -->
    <item android:state_pressed="true" android:drawable="@drawable/function_greenbutton_pressed"/>
    <!-- 未选中状态:浅绿色  -->
    <item android:drawable="@drawable/function_greenbutton_normal"/>
</selector>

6.手机防盗——.9格式的图片拖拽区域 & 文字所在区域

项目里用到了.9格式的图片,.9格式的图片最大的作用就是适配,其更多的性质和制作可以参考百度,这里不再详述

7.手机防盗——第二个导航界面

我们前面完成了第一个导航界面的编写,现在来完成第二个界面的编写

  1. 修改res/value/style.xml,将按钮的样式抽取出来,命名为NextBtnStyle和PreBtnStyle,方便后面的界面在创建按钮时直接调用该样式,代码如下:
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="TitleStyle">
        <item name="android:gravity">center</item>
        <item name="android:textSize">20sp</item>
        <item name="android:textColor">#000</item>
        <item name="android:padding">10dp</item>
        <item name="android:background">#0f0</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
    </style>

    <style name="PreBtnStyle">
        <item name="android:text">上一页</item>
        <item name="android:onClick">prePage</item>
        <item name="android:layout_alignParentLeft">true</item>
        <item name="android:layout_alignParentBottom">true</item>
        <item name="android:drawableLeft">@drawable/previous</item>
        <item name="android:background">@drawable/selector_next_btn_bg</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_width">wrap_content</item>
    </style>

    <style name="NextBtnStyle">
        <item name="android:text">下一页</item>
        <item name="android:onClick">nextPage</item>
        <item name="android:layout_alignParentRight">true</item>
        <item name="android:layout_alignParentBottom">true</item>
        <item name="android:drawableRight">@drawable/next</item>
        <item name="android:background">@drawable/selector_next_btn_bg</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_width">wrap_content</item>
    </style>

</resources>
  1. 修改activity_setup_one.xml,给按钮调用样式,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.SetupOneActivity">

    <TextView
        style="@style/TitleStyle"
        android:text="1.欢迎使用手机防盗"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:layout_margin="5dp"
        android:text="您的手机防盗卫士:"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="sim卡变更报警"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="GPS追踪"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="远程数据销毁"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:drawableLeft="@android:drawable/star_big_on"
        android:layout_margin="5dp"
        android:text="远程锁屏"/>

    <LinearLayout
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:background="@android:drawable/presence_online"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:background="@drawable/setup1"
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <!--  图片选择器,选择时和未选择时显示的图片不同  -->
        <Button
            style="@style/NextBtnStyle"/>
    </RelativeLayout>

</LinearLayout>
  1. 修改SetupOneActivity,添加点击事件的跳转方法,代码如下:
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

import com.example.mobilesafe.R;

public class SetupOneActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_one);
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupTwoActivity.class);
        startActivity(intent);
        finish();
    }
}
  1. 新建名为SetupTwoActivity的Activity,布局如图所示:

在这里插入图片描述

​ 首先编写相应的布局文件activity_setup_two.xml,代码如下:

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

    <TextView
        style="@style/TitleStyle"
        android:text="2.手机卡绑定"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:layout_margin="5dp"
        android:text="通过绑定SIM卡:\n下次重启手机如果发现SIM卡变化,就会发送报警短信"/>

    <com.example.mobilesafe.view.SettingItemView
        xmlns:mobilesafe="http://schemas.android.com/apk/res/com.example.mobilesafe"
        android:id="@+id/siv_sim_bound"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        mobilesafe:destitle="点击绑定sim卡"
        mobilesafe:desoff="sim卡未绑定"
        mobilesafe:deson="sim卡已绑定"/>

    <LinearLayout
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_online"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:background="@drawable/bind"
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <!--  图片选择器,选择时和未选择时显示的图片不同  -->
        <Button
            style="@style/PreBtnStyle"/>

        <Button
            style="@style/NextBtnStyle"/>
    </RelativeLayout>

</LinearLayout>

8.手机防盗——导航界面和功能列表界面跳转逻辑处理

上一节中我们完成了第二个导航界面的编写,现在来完善该界面的相应逻辑

  1. 修改SetupTwoActivity,完善跳转的相应逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

import com.example.mobilesafe.R;

public class SetupTwoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_two);
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupThreeActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupOneActivity.class);
        startActivity(intent);
        finish();
    }
}
  1. 在Activity新建SetupThreeActivity,作为第三个导航页面,同样完善跳转逻辑,其布局文件和类代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.SetupThreeActivity">

    <TextView
        style="@style/TitleStyle"
        android:text="3.设置安全号码"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:layout_margin="5dp"
        android:text="SIM卡变更后:\n报警短信会发送给安全号码"/>

    <EditText
        android:id="@+id/et_phone_number"
        android:hint="请输入电话号码"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/btn_select_number"
        android:background="@drawable/selector_number_btn_bg"
        android:text="选择联系人"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_online"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:background="@drawable/bind"
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <!--  图片选择器,选择时和未选择时显示的图片不同  -->
        <Button
            style="@style/PreBtnStyle"/>

        <Button
            style="@style/NextBtnStyle"/>
    </RelativeLayout>

</LinearLayout>
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

import com.example.mobilesafe.R;

public class SetupThreeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_three);
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupFourActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupTwoActivity.class);
        startActivity(intent);
        finish();
    }
}
  1. 在drawable下新建selector_number_btn_bg.xml,作为第三个导航界面中按钮的状态选择器,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/function_greenbutton_pressed"/>
    <item android:drawable="@drawable/function_greenbutton_normal"/>
</selector>
  1. 在Activity新建SetupFourActivity,作为第四个导航页面,同样完善跳转逻辑,其布局文件和类代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.SetupFourActivity">

    <TextView
        style="@style/TitleStyle"
        android:text="4.恭喜您,设置完成"/>

    <CheckBox
        android:id="@+id/cb_box"
        android:text="您没有开启防盗保护"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:background="@android:drawable/presence_online"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:background="@drawable/phone"
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <!--  图片选择器,选择时和未选择时显示的图片不同  -->
        <Button
            style="@style/PreBtnStyle"/>

        <Button
            style="@style/NextBtnStyle"/>
    </RelativeLayout>

</LinearLayout>
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

import com.example.mobilesafe.R;
import com.example.mobilesafe.constant.ConstantValue;
import com.example.mobilesafe.utils.SharedPreferencesUtil;

public class SetupFourActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_four);
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupOverActivity.class);
        startActivity(intent);
        finish();
        // 存储标识
        SharedPreferencesUtil.putBoolean(this, ConstantValue.SETUP_OVER,true);
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupThreeActivity.class);
        startActivity(intent);
        finish();
    }
}

9.手机防盗——绑定sim卡序列号

上一节中我们完善了手机防盗的四个界面的视图,这一节里我们来逐步完善对应逻辑,首先来完善第二个导航界面的逻辑,即绑定sim卡序列号

  1. 修改ConstantValue,添加静态常量SIM_NUMBER,作为本机SIM卡的标识符,代码如下:
package com.example.mobilesafe.constant;

public class ConstantValue {

    /**
     * 记录更新的状态
     */
    public static final String OPEN_UPDATE = "open_update";

    /**
     * 手机防盗——设置密码的状态
     */
    public static final String MOBILE_SAFE_PASSWORD = "mobile_safe_password";

    /**
     * 手机防盗——四个界面是否设置完成的状态
     */
    public static final String SETUP_OVER = "setup_over";

    /**
     * 手机防盗——SIM卡绑定序列号
     */
    public static final String SIM_NUMBER = "sim_number";
}
  1. 修改SharedPreferencesUtil,添加remove(),作为移除SharedPreferences中特定节点数据的方法,代码如下:
 package com.example.mobilesafe.utils;

import android.content.Context;
import android.content.SharedPreferences;

public class SharedPreferencesUtil {

    private static SharedPreferences sp;

    /**
     * 1.写入(boolean)
     * @param ctx 上下文
     * @param key 键
     * @param value 值
     */
    public static void putBoolean(Context ctx,String key,boolean value){
        if (sp == null){
            sp = ctx.getSharedPreferences("config", Context.MODE_PRIVATE);
        }
        sp.edit().putBoolean(key,value).commit();
    }

    /**
     * 2.读取(boolean)
     * @param ctx 上下文
     * @param key 键
     * @param defValue (默认)值
     * @return 默认值或者相应结果
     */
    public static boolean getBoolean(Context ctx,String key,boolean defValue){
        if (sp == null){
            sp = ctx.getSharedPreferences("config", Context.MODE_PRIVATE);
        }
        return sp.getBoolean(key,defValue);
    }

    /**
     * 3.写入(string)
     * @param ctx 上下文
     * @param key 键
     * @param value 值
     */
    public static void putString(Context ctx,String key,String value){
        if (sp == null){
            sp = ctx.getSharedPreferences("config", Context.MODE_PRIVATE);
        }
        sp.edit().putString(key,value).commit();
    }

    /**
     * 4.读取(string)
     * @param ctx 上下文
     * @param key 键
     * @param defValue (默认)值
     * @return 默认值或者相应结果
     */
    public static String getString(Context ctx,String key,String defValue){
        if (sp == null){
            sp = ctx.getSharedPreferences("config", Context.MODE_PRIVATE);
        }
        return sp.getString(key,defValue);
    }

    /**
     * 5.移除节点
     * @param ctx 上下文
     * @param key 键
     */
    public static void remove(Context ctx, String key) {
        if (sp == null){
            sp = ctx.getSharedPreferences("config", Context.MODE_PRIVATE);
        }
        sp.edit().remove(key).commit();
    }
}
  1. 修改SetupTwoActivity,完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;

import com.example.mobilesafe.R;
import com.example.mobilesafe.constant.ConstantValue;
import com.example.mobilesafe.utils.SharedPreferencesUtil;
import com.example.mobilesafe.view.SettingItemView;

public class SetupTwoActivity extends AppCompatActivity {

    /**
     * SettingItemView实例
     */
    private SettingItemView siv_sim_bound;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_two);

        // 初始化UI
        initUI();
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view) {
        Intent intent = new Intent(getApplicationContext(), SetupThreeActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view) {
        Intent intent = new Intent(getApplicationContext(), SetupOneActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 3.初始化UI
     */
    private void initUI() {
        siv_sim_bound = findViewById(R.id.siv_sim_bound);
        // 1.回显(读取已有的绑定状态,用作显示,看sp中是否存储了sim卡的序列号)
        String sim_number = SharedPreferencesUtil.getString(this, ConstantValue.SIM_NUMBER, "");
        // 2.判断是否序列卡号为空
        if (TextUtils.isEmpty("")) {
            siv_sim_bound.setCheck(false);
        } else {
            siv_sim_bound.setCheck(true);
        }
        siv_sim_bound.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 3.获取原有的状态
                boolean isCheck = siv_sim_bound.isCheck();
                // 4.将原有状态取反,设置给当前条目
                siv_sim_bound.setCheck(!isCheck);
                // 5.存储序列卡号
                if (!isCheck) {
                    // 6.存储
                    // 6.1 获取sim卡序列号
                    TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
                    // 6.2 获取sim卡的序列卡号,注意在Android 6.0版本以上要动态申请权限
                    if (ActivityCompat.checkSelfPermission(SetupTwoActivity.this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
                        return;
                    }
                    String simSerialNumber = manager.getSimSerialNumber();
                    // 6.3 存储
                    SharedPreferencesUtil.putString(getApplicationContext(),ConstantValue.SIM_NUMBER,simSerialNumber);
                    Log.i("SetupTwoActivity","存储成功!" + simSerialNumber);
                }else {
                    // 7.不存储,并且删除
                    SharedPreferencesUtil.remove(getApplicationContext(),ConstantValue.SIM_NUMBER);
                    Log.i("SetupTwoActivity","存储失败!");
                }
            }
        });
    }
}

10.手机防盗——第二个导航界面跳转到第三个导航界面

我们完善了绑定sim卡的逻辑,现在需要完善跳转逻辑

修改SetupTwoActivity,修改nextPage()方法,完善跳转逻辑,代码如下:

package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;

import com.example.mobilesafe.R;
import com.example.mobilesafe.constant.ConstantValue;
import com.example.mobilesafe.utils.SharedPreferencesUtil;
import com.example.mobilesafe.utils.ToastUtil;
import com.example.mobilesafe.view.SettingItemView;

public class SetupTwoActivity extends AppCompatActivity {

    /**
     * SettingItemView实例
     */
    private SettingItemView siv_sim_bound;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_two);

        // 初始化UI
        initUI();
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view) {
        String serialNumber = SharedPreferencesUtil.getString(this, ConstantValue.SIM_NUMBER, "");
        if (!TextUtils.isEmpty(serialNumber)){
            Intent intent = new Intent(getApplicationContext(), SetupThreeActivity.class);
            startActivity(intent);
            finish();
            overridePendingTransition(R.anim.next_in_anim,R.anim.next_out_anim);
        }else {
            ToastUtil.show(this,"请绑定sim卡");
        }

    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view) {
        Intent intent = new Intent(getApplicationContext(), SetupOneActivity.class);
        startActivity(intent);
        finish();
        overridePendingTransition(R.anim.pre_in_anim,R.anim.pre_out_anim);
    }

    /**
     * 3.初始化UI
     */
    private void initUI() {
        siv_sim_bound = findViewById(R.id.siv_sim_bound);
        // 0.动态获取授权
        if (ContextCompat.checkSelfPermission(SetupTwoActivity.this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(SetupTwoActivity.this, new String[]{Manifest.permission.READ_PHONE_STATE}, 1);
        }
        // 1.回显(读取已有的绑定状态,用作显示,看sp中是否存储了sim卡的序列号)
        String sim_number = SharedPreferencesUtil.getString(this, ConstantValue.SIM_NUMBER, "");
        // 2.判断是否序列卡号为空
        if (TextUtils.isEmpty(sim_number)) {
            siv_sim_bound.setCheck(false);
        } else {
            siv_sim_bound.setCheck(true);
        }
        siv_sim_bound.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 3.获取原有的状态
                boolean isCheck = siv_sim_bound.isCheck();
                // 4.将原有状态取反,设置给当前条目
                siv_sim_bound.setCheck(!isCheck);
                // 5.存储序列卡号
                if (!isCheck) {
                    // 6.存储
                    // 6.1 获取sim卡序列号
                    TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
                    // 6.2 获取sim卡的序列卡号,注意在Android 6.0版本以上要动态申请权限
                    if (ContextCompat.checkSelfPermission(SetupTwoActivity.this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
                        return;
                    }
                    // String simSerialNumber = manager.getSimSerialNumber(); 获取不到SIM卡号,故而写死
                    String simSerialNumber = "SIM卡号";
                    // 6.3 存储
                    SharedPreferencesUtil.putString(getApplicationContext(),ConstantValue.SIM_NUMBER,simSerialNumber);
                    Log.i("SetupTwoActivity","存储成功!" + simSerialNumber);
                }else {
                    // 7.不存储,并且删除
                    SharedPreferencesUtil.remove(getApplicationContext(),ConstantValue.SIM_NUMBER);
                    Log.i("SetupTwoActivity","存储失败!");
                }
            }
        });
    }
}

11.手机防盗——第三个导航界面编写

之前我们完成了第二个导航界面的编写,现在需要完成第三个导航界面的编写,首先来完成布局

  1. 新增ContactListActivity,作为显示联系人列表的Activity,其布局和类代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.ContactListActivity">

    <TextView
        style="@style/TitleStyle"
        android:text="选择联系人"/>
    
    <ListView
        android:id="@+id/lv_contact"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;

import com.example.mobilesafe.R;

public class ContactListActivity extends AppCompatActivity {

    private ListView lv_contact;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact_list);

        // 初始化UI
        intiUI();

        // 初始化数据
        initData();
    }

    /**
     * 1.初始化UI
     */
    private void intiUI() {
        lv_contact = findViewById(R.id.lv_contact);
    }


    /**
     * 2.初始化数据
     */
    private void initData() {
    }
}
  1. 修改SetupThreeActivity,添加initUI(),作为初始化ui的方法,代码如下:
package com.example.mobilesafe.activity;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.example.mobilesafe.R;

public class SetupThreeActivity extends AppCompatActivity {

    /**
     * "电话号码"编辑框
     */
    private EditText et_phone_number;

    /**
     * "选择联系人"按钮
     */
    private Button btn_select_number;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_three);

        // 初始化UI
        initUI();
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupFourActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupTwoActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 3.初始化UI
     */
    private void initUI() {
        et_phone_number = findViewById(R.id.et_phone_number);
        btn_select_number = findViewById(R.id.btn_select_number);
        btn_select_number.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), ContactListActivity.class);
                startActivityForResult(intent,0);
            }
        });
    }

    /**
     * 4.Activity回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

12.手机防盗——联系人内容提供者分析过程

上一节里我们完成了第三个导航界面的布局编写,现在来完善相关的代码逻辑,主要使用到内容解析器去匹配Uri地址

透过源码得知,系统联系人数据库中的核心表有三张:

  • raw_contact:联系人表,
  • data:用户信息表
  • mimetype:类型表

而Uri的格式为:content://com.android.contacts/表名

分析过后,我们可以开始编写代码了

13.手机防盗——获取联系人唯一性id

前面我们分析了获取联系人信息的相关逻辑,现在需要开始编写相应代码

修改ContactListActivity,完善相应逻辑,注意在Android 6.0版本以上要动态获取授权,代码如下:

package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;

import com.example.mobilesafe.R;

public class ContactListActivity extends AppCompatActivity {

    private ListView lv_contact;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact_list);

        // 初始化UI
        intiUI();

        // 初始化数据
        initData();
    }

    /**
     * 1.初始化UI
     */
    private void intiUI() {
        lv_contact = findViewById(R.id.lv_contact);
        if (ContextCompat.checkSelfPermission(ContactListActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(ContactListActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
        }
    }


    /**
     * 2.初始化数据
     */
    private void initData() {
        // 读取联系人是一个耗时操作,可以放在线程中执行
        new Thread(){
            @Override
            public void run() {
                // 1.获取内容解析器对象
                ContentResolver contentResolver = getContentResolver();
                // 2.封装查询过程
                Cursor cursor = contentResolver.query(
                        Uri.parse("content://com.android.contacts/raw_contacts"),
                        new String[]{"contact_id"},
                        null, null, null);
                // 3.循环游标,直到没有数据为止
                while (cursor.moveToNext()){
                    String id = cursor.getString(0);
                    Log.i("ContactListActivity",id);
                }
                // 4.关闭游标
                cursor.close();
            }
        }.start();
    }
}

14.手机防盗——关联查询data表 & 数据封装分析

上一节中我们获取到了所有联系人的id,现在需要通过关联查询获取联系人的具体信息

修改ContactListActivity,进一步完善相应逻辑,代码如下:

package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;

import com.example.mobilesafe.R;

public class ContactListActivity extends AppCompatActivity {

    private ListView lv_contact;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact_list);

        // 初始化UI
        intiUI();

        // 初始化数据
        initData();
    }

    /**
     * 1.初始化UI
     */
    private void intiUI() {
        lv_contact = findViewById(R.id.lv_contact);
        if (ContextCompat.checkSelfPermission(ContactListActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(ContactListActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
        }
    }


    /**
     * 2.初始化数据
     */
    private void initData() {
        // 读取联系人是一个耗时操作,可以放在线程中执行
        new Thread(){
            @Override
            public void run() {
                // 1.获取内容解析器对象
                ContentResolver contentResolver = getContentResolver();
                // 2.封装查询过程
                Cursor cursor = contentResolver.query(
                        Uri.parse("content://com.android.contacts/raw_contacts"),
                        new String[]{"contact_id"},
                        null, null, null);
                // 3.循环游标,直到没有数据为止
                while (cursor.moveToNext()){
                    String id = cursor.getString(0);
                    Log.i("ContactListActivity",id);
                    // 4.根据用户唯一性id值,查询data表和mimetype表生成的视图,获取data以及mimetype字段
                    Cursor indexCursor = contentResolver.query(
                            Uri.parse("content://com.android.contacts/data"),
                            new String[]{"data1", "mimetype"},
                            "raw_contact_id = ?", new String[]{id}, null);
                    while (indexCursor.moveToNext()){
                        // 5.循环获取每一个联系人的电话号码以及姓名
                        String data1 = indexCursor.getString(0);
                        String mimetype = indexCursor.getString(1);
                        Log.i("ContactListActivity",data1);
                        Log.i("ContactListActivity",mimetype);
                    }
                    // 6.关闭游标
                    indexCursor.close();
                }
                // 7.关闭游标
                cursor.close();
            }
        }.start();
    }
}

15.手机防盗——联系人数据封装 & 填充数据适配器

上一节里我们获取到了联系人的数据,现在需要将这些数据进行封装,并填充到数据适配器中

  1. 在res/layout布局下新增listview_contact_item.xml,作为显示联系人内容的子布局,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_name"
        android:textColor="#f00"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/tv_phone"
        android:textColor="#000"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
  1. 修改ContactListActivity,进一步完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.example.mobilesafe.R;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ContactListActivity extends AppCompatActivity {

    /**
     * ListView
     */
    private ListView lv_contact;

    /**
     * 存放联系人的集合
     */
    private List<HashMap<String,String>> mContactList = new ArrayList<>();

    /**
     * Handler对象
     */
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            // 10.填充数据适配器
            MyAdapter adapter = new MyAdapter();
            lv_contact.setAdapter(adapter);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact_list);

        // 初始化UI
        intiUI();

        // 初始化数据
        initData();
    }

    /**
     * 1.初始化UI
     */
    private void intiUI() {
        lv_contact = findViewById(R.id.lv_contact);
        if (ContextCompat.checkSelfPermission(ContactListActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(ContactListActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
        }
    }


    /**
     * 2.初始化数据
     */
    private void initData() {
        // 读取联系人是一个耗时操作,可以放在线程中执行
        new Thread(){
            @Override
            public void run() {
                // 1.获取内容解析器对象
                ContentResolver contentResolver = getContentResolver();
                // 2.封装查询过程
                Cursor cursor = contentResolver.query(
                        Uri.parse("content://com.android.contacts/raw_contacts"),
                        new String[]{"contact_id"},
                        null, null, null);
                mContactList.clear();
                // 3.循环游标,直到没有数据为止
                while (cursor.moveToNext()){
                    String id = cursor.getString(0);
                    Log.i("ContactListActivity",id);
                    // 4.根据用户唯一性id值,查询data表和mimetype表生成的视图,获取data以及mimetype字段
                    Cursor indexCursor = contentResolver.query(
                            Uri.parse("content://com.android.contacts/data"),
                            new String[]{"data1", "mimetype"},
                            "raw_contact_id = ?", new String[]{id}, null);
                    HashMap<String, String> hashMap = new HashMap<>();
                    while (indexCursor.moveToNext()){
                        // 5.循环获取每一个联系人的电话号码以及姓名
                        String data1 = indexCursor.getString(0);
                        String mimetype = indexCursor.getString(1);
                        // 区分类型填充数据
                        if (mimetype.equals("vnd.android.cursor.item/phone_v2")){
                            // 数据非空
                            if (!TextUtils.isEmpty(data1)){
                                hashMap.put("phone",data1);
                            }
                        }else if(mimetype.equals("vnd.android.cursor.item/name")){
                            // 数据非空
                            if (!TextUtils.isEmpty(data1)){
                                hashMap.put("name",data1);
                            }
                        }
                    }
                    // 6.关闭游标
                    indexCursor.close();
                    // 7.添加数据
                    mContactList.add(hashMap);
                }
                // 8.关闭游标
                cursor.close();
                // 9.发送消息给主线程
                mHandler.sendEmptyMessage(0);
            }
        }.start();
    }

    /**
     * 3.数据适配器内部类
     */
    private class MyAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return mContactList.size();
        }

        @Override
        public HashMap<String, String> getItem(int position) {
            return mContactList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = View.inflate(getApplicationContext(), R.layout.listview_contact_item, null);
            TextView tv_name = view.findViewById(R.id.tv_name);
            TextView tv_phone = view.findViewById(R.id.tv_phone);
            tv_name.setText(getItem(position).get("name"));
            tv_phone.setText(getItem(position).get("phone"));
            return view;
        }
    }
}

16.手机防盗——选择联系人 & 结束activity没有传递intent

上一节中我们获取到了所有联系人的信息,现在需要为这些信息注册点击事件

  1. 修改ContactListActivity,修改initUI()方法,完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.example.mobilesafe.R;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ContactListActivity extends AppCompatActivity {

    /**
     * ListView
     */
    private ListView lv_contact;

    /**
     * 存放联系人的集合
     */
    private List<HashMap<String,String>> mContactList = new ArrayList<>();

    /**
     * 适配器对象
     */
    private MyAdapter mAdapter = new MyAdapter();

    /**
     * Handler对象
     */
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            // 10.填充数据适配器
            MyAdapter adapter = new MyAdapter();
            lv_contact.setAdapter(adapter);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact_list);

        // 初始化UI
        intiUI();

        // 初始化数据
        initData();
    }

    /**
     * 1.初始化UI
     */
    private void intiUI() {
        lv_contact = findViewById(R.id.lv_contact);
        if (ContextCompat.checkSelfPermission(ContactListActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(ContactListActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
        }
        lv_contact.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // 1.获取点中条目的索引,指向集合中的对象
                if (mAdapter != null){
                    HashMap<String, String> item = mAdapter.getItem(position);
                    // 2.获取当前条目指向集合对应的电话号码
                    String phone = item.get("phone");
                    // 3.此电话号码需要给第三个导航界面使用
                    Intent intent = new Intent();
                    intent.putExtra("phone",phone);
                    setResult(0,intent);
                    // 4.结束当前Activity
                    finish();
                }
            }
        });
    }


    /**
     * 2.初始化数据
     */
    private void initData() {
        // 读取联系人是一个耗时操作,可以放在线程中执行
        new Thread(){
            @Override
            public void run() {
                // 1.获取内容解析器对象
                ContentResolver contentResolver = getContentResolver();
                // 2.封装查询过程
                Cursor cursor = contentResolver.query(
                        Uri.parse("content://com.android.contacts/raw_contacts"),
                        new String[]{"contact_id"},
                        null, null, null);
                mContactList.clear();
                // 3.循环游标,直到没有数据为止
                while (cursor.moveToNext()){
                    String id = cursor.getString(0);
                    Log.i("ContactListActivity",id);
                    // 4.根据用户唯一性id值,查询data表和mimetype表生成的视图,获取data以及mimetype字段
                    Cursor indexCursor = contentResolver.query(
                            Uri.parse("content://com.android.contacts/data"),
                            new String[]{"data1", "mimetype"},
                            "raw_contact_id = ?", new String[]{id}, null);
                    HashMap<String, String> hashMap = new HashMap<>();
                    while (indexCursor.moveToNext()){
                        // 5.循环获取每一个联系人的电话号码以及姓名
                        String data1 = indexCursor.getString(0);
                        String mimetype = indexCursor.getString(1);
                        // 区分类型填充数据
                        if (mimetype.equals("vnd.android.cursor.item/phone_v2")){
                            // 数据非空
                            if (!TextUtils.isEmpty(data1)){
                                hashMap.put("phone",data1);
                            }
                        }else if(mimetype.equals("vnd.android.cursor.item/name")){
                            // 数据非空
                            if (!TextUtils.isEmpty(data1)){
                                hashMap.put("name",data1);
                            }
                        }
                    }
                    // 6.关闭游标
                    indexCursor.close();
                    // 7.添加数据
                    mContactList.add(hashMap);
                }
                // 8.关闭游标
                cursor.close();
                // 9.发送消息给主线程
                mHandler.sendEmptyMessage(0);
            }
        }.start();
    }

    /**
     * 3.数据适配器内部类
     */
    private class MyAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return mContactList.size();
        }

        @Override
        public HashMap<String, String> getItem(int position) {
            return mContactList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = View.inflate(getApplicationContext(), R.layout.listview_contact_item, null);
            TextView tv_name = view.findViewById(R.id.tv_name);
            TextView tv_phone = view.findViewById(R.id.tv_phone);
            tv_name.setText(getItem(position).get("name"));
            tv_phone.setText(getItem(position).get("phone"));
            return view;
        }
    }
}
  1. 修改SetupThreeActivity,修改onActivityResult()方法,完善接收数据的相应逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.example.mobilesafe.R;

public class SetupThreeActivity extends AppCompatActivity {

    /**
     * "电话号码"编辑框
     */
    private EditText et_phone_number;

    /**
     * "选择联系人"按钮
     */
    private Button btn_select_number;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_three);

        // 初始化UI
        initUI();
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupFourActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupTwoActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 3.初始化UI
     */
    private void initUI() {
        et_phone_number = findViewById(R.id.et_phone_number);
        btn_select_number = findViewById(R.id.btn_select_number);
        btn_select_number.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), ContactListActivity.class);
                startActivityForResult(intent,0);
            }
        });
    }

    /**
     * 4.Activity回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (data != null){
            // 1.返回到当前界面后接收数据
            String phone = data.getStringExtra("phone");
            // 2.过滤特殊字符(中划线转换成空字符串)
            phone.replace("-","").replace(" ","").trim();
            et_phone_number.setText(phone);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

17.手机防盗——联系人回显过程

获取联系人信息后,我们应该将联系人信息存储到SharedPreferences中

  1. 修改ConstantValue,增加成员常量CONTACT_PHONE,表示被联系人的电话号码,代码如下:
package com.example.mobilesafe.constant;

public class ConstantValue {

    /**
     * 记录更新的状态
     */
    public static final String OPEN_UPDATE = "open_update";

    /**
     * 手机防盗——设置密码的状态
     */
    public static final String MOBILE_SAFE_PASSWORD = "mobile_safe_password";

    /**
     * 手机防盗——四个界面是否设置完成的状态
     */
    public static final String SETUP_OVER = "setup_over";

    /**
     * 手机防盗——SIM卡绑定序列号
     */
    public static final String SIM_NUMBER = "sim_number";

    /**
     * 手机防盗——联系人电话号码
     */
    public static final String CONTACT_PHONE = "contact_phone";
}
  1. 修改SetupThreeActivity,修改onActivityResult()和nextPage()方法,完善存储逻辑,最后再修改initUI()方法,增加回显逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.example.mobilesafe.R;
import com.example.mobilesafe.constant.ConstantValue;
import com.example.mobilesafe.utils.SharedPreferencesUtil;
import com.example.mobilesafe.utils.ToastUtil;

public class SetupThreeActivity extends AppCompatActivity {

    /**
     * "电话号码"编辑框
     */
    private EditText et_phone_number;

    /**
     * "选择联系人"按钮
     */
    private Button btn_select_number;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_three);

        // 初始化UI
        initUI();
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        // 点击按钮以后,需要获取输入框中的联系人,再做下一页操作
        String phone = et_phone_number.getText().toString();
        // sp中存储了相关联系人以后才可以跳转到下一个界面
        // String contact_phone = SharedPreferencesUtil.getString(getApplicationContext(), ConstantValue.CONTACT_PHONE, "");
        if (!TextUtils.isEmpty(phone)){
            Intent intent = new Intent(getApplicationContext(), SetupFourActivity.class);
            startActivity(intent);
            finish();
            // 如果是输入电话号码,同样需要保存
            SharedPreferencesUtil.putString(getApplicationContext(), ConstantValue.CONTACT_PHONE,phone);
        }else {
            ToastUtil.show(this,"请输入电话号码!");
        }
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupTwoActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 3.初始化UI
     */
    private void initUI() {
        et_phone_number = findViewById(R.id.et_phone_number);
        // 回显过程
        String phone = SharedPreferencesUtil.getString(this, ConstantValue.CONTACT_PHONE, "");
        et_phone_number.setText(phone);
        btn_select_number = findViewById(R.id.btn_select_number);
        btn_select_number.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), ContactListActivity.class);
                startActivityForResult(intent,0);
            }
        });
    }

    /**
     * 4.Activity回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (data != null){
            // 1.返回到当前界面后接收数据
            String phone = data.getStringExtra("phone");
            // 2.过滤特殊字符(中划线转换成空字符串)
            phone.replace("-","").replace(" ","").trim();
            et_phone_number.setText(phone);
            // 3.存储联系人
            SharedPreferencesUtil.putString(getApplicationContext(), ConstantValue.CONTACT_PHONE,phone);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

18.手机防盗——第四个导航界面编写

之前的小节中,我们完成了三个导航界面的编写,现在需要完成最后一个导航界面的编写

  1. 修改ConstatValue,增加OPEN_SECURITY常量,表示是否开启防盗保护,代码如下:
package com.example.mobilesafe.constant;

public class ConstantValue {

    /**
     * 设置中心——记录更新的状态
     */
    public static final String OPEN_UPDATE = "open_update";

    /**
     * 手机防盗——设置密码的状态
     */
    public static final String MOBILE_SAFE_PASSWORD = "mobile_safe_password";

    /**
     * 手机防盗——四个界面是否设置完成的状态
     */
    public static final String SETUP_OVER = "setup_over";

    /**
     * 手机防盗——SIM卡绑定序列号
     */
    public static final String SIM_NUMBER = "sim_number";

    /**
     * 手机防盗——联系人电话号码
     */
    public static final String CONTACT_PHONE = "contact_phone";

    /**
     * 手机防盗——是否开启防盗保护总开关
     */
    public static final String OPEN_SECURITY = "open_security";
}
  1. 修改SetupFourActivity,增加initUI()方法,完善对其视图的操作逻辑,最后再完善跳转逻辑,代码如下:
package com.example.mobilesafe.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;

import com.example.mobilesafe.R;
import com.example.mobilesafe.constant.ConstantValue;
import com.example.mobilesafe.utils.SharedPreferencesUtil;
import com.example.mobilesafe.utils.ToastUtil;

public class SetupFourActivity extends AppCompatActivity {

    /**
     * CheckBox控件
     */
    private CheckBox cb_box;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setup_four);

        // 初始化UI
        initUI();
    }

    /**
     * 1.“下一页”按钮的点击方法
     * @param view
     */
    public void nextPage(View view){
        boolean open_security = SharedPreferencesUtil.getBoolean(this, ConstantValue.OPEN_SECURITY, false);
        if (open_security){
            Intent intent = new Intent(getApplicationContext(), SetupOverActivity.class);
            startActivity(intent);
            finish();
            // 存储标识
            SharedPreferencesUtil.putBoolean(this, ConstantValue.SETUP_OVER,true);
            overridePendingTransition(R.anim.next_in_anim,R.anim.next_out_anim);
        }else {
            ToastUtil.show(getApplicationContext(),"请开启防盗保护");
        }
    }

    /**
     * 2.“上一页”按钮的点击方法
     * @param view
     */
    public void prePage(View view){
        Intent intent = new Intent(getApplicationContext(), SetupThreeActivity.class);
        startActivity(intent);
        finish();
        overridePendingTransition(R.anim.pre_in_anim,R.anim.pre_out_anim);
    }

    /**
     * 3.初始化UI
     */
    private void initUI() {
        cb_box = findViewById(R.id.cb_box);
        // 1.是否选中状态的回显
        boolean open_security = SharedPreferencesUtil.getBoolean(this, ConstantValue.OPEN_SECURITY, false);
        // 2.根据状态,修改checkbox后续的文字显示
        cb_box.setChecked(open_security);
        if (open_security){
            cb_box.setText("安全设置已开启");
        }else {
            cb_box.setText("安全设置已关闭");
        }
        // 3.点击过程中,checkbox状态的切换
        cb_box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // 4.状态切换后的存储
                SharedPreferencesUtil.putBoolean(getApplicationContext(),ConstantValue.OPEN_SECURITY,isChecked);
                // 5.根据开启关闭状态,去修改显示的文字
                if (isChecked){
                    cb_box.setText("安全设置已开启");
                }else {
                    cb_box.setText("安全设置已关闭");
                }
            }
        });
    }
}

19.手机防盗——平移动画分析

之前我们已经完成了四个导航界面的逻辑实现,现在需要做一个进一步优化——即让四个界面跳转时增加一个平移动画的效果

这里主要做出“上一页”和“下一页‘两个按钮在点击时所触发的效果,这一节主要分析平移动画

  • "上一页"的动画逻辑图:

在这里插入图片描述

  • "下一页"的动画逻辑图:

在这里插入图片描述

  • 整体动画逻辑图:

在这里插入图片描述

平移动画总体可以划分为:

  • 上一页
    • 移入动画:(-屏幕宽度,y) ——> (0,y)
    • 移出动画:(0,y) ——> (屏幕宽度,y)
  • 下一页
    • 移入动画:(屏幕宽度,y) ——> (0,y)
    • 移出动画:(0,y) ——> (-屏幕宽度,y)

20.手机防盗——平移动画集成

经过上一节的分析后,我们现在开始着手实现平移动画

  1. 在res目录下新建anim,作为存放动画的文件夹,分别创建next_in_anim.xml,next_out_anim.xml,pre_in_anim.xml,pre_out_anim.xml

  2. 分别编写next_in_anim.xml,next_out_anim.xml,pre_in_anim.xml,pre_out_anim.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- -100%p 负一屏幕的宽度大小值-->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="100%p"
    android:toXDelta="0"
    android:duration="500">

</translate>
<?xml version="1.0" encoding="utf-8"?>
<!-- -100%p 负一屏幕的宽度大小值-->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0"
    android:toXDelta="-100%p"
    android:duration="500">

</translate>
<?xml version="1.0" encoding="utf-8"?>
<!-- -100%p 负一屏幕的宽度大小值-->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="-100%p"
    android:toXDelta="0"
    android:duration="500">

</translate>
<?xml version="1.0" encoding="utf-8"?>
<!-- -100%p 负一屏幕的宽度大小值-->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0"
    android:toXDelta="100%p"
    android:duration="500">

</translate>
  1. 分别修改SetupOneActivity,SetupTwoActivity,SetupThreeActivity,SetupFourActivity,在nextPage()和prePage()中分别添加以下代码:
overridePendingTransition(R.anim.next_in_anim,R.anim.next_out_anim);
overridePendingTransition(R.anim.pre_in_anim,R.anim.pre_out_anim);
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赈川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值