Android---程序锁(1)展示页面的搭建

一.写在前面的话

在日常使用手机的过程中,我们经常希望有这样一个功能:可以对我们的某一个应用加锁,进入的时候需要输入密码验证身份,然后才可以进入主界面,这就是一个程序锁的功能。其实这种功能并不难实现,正好在我最近跟着黑马74期视频敲的一个大的Demo里有这一块的内容,所以决定记录一下实现的方式。纯记录。。

二.界面显示逻辑

2.1界面效果图
Android---程序锁(1)展示页面的搭建_第1张图片
Android---程序锁(1)展示页面的搭建_第2张图片

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多人浏览。。是我的 错觉吗?我这个渣渣的技术博客一篇文章居然访问量这么高。 还是最近很多人在学这一块的实现?
不管为什么,这也让我更加坚定,坚持写技术博客的决心!

Android—程序锁(2)业务逻辑

你可能感兴趣的:(Android---程序锁(1)展示页面的搭建)