应用程序主界面
主页效果图
- 提取标题栏Style以便后期复用
\res\values下styles.xml
<!-- <TextView
android:text="功能列表"
android:gravity="center"
android:textSize="20sp"
android:textColor="#000"
android:padding="10dp"
android:background="#0f0"
android:layout_width="match_parent"
android:layout_height="wrap_content"/> -->
<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>
使用
<TextView
style="@style/TitleStyle"
android:text="功能列表" />
- 跑马灯效果
<!-- android:ellipsize="end"添加省略点的所在位置 -->
<!-- 想让文字出现跑马灯效果,必须让其获取焦点 -->
<!-- android:marqueeRepeatLimit="marquee_forever"一直滚动属性 -->
<!-- 自定义控件达到滚动效果(其实就是重新原有的TextView,让其一直能够获取焦点即可) -->
<TextView
android:text="秋天秋天悄悄过去,留下小秘密,啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊"
android:textColor="#000"
android:singleLine="true"
android:padding="5dp"
android:ellipsize="marquee"
android:focusable="true"
android:marqueeRepeatLimit="marquee_forever"
android:focusableInTouchMode="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
主页面GridView搭建
和ListView使用方式类似
activity布局配置
<!--android:numColumns指定列数 -->
<!-- android:verticalSpacing="10dp"指定内部条目竖直方向间距为10dp -->
<GridView
android:id="@+id/gv_home"
android:numColumns="3"
android:verticalSpacing="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</GridView>
gridview_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:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_icon"
android:background="@drawable/ic_launcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_title"
android:text="模块标题"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
activity内示例代码填充数据
private void initData() {
//准备数据(文字(9组),图片(9张))
mTitleStrs = new String[]{
"手机防盗","通信卫士","软件管理","进程管理","流量统计","手机杀毒","缓存清理","高级工具","设置中心"
};
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
};
//九宫格控件设置数据适配器(等同ListView数据适配器)
gv_home.setAdapter(new MyAdapter());
//注册九宫格单个条目点击事件
gv_home.setOnItemClickListener(new OnItemClickListener() {
//点中列表条目索引position
@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;
}
}
});
}
private void initUI() {
gv_home = (GridView) findViewById(R.id.gv_home);
}
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 = (TextView) view.findViewById(R.id.tv_title);
ImageView iv_icon = (ImageView) view.findViewById(R.id.iv_icon);
tv_title.setText(mTitleStrs[position]);
iv_icon.setBackgroundResource(mDrawableIds[position]);
return view;
}
}
自定义组合控件
实现效果图
- 总结自定义组合控件过程
1.声明一个View对象,继承相对布局,或者线性布局或者其他的ViewGroup。
2.在自定义的View对象里面重写它的构造方法,在构造方法里面就把布局都初
始化完毕。
3.根据业务需求添加一些api方法,扩展自定义的组合控件;
4.希望在布局文件里面 可以自定义一些属性。
5.声明自定义属性的命名空间。
xmlns:itheima=”http://schemas.android.com/apk/res/com.itheima.mobilesafe”
6.在res目录下的values目录下创建attrs.xml的文件 声明我们写的属性。
7.在布局文件中写自定义的属性。
8.使用这些定义的属性。自定义View对象的构造方法里面
有一个带两个参数的构造方法布局文件里面定义的属性都放在 AttributeSet
attrs,获取那些定义的属性。
代码示例
setting_item_view.xml布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RelativeLayout
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_title"
android:textColor="#000"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_des"
android:layout_below="@id/tv_title"
android:textColor="#000"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
让当前的 CheckBox不能被点击,即不能响应事件-->
<CheckBox
android:id="@+id/cb_box"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<View
android:background="#000"
android:layout_below="@id/tv_des"
android:layout_width="match_parent"
android:layout_height="1dp"/>
</RelativeLayout>
</RelativeLayout>
自定义View对象
public class SettingItemView extends RelativeLayout {
private static final String NAMESPACE = "http://schemas.android.com/apk/res/com.itheima.mobilesafe74";
private static final String tag = "SettingItemView";
private CheckBox cb_box;
private TextView tv_des;
private String mDestitle;
private String mDesoff;
private String mDeson;
public SettingItemView(Context context) {
this(context,null);
}
public SettingItemView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public SettingItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//xml--->view 将设置界面的一个条目转换成view对象,直接添加到了当前SettingItemView对应的view中
View.inflate(context, R.layout.setting_item_view, this);
//等同于以下两行代码
/*View view = View.inflate(context, R.layout.setting_item_view, null);
this.addView(view);*/
//自定义组合控件中的标题描述
TextView tv_title = (TextView) findViewById(R.id.tv_title);
tv_des = (TextView) findViewById(R.id.tv_des);
cb_box = (CheckBox) findViewById(R.id.cb_box);
//获取自定义以及原生属性的操作,写在此处,AttributeSet attrs对象中获取
initAttrs(attrs);
//获取布局文件中定义的字符串,赋值给自定义组合控件的标题
tv_title.setText(mDestitle);
}
/**
* 返回属性集合中自定义属性属性值
* @param attrs 构造方法中维护好的属性集合
*/
private void initAttrs(AttributeSet attrs) {
/*//获取属性的总个数
Log.i(tag, "attrs.getAttributeCount() = "+attrs.getAttributeCount());
//获取属性名称以及属性值
for(int i=0;i<attrs.getAttributeCount();i++){
Log.i(tag, "name = "+attrs.getAttributeName(i));
Log.i(tag, "value = "+attrs.getAttributeValue(i));
Log.i(tag, "分割线 ================================= ");
}*/
//通过名空间+属性名称获取属性值
mDestitle = attrs.getAttributeValue(NAMESPACE, "destitle");
mDesoff = attrs.getAttributeValue(NAMESPACE, "desoff");
mDeson = attrs.getAttributeValue(NAMESPACE, "deson");
Log.i(tag, mDestitle);
Log.i(tag, mDesoff);
Log.i(tag, mDeson);
}
/**
* 判断是否开启的方法
* @return 返回当前SettingItemView是否选中状态 true开启(checkBox返回true) false关闭(checkBox返回true)
*/
public boolean isCheck(){
//由checkBox的选中结果,决定当前条目是否开启
return cb_box.isChecked();
}
/**
* @param isCheck 是否作为开启的变量,由点击过程中去做传递
*/
public void setCheck(boolean isCheck){
//当前条目在选择的过程中,cb_box选中状态也在跟随(isCheck)变化
cb_box.setChecked(isCheck);
if(isCheck){
//开启
tv_des.setText(mDeson);
}else{
//关闭
tv_des.setText(mDesoff);
}
}
}
设置页布局设置
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
style="@style/TitleStyle"
android:text="设置中心"/>
<!-- 将以下的相对布局,抽取到单独的一个类中去做管理,以后只需要在布局文件中添加此类,即可达到以下效果-->
<!-- <RelativeLayout
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_title"
android:text="自动更新设置"
android:textColor="#000"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_des"
android:layout_below="@id/tv_title"
android:text="自动更新已关闭"
android:textColor="#000"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:id="@+id/cb_box"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<View
android:background="#000"
android:layout_below="@id/tv_des"
android:layout_width="match_parent"
android:layout_height="1dp"/>
</RelativeLayout> -->
<com.itheima.mobilesafe74.view.SettingItemView
xmlns:mobilesafe="http://schemas.android.com/apk/res/com.itheima.mobilesafe74"
android:id="@+id/siv_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
mobilesafe:destitle="自动更新设置"
mobilesafe:desoff="自动更新已关闭"
mobilesafe:deson="自动更新已开启">
</com.itheima.mobilesafe74.view.SettingItemView>
<!-- <com.itheima.mobilesafe74.view.SettingItemView
xmlns:mobilesafe="http://schemas.android.com/apk/res/com.itheima.mobilesafe74"
android:layout_width="match_parent"
android:layout_height="wrap_content"
mobilesafe:destitle="电话归属地的显示设置"
mobilesafe:desoff="归属地的显示已关闭"
mobilesafe:deson="归属地的显示已开启">
</com.itheima.mobilesafe74.view.SettingItemView> -->
<!-- SettingItemView需要在构建布局的时候指定title和des字符串内容 -->
<!-- 自定义属性 -->
<!-- <com.itheima.mobilesafe74.view.SettingItemView
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.itheima.mobilesafe74.view.SettingItemView> -->
</LinearLayout>
自定义属性
\res\values\
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.itheima.mobilesafe74.view.SettingItemView">
<attr name="destitle" format="string"/>
<attr name="desoff" format="string"/>
<attr name="deson" format="string"/>
</declare-styleable>
</resources>
设置页activity
public class SettingActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting);
initUpdate();
}
/**
* 版本更新开关
*/
private void initUpdate() {
final SettingItemView siv_update = (SettingItemView) findViewById(R.id.siv_update);
//获取已有的开关状态,用作显示
boolean open_update = SpUtil.getBoolean(this, ConstantValue.OPEN_UPDATE, false);
//是否选中,根据上一次存储的结果去做决定
siv_update.setCheck(open_update);
siv_update.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//如果之前是选中的,点击过后,变成未选中
//如果之前是未选中的,点击过后,变成选中
//获取之前的选中状态
boolean isCheck = siv_update.isCheck();
//将原有状态取反,等同上诉的两部操作
siv_update.setCheck(!isCheck);
//将取反后的状态存储到相应sp中
SpUtil.putBoolean(getApplicationContext(), ConstantValue.OPEN_UPDATE,!isCheck);
}
});
}
}
SharedPreferences抽取工具
public class SpUtil {
private static SharedPreferences sp;
/**
* 写入boolean变量至sp中
* @param ctx 上下文环境
* @param key 存储节点名称
* @param value 存储节点的值 boolean
*/
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();
}
/**
* 读取boolean标示从sp中
* @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);
}
/**
* 写入boolean变量至sp中
* @param ctx 上下文环境
* @param key 存储节点名称
* @param value 存储节点的值string
*/
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();
}
/**
* 读取boolean标示从sp中
* @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);
}
}
防盗模块自定义对话框
1.检测密码是否已经设置, 弹出设置密码框或输入密码框
设置密码自定义对话框
布局dialog_set_psd.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
style="@style/TitleStyle"
android:text="设置密码"
android:background="#f30"/>
<!-- android:hint=""提示用户输入内容 -->
<EditText
android:id="@+id/et_set_psd"
android:hint="设置密码"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/et_confirm_psd"
android:hint="确认密码"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/bt_submit"
android:text="确认"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:text="取消"
android:id="@+id/bt_cancel"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
activity内代码初始化自定义对话框
protected void showDialog() {
//判断本地是否有存储密码(sp 字符串)
String psd = SpUtil.getString(this, ConstantValue.MOBILE_SAFE_PSD, "");
if(TextUtils.isEmpty(psd)){
//1,初始设置密码对话框
showSetPsdDialog();
}else{
//2,确认密码对话框
showConfirmPsdDialog();
}
}
/**
* 设置密码对话框
*/
private void showSetPsdDialog() {
//因为需要去自己定义对话框的展示样式,所以需要调用dialog.setView(view);
//view是由自己编写的xml转换成的view对象xml----->view
Builder builder = new AlertDialog.Builder(this);
final AlertDialog dialog = builder.create();
final View view = View.inflate(this, R.layout.dialog_set_psd, null);
//让对话框显示一个自己定义的对话框界面效果
dialog.setView(view);
dialog.show();
Button bt_submit = (Button) view.findViewById(R.id.bt_submit);
Button bt_cancel = (Button) view.findViewById(R.id.bt_cancel);
bt_submit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
EditText et_set_psd = (EditText) view.findViewById(R.id.et_set_psd);
EditText et_confirm_psd = (EditText)view.findViewById(R.id.et_confirm_psd);
String psd = et_set_psd.getText().toString();
String confirmPsd = et_confirm_psd.getText().toString();
if(!TextUtils.isEmpty(psd) && !TextUtils.isEmpty(confirmPsd)){
if(psd.equals(confirmPsd)){
//进入应用手机防盗模块,开启一个新的activity
Intent intent = new Intent(getApplicationContext(), TestActivity.class);
startActivity(intent);
//跳转到新的界面以后需要去隐藏对话框
dialog.dismiss();
SpUtil.putString(getApplicationContext(), ConstantValue.MOBILE_SAFE_PSD, psd);
}else{
ToastUtil.show(getApplicationContext(),"确认密码错误");
}
}else{
//提示用户密码输入有为空的情况
ToastUtil.show(getApplicationContext(), "请输入密码");
}
}
});
bt_cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
确认密码自定义对话框
dialog_confirm_psd.xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
style="@style/TitleStyle"
android:text="确认密码"
android:background="#f30"/>
<!-- android:hint=""提示用户输入内容 -->
<EditText
android:id="@+id/et_confirm_psd"
android:hint="确认密码"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/bt_submit"
android:text="确认"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:text="取消"
android:id="@+id/bt_cancel"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
activity内代码初始化
/**
* 确认密码对话框
*/
private void showConfirmPsdDialog() {
//因为需要去自己定义对话框的展示样式,所以需要调用dialog.setView(view);
//view是由自己编写的xml转换成的view对象xml----->view
Builder builder = new AlertDialog.Builder(this);
final AlertDialog dialog = builder.create();
final View view = View.inflate(this, R.layout.dialog_confirm_psd, null);
//让对话框显示一个自己定义的对话框界面效果
dialog.setView(view);
dialog.show();
Button bt_submit = (Button) view.findViewById(R.id.bt_submit);
Button bt_cancel = (Button) view.findViewById(R.id.bt_cancel);
bt_submit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
EditText et_confirm_psd = (EditText)view.findViewById(R.id.et_confirm_psd);
String confirmPsd = et_confirm_psd.getText().toString();
if(!TextUtils.isEmpty(confirmPsd)){
String psd = SpUtil.getString(getApplicationContext(), ConstantValue.MOBILE_SAFE_PSD, "");
if(psd.equals(confirmPsd)){
//进入应用手机防盗模块,开启一个新的activity
Intent intent = new Intent(getApplicationContext(), TestActivity.class);
startActivity(intent);
//跳转到新的界面以后需要去隐藏对话框
dialog.dismiss();
}else{
ToastUtil.show(getApplicationContext(),"确认密码错误");
}
}else{
//提示用户密码输入有为空的情况
ToastUtil.show(getApplicationContext(), "请输入密码");
}
}
});
bt_cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
对话框低版本展示样式,兼容性的处理
dialog.setView(view,0,0,0,0);//设置对话框样式的时候,不需要内边距
实现MD5加密
public class Md5Util {
/**
* @param args
*/
public static void main(String[] args) {
//加盐
String psd = "123"+"abc";
encoder(psd);
}
/**给指定字符串按照md5算法去加密
* @param psd 需要加密的密码
*/
private static void encoder(String psd) {
try {
//1,指定加密算法类型
MessageDigest digest = MessageDigest.getInstance("MD5");
//2,将需要加密的字符串中转换成byte类型的数组,然后进行随机哈希过程
byte[] bs = digest.digest(psd.getBytes());
// System.out.println(bs.length);
//3,循环遍历bs,然后让其生成32位字符串,固定写法
//4,拼接字符串过程
StringBuffer stringBuffer = new StringBuffer();
for (byte b : bs) {
int i = b & 0xff;
//int类型的i需要转换成16机制字符
String hexString = Integer.toHexString(i);
// System.out.println(hexString);
if(hexString.length()<2){
hexString = "0"+hexString;
}
stringBuffer.append(hexString);
}
//5,打印测试
System.out.println(stringBuffer.toString());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}