一.写在前面的话
在日常使用手机的过程中,我们经常希望有这样一个功能:可以对我们的某一个应用加锁,进入的时候需要输入密码验证身份,然后才可以进入主界面,这就是一个程序锁的功能。其实这种功能并不难实现,正好在我最近跟着黑马74期视频敲的一个大的Demo里有这一块的内容,所以决定记录一下实现的方式。纯记录。。
二.界面显示逻辑
2.1界面效果图
2.2layout布局文件
这里我们将“未加锁”和“已加锁”两个模块的ListView写在同一个布局文件中,用android:visibility=”“ 属性结合上方按钮的选中来决定下方是显示哪一个ListView
布局文件代码如下:
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/bt_unlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tab_left_pressed"
android:text="未加锁"
android:textColor="#fff"
android:textSize="18sp"/>
<Button
android:id="@+id/bt_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tab_right_default"
android:text="已加锁"
android:textColor="#fff"
android:textSize="18sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_unlock"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_unlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未加锁应用"/>
<ListView
android:id="@+id/lv_unlock"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_lock"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/tv_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未加锁应用"/>
<ListView
android:id="@+id/lv_lock"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
</LinearLayout>
注意到@+id/ll_lock android:visibility=”gone”,也就是通过对这两个属性的改变来控制下方的ListView到底显示的是哪一个。
2.3ListView的数据来源
有了ListView我们自然就会想到,LIstView始终需要两个部分,数据源和数据适配器,也就是Adapter。首先我记录一下展示数据的来源。
//区分已加锁应用和未加锁的应用
private void initData() {
new Thread(new Runnable() {
@Override
public void run() {
//1.获取手机中所有的应用
mAppInfoList = appInfoProvider.getAppInfoList(getApplicationContext());
//2.区分已加锁应用和未加锁应用
mLockList = new ArrayList<AppInfo>();
mUnlockList = new ArrayList<AppInfo>();
//3.获取数据库中已加锁应用包名的集合
mDao = appLockDao.getInstance(getApplicationContext());
List<String> lockPackageList = mDao.findAll();
for (AppInfo appInfo : mAppInfoList) {
//4.如果循环到的应用的包名在数据库中,说明是已经加锁了的应用
if (lockPackageList.contains(appInfo.getPackageName())) {
mLockList.add(appInfo);
} else {
mUnlockList.add(appInfo);
}
}
//5.告知主线程,数据准备好了,可以使用了 消息机制
mHandler.sendEmptyMessage(0);
}
}).start();
}
其中:appInfoProvider.getAppInfoList(getApplicationContext());
mDao = appLockDao.getInstance(getApplicationContext());
List lockPackageList = mDao.findAll();
这三个是我已经封装好的方法,分别用于拿到手机中所有的应用;拿到岁数据库增删改查的对象;拿到目前数据库中已经有的数据。
这个意思就是说,我将已经加锁的应用放到数据库中,然后将已经加锁和未加锁的应用分别放到两个集合中:mLockList,mUnlockList。由于拿数据这个操作可能耗时,所以我们将这个方法放到线程中去执行。最后在利用消息机制通知主线程,数据已准备好。
2.4Adapter的设置
由于我们将两个ListVIew都写在同一个布局里,所以我们也用一个Adapter同时去配置两个LIstView,只是加上一个private boolean isLock; 这个标记,来区分当前是配置哪一个ListView。
class myAdapter extends BaseAdapter {
private boolean isLock;
//用于区分已加锁和未加锁应用的标识 重写的构造方法
public myAdapter(boolean isLock) {
this.isLock = isLock;
}
@Override
public int getCount() {
if (isLock) {
tv_lock.setText("已加锁应用:" + mLockList.size());
return mLockList.size();
} else {
tv_unlock.setText("未加锁应用:" + mUnlockList.size());
return mUnlockList.size();
}
}
@Override
public AppInfo getItem(int position) {
if (isLock) {
return mLockList.get(position);
} else {
return mUnlockList.get(position);
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = View.inflate(getApplicationContext(), R.layout.listview_islock_item, null);
holder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.iv_lock = (ImageView) convertView.findViewById(R.id.iv_lock);
holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final AppInfo appinfo = getItem(position);
holder.iv_icon.setBackgroundDrawable(appinfo.getIcon());
holder.tv_name.setText(appinfo.getName());
if (isLock) {
holder.iv_lock.setBackgroundResource(R.drawable.lock);
} else {
holder.iv_lock.setBackgroundResource(R.drawable.unlock);
}
return convertView;
}
}
其中getView()方法中用了convertView和holderView来优化Listview,这已经是模板代码了。所以具体的HolderView就不贴出来了 。
==========================================
下面这个部分单独拎出来记录:
当我们在“未加锁”界面点击右边的小锁时候,我们希望达到这样的一种效果:我们点击的这一个条目产生一个动画效果,向右边滑出,然后消失,在“已加锁”界面显示出我们方才点击的哪一个条目
按照这种思路,我们首先弄一个执行动画的类:
/**
* 初始化平移动画,平移自身宽度
*
* @param
* @return
* @author zfy
* @created at 2016/6/26 10:56
*/
private void initAnimation() {
mTranslateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0);
mTranslateAnimation.setDuration(500);
}
接下来我们在Adapter 的getView()方法中,监听holder.iv_lock这个图标 的点击事件:下面就是我一开始犯错误的地方了:
final View finalConvertView = convertView;
holder.iv_lock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//添加动画效果
finalConvertView.startAnimation(mTranslateAnimation);
if (isLock) {
//由已加锁------>未加锁
//添加动画效果
//1.已加锁的集合要删除一个,未加锁的几个要增加一个
mLockList.remove(appinfo);
mUnlockList.add(appinfo);
//2.从已加锁的数据库中删除一条数据
mDao.delete(appinfo.getPackageName());
//3.通知adapter刷新
mLockAdapter.notifyDataSetChanged();
} else {
//未加锁----->已加锁
//1.未加锁的集合要删除一个,已加锁的几个要增加一个
mLockList.add(appinfo);
mUnlockList.remove(appinfo);
//2.从已加锁的数据库中删除一条数据
mDao.insert(appinfo.getPackageName());
//3.通知adapter刷新
mUnlockAdapter.notifyDataSetChanged();
}
一切都是这么的水到渠成,点击加锁按钮–>开启动画–>从未加锁集合中删除–>添加到已加锁集合中–>添加到数据库–>通知adapter刷新。
但是我忽略了一个问题,当我执行平移动画的时候(500ms),下面对集合的操作,更新Adapter的操作就已经在执行了,并且已经执行完了。所以最终实现的动画效果是,我点了一个条目,但是发生平移动画的却是下一个条目。这一点困惑了很久!所以我在这里对动画做了一个监听:当动画执行完了,才接着行对集合,数据库,和adapter刷新的操作!!
final View finalConvertView = convertView;
holder.iv_lock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//添加动画效果
finalConvertView.startAnimation(mTranslateAnimation);
//对动画执行的效果做监听,要监听到动画执行完成之后,再去移除集合中数据,操作数据库,刷新界面
mTranslateAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override //动画结束后回调方法
public void onAnimationEnd(Animation animation) {
if (isLock) {
//由已加锁------>未加锁
//添加动画效果
//1.已加锁的集合要删除一个,未加锁的几个要增加一个
mLockList.remove(appinfo);
mUnlockList.add(appinfo);
//2.从已加锁的数据库中删除一条数据
mDao.delete(appinfo.getPackageName());
//3.通知adapter刷新
mLockAdapter.notifyDataSetChanged();
} else {
//未加锁----->已加锁
//1.未加锁的集合要删除一个,已加锁的几个要增加一个
mLockList.add(appinfo);
mUnlockList.remove(appinfo);
//2.从已加锁的数据库中删除一条数据
mDao.insert(appinfo.getPackageName());
//3.通知adapter刷新
mUnlockAdapter.notifyDataSetChanged();
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
});
最终写成这个样子,就完全没有问题了。
2.5对最上方两个按钮的处理
bt_unlock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1.已加锁列表隐藏,未加锁列表显示
ll_lock.setVisibility(View.GONE);
ll_unlock.setVisibility(View.VISIBLE);
//2.按钮颜色切换
bt_lock.setBackgroundResource(R.drawable.tab_right_default);
bt_unlock.setBackgroundResource(R.drawable.tab_left_pressed);
}
});
bt_lock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1.已加锁列表显示,未加锁列表隐藏
ll_lock.setVisibility(View.VISIBLE);
ll_unlock.setVisibility(View.GONE);
//2.按钮颜色切换
bt_lock.setBackgroundResource(R.drawable.tab_right_pressed);
bt_unlock.setBackgroundResource(R.drawable.tab_left_default);
}
});
比较简单,就不作说明了。
2.6分别设置Adapter
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//接收到消息,填充已加锁和未加锁的数据适配器
mLockAdapter = new myAdapter(true);
lv_lock.setAdapter(mLockAdapter);
mUnlockAdapter = new myAdapter(false);
lv_unlock.setAdapter(mUnlockAdapter);
}
};
这也是常规写法,不做说明
三. 后记
到这里就已经可以实现程序锁的界面展示效果了,但是具体的业务逻辑还没有处理, 只是一个空架子。
由于明天还有《数字信号处理》的抽考,今天还要复习,所以业务逻辑这一块,留到考试考完再记录。
PS: 《数字信号处理》 这门课也是够了。整本书的傅里叶变换,离散傅里叶变换,快速傅里叶变化,Z变换,逆Z变换……TM的 ~
上次写的Widget那片文章,不知道为什么,居然一晚上有2000多人浏览。。是我的 错觉吗?我这个渣渣的技术博客一篇文章居然访问量这么高。 还是最近很多人在学这一块的实现?
不管为什么,这也让我更加坚定,坚持写技术博客的决心!