美文网首页
2018-09-09 关于用户登录的过程(用Fragment去实

2018-09-09 关于用户登录的过程(用Fragment去实

作者: 仲夏之雪梦旅人 | 来源:发表于2018-09-30 15:48 被阅读0次

    之前已经说过如何进行登录界面的编写,一个好的app是需要从用户的角度去出发的,我们登录界面不仅能提供用户密码登录,还应该能支持用户短信登录甚至是短信修改密码,这样就需要我们用更多的精力去优化我们的代码.首先说一下本篇文章的重点
    1.关于如何进行Fragment的跳转
    2.如何进行Fragment之前的传参
    3.如何使从当前Fragment回退到我们需要的界面
    4.如何使用mobSdk进行短信验证.
    5.对于用户输入信息的判断.
    这样,我们一点一点来分析,首先,我们需要进行Fragment的跳转。这个问题很重要,因为我们的界面肯定是有按键需求的,当用户按下一个键后,会要跳转到我们需要的界面上,上一篇文章在最后提到可以用接口回调的方式去实现,其实这样太麻烦了,我们可以用事务处理的方式去实现对Fragment管理.

    public class FragmentTranscationUtil {
        //获取FragmentManager,开启事务,添加碎片
        public static void replaceFragment(FragmentActivity activity, Fragment fragment) {
            FragmentManager manager = activity.getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction.setCustomAnimations(R.anim.activity_right_in, 0, 0, R.anim.activity_right_out);
            transaction.replace(R.id.userloginFrame_layout, fragment);
            transaction.addToBackStack(null);
            transaction.commit();
        }
    
        //获取FragmentManager,开启事务,添加碎片
        public static void replaceFragment(FragmentActivity activity, Fragment fragment, Bundle bundle) {
            FragmentManager manager = activity.getSupportFragmentManager();
            fragment.setArguments(bundle);
            FragmentTransaction transaction = manager.beginTransaction();
            transaction.setCustomAnimations(R.anim.activity_right_in, 0, 0, R.anim.activity_right_out);
            transaction.replace(R.id.userloginFrame_layout, fragment);
            transaction.addToBackStack(null);
            transaction.commit();
        }
    

    这是一个帮助类,参数很简单,首先,我们要获取当前所在Activity,这个不用多说吗,因为Fragment多是嵌套在Activity里面的,第二个参数就是我们需要跳转的目标Fragment
    下面第二个构造函数的第三个参数Bundle则是用来进行传参的.这个我下面会详细讲解
    首先,我们要获取到FragmentManger的实例manger,然后manager.beginTransaction();
    去开启事务,将我们需要执行的逻辑放入到事务中,在调用transaction.commit();去执行
    就行了,这里顺便向大家展示一下Fragment生命周期
    Fragment的生命周期和activity生命周期很像,其生命周期方法如下所示。

    1. onAttach:绑定到activity
    2. onCreate:创建fragment
    3. onCreateView: 创建fragment的布局
    4. onActivityCreated: activity创建完成后
    5. onStart: 可见, 不可交互
    6. onResume: 可见, 可交互
    7. onPause: 部分可见, 不可交互
    8. onStop:不可见
    9. onDestroyView: 销毁fragment的view对象
    10. onDestroy: fragment销毁了
    11. onDetach: 从activity解绑了
      下面给出FragmentTransaction的全部方法(API 24)转载自https://www.jianshu.com/p/5761ee2d3ea1
    • add(Fragment fragment, String tag) // 调用add(int, Fragment, String),填入为0的containerViewId.
    • add(int containerViewId, Fragment fragment) // 调用add(int, Fragment, String),填入为null的tag.
    • add(int containerViewId, Fragment fragment, String tag) // 向Activity中添加一个Fragment.
    • addSharedElement(View sharedElement, String name) // 添加共享元素
    • addToBackStack(String name) // 将事务添加到回退栈
    • attach(Fragment fragment) // 重新关联Fragment(当Fragment被detach时)
    • commit() // 提交事务
    • commitAllowingStateLoss() // 类似commit(),但允许在Activity状态保存之后提交(即允许状态丢失)。
    • commitNow() // 同步提交事务
    • commitNowAllowingStateLoss() // 类似commitNow(),但允许在Activity状态保存之后提交(即允许状态丢失)。
    • detach(Fragment fragment) // 将fragment保存的界面从UI中移除
    • disallowAddToBackStack() // 不允许调用addToBackStack(String)操作
    • hide(Fragment fragment) // 隐藏已存在的Fragment
    • isAddToBackStackAllowed() // 是否允许添加到回退栈
    • isEmpty() // 事务是否未包含的任何操作
    • remove(Fragment fragment) // 移除一个已存在的Fragment
    • replace(int containerViewId, Fragment fragment) // 调用replace(int, Fragment, String)填入为null的tag.
    • replace(int containerViewId, Fragment fragment, String tag) // 替换已存在的Fragment
    • setBreadCrumbShortTitle(int res) // 为事务设置一个BreadCrumb短标题
    • setBreadCrumbShortTitle(CharSequence text) // 为事务设置一个BreadCrumb短标题,将会被FragmentBreadCrumbs使用
    • setBreadCrumbTitle(int res) // 为事务设置一个BreadCrumb全标题,将会被FragmentBreadCrumbs使用
    • setBreadCrumbTitle(CharSequence text) // 为事务设置一个BreadCrumb全标题
    • setCustomAnimations(int enter, int exit, int popEnter, int popExit) // 自定义事务进入/退出以及入栈/出栈的动画效果
    • setCustomAnimations(int enter, int exit) // 自定义事务进入/退出的动画效果
    • setTransition(int transit) // 为事务设置一个标准动画
    • setTransitionStyle(int styleRes) // 为事务标准动画设置自定义样式
    • show(Fragment fragment) // 显示一个被隐藏的Fragment

    这样我们就解决了Fragment跳转问题,同时Fragment回退问题也给出了答案,那就是利用addToBackStack(String name)
    我们知道Activity有任务栈,用户通过startActivity将Activity加入栈,点击返回按钮将Activity出栈。Fragment也有类似的栈,称为回退栈(Back Stack),回退栈是由FragmentManager管理的。

    image

    默认情况下,Fragment事务是不会加入回退栈的,如果想将Fragment加入回退栈并实现事物回滚,首先需要在commit()方法之前调用事务的以下方法将其添加到回退栈中:

    • addToBackStack(String name)
      这个方法在我们跳转Fragment时候将事务添加至回退栈
      Fragment的回退非常简单,然而这里又会出现一个新的问题,就是在修改后的案例每次只能回退到上一步操作,而并不能一次性回退到我们想要的位置,这样才更满足实际开发需要。这就需要我们来多了解事物回滚的相关原理,其实在Fragment回退时,默认调用FragmentManager的popBackStack()方法将最上层的操作弹出回退栈。当栈中有多层时,我们可以根据id或TAG标识来指定弹出到的操作所在层。
    • popBackStack(int id, int flags):其中id表示提交变更时commit()的返回值。
    • popBackStack(String name, int flags):其中name是addToBackStack(String tag)中的tag值。
      在上面2个方法里面,都用到了flags,其实flags有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE。当取值0时,表示除了参数指定这一层之上的所有层都退出栈,指定的这一层为栈顶层;当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数指定的这一层一起退出栈。
      这样Fragment的跳转和回退就说到这里,接下来是传参问题,这个问题也很简单,因为Bundle的底层代码就是HasMap<key,value>组合,首先实例化一个bundle,然后用<key,value>储存想要保存的数据,然后调用
    • setArguments(bundle);//添加将要传递的bundle
      在目标Fragment,使用getArguments();去获取到传入的值,比较简单,就不在赘述了。这样,1,2,3,问题我们都已经解决
      接下来是重点!如何使用mobSDK去实现短信验证.
      首先给大家一张图,便于理解短信验证流程
      TIM截图20180930115033.png

    我个人使用的是Mob开发者平台(http://sms.mob.com)注册,获取到Appkey和appSecret,这个非常重要

    配置Gradle
    1、打开项目根目录的build.gradle,在buildscrip–>dependencies 模块下面添加 classpath ‘com.mob.sdk:MobSDK:+’,如下所示;

    buildscript {
        repositories {
            jcenter()
        }
     
        dependencies {
            ...
            classpath 'com.mob.sdk:MobSDK:+'
     
        }
    }
    

    2、在使用SMSSDK模块的build.gradle中,添加MobSDK插件和扩展,如:

    // 添加插件
    apply plugin: 'com.mob.sdk'
    
    // 在MobSDK的扩展中注册SMSSDK的相关信息
    MobSDK {
        appKey "d580ad5*****"//填写自己获取的appKey
        appSecret "7fcae59a******7e2759e9e397c82bdd"//填写自己获取的appSecret
    
        SMSSDK {}
    }
    

    这里面的appkey和appSecret是自己注册而获取到的.替换一下就行了,
    3、初始化MobSDK
    如果您没有在AndroidManifest中设置appliaction的类名,MobSDK会将这个设置为com.mob.MobApplication,但如果您设置了,请在您自己的Application类中调用:

    MobSDK.init(this);
    

    以初始化MobSDK。
    具体代码
    这里有两种方式去实现,一个是可视化界面,一个是非UI实现,
    因为我个人是是使用无UI的方式,所以会讲述无Ui实现的方法,这里给出可视化界面的文档(http://wiki.mob.com/sdk-sms-android-3-0-0/
    首先介绍一下EventHandler这是一个异步操作,和Handler类似所有的业务逻辑将会在这里处理,它是和registerEventHandler一起使用的,registerEventHandler是一个事件接收器,他是专门负责接收所有被触发的事件,而registerEventHandler本身可以注册多个,所有接收器也会在事件被触发时候接收消息.一般是配套使用,否则容易产生内存泄露
    下面给出具体代码

     //请求短信验证码
        private void SendCode(String country, String number) {
            EventHandler eh = new EventHandler() {
                @Override
                public void afterEvent(int i, int i1, Object o) {
                    if (i == SMSSDK.EVENT_GET_VERIFICATION_CODE) {//获取短信验证码事件
                        //获取验证码成功
                        if (i1 == SMSSDK.RESULT_COMPLETE) {
                            listener.getCodeSuccess();
                        } else if (i1 == SMSSDK.RESULT_ERROR) {
                            listener.getCodeFailure();
                        }
                    }
                }
            };
            SMSSDK.registerEventHandler(eh);
            SMSSDK.getVerificationCode(country, number);
        }
    

    可以看到这是我自定义的一个方法,首先获取国家,中国是86开头,没啥好说的,然后是你将要发送短信验证的用户手机号码,这也没啥好说的,至于listener是我自定义的接口,会统一处理事务逻辑.

     //提交验证码
        private void SubmitCode(String country, final String number, String code) {
            EventHandler eh = new EventHandler() {
                @Override
                public void afterEvent(int i, int i1, Object o) {
                    if (i == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {//验证码提交事件
                        if (i1 == SMSSDK.RESULT_COMPLETE) {
                            //回调成功
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                            listener.submitCodeSuccesss(number, dateFormat.format(new Date()));
                        } else if (i1 == SMSSDK.RESULT_ERROR) {//提交验证码失败
                            listener.submitCodeFailure();
                        }
                    }
                }
            };
            SMSSDK.registerEventHandler(eh);
            SMSSDK.submitVerificationCode(country, number, code);
        }
    

    提交验证码多出一个你接收短信验证的信息code,其他都差不多的。
    这里给出mob业务逻辑,可以最大限度自定义出符合你自己要求的业务逻辑。

    Handler mhandle = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                int event = msg.arg1;
                int result = msg.arg2;
                Object data = msg.obj;
    
                if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {//验证码提交事件
                    if (result == SMSSDK.RESULT_COMPLETE) {
                        //回调成功
                        Toast.makeText(context, "提交验证码成功"+result, Toast.LENGTH_LONG).show();
                    } else if (result == SMSSDK.RESULT_ERROR) {
                        Toast.makeText(context, "提交验证码失败"+data, Toast.LENGTH_LONG).show();
                    }
    
                } else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE) {//获取短信验证码事件
    
                    //获取验证码成功
                    if (result == SMSSDK.RESULT_COMPLETE) {
                        Toast.makeText(context, "获取短信验证码成功", Toast.LENGTH_LONG).show();
                        boolean mobcheck = (Boolean) data;
                        if (mobcheck) {
                            //通过智能验证
                            Toast.makeText(context, "mob云验证", Toast.LENGTH_LONG).show();
                        } else {
                            //依然走短信验证
                            Toast.makeText(context, "短信验证", Toast.LENGTH_LONG).show();
                        }
                    } else if (result == SMSSDK.RESULT_ERROR) {
                        Toast.makeText(context, "获取短信验证码失败"+data, Toast.LENGTH_LONG).show();
                    }
                } else if (event == SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES) {
                    SMSSDK.getSupportedCountries();
                } else {
    
                    try {
                        ((Throwable) data).printStackTrace();
                        Throwable throwable = (Throwable) data;
                        JSONObject jsonObject = new JSONObject(throwable.getMessage());
                        String des = jsonObject.optString("detail");
                        int status = 0;
                        status = jsonObject.optInt("status");
                        if (TextUtils.isEmpty(des)) {
    
                        }
                    } catch (Exception e) {
                        SMSLog.getInstance().w(e);
                    }
                }
            }
        };
    

    Ps:SMSSDK已经做了混淆处理,再次混淆会导致不可预期的错误,请在您的混淆脚本中添加如下的配置,跳过对SMSSDK的混淆操作:

    -keep class com.mob.**{*;}
    -keep class cn.smssdk.**{*;}
    -dontwarn com.mob.**
    

    这个时候,大家可能就有疑问了,没有UI界面我如何去输入收到的短信验证码去验证,很简单,自己写个UI就行了啊,一般现在流行的界面可能就是如下图所示了,引用一下图片https://www.jianshu.com/p/91b0b8038dd5

    1879314-56d1f0d5b902bfee.png

    这位大牛的效果还是不错的,我说下自己的思路,简单的就是TextView+Edittext去组合实现的,下面,附上xml资源,大家可以详细了解一下

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorWhite">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center"
            android:orientation="horizontal">
    
            <TextView
                android:id="@+id/tv_code1"
                style="@style/codeTextView" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_code2"
                style="@style/codeTextView" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_code3"
                style="@style/codeTextView" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_code4"
                style="@style/codeTextView" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_code5"
                style="@style/codeTextView" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_code6"
                style="@style/codeTextView" />
    
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center"
            android:orientation="horizontal">
    
            <View
                android:id="@+id/view1"
                android:layout_width="40dp"
                android:layout_height="2dp"
                android:layout_gravity="bottom"
                android:background="@color/colorGray707061" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <View
                android:id="@+id/view2"
                android:layout_width="40dp"
                android:layout_height="2dp"
                android:layout_gravity="bottom"
                android:background="@color/colorGray707061" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <View
                android:id="@+id/view3"
                android:layout_width="40dp"
                android:layout_height="2dp"
                android:layout_gravity="bottom"
                android:background="@color/colorGray707061" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <View
                android:id="@+id/view4"
                android:layout_width="40dp"
                android:layout_height="2dp"
                android:layout_gravity="bottom"
                android:background="@color/colorGray707061" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <View
                android:id="@+id/view5"
                android:layout_width="40dp"
                android:layout_height="2dp"
                android:layout_gravity="bottom"
                android:background="@color/colorGray707061" />
    
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <View
                android:id="@+id/view6"
                android:layout_width="40dp"
                android:layout_height="2dp"
                android:layout_gravity="bottom"
                android:background="@color/colorGray707061" />
    
        </LinearLayout>
    
        <lf.com.android.blackfishdemo.view.CodeEditTextView
            android:id="@+id/et_code_text"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#00000000"
            android:inputType="number"
            android:longClickable="false"
            android:maxLength="6"
            android:textColor="#00000000" />
    
    </RelativeLayout>
    

    乍一看貌似很复杂,其实就是绘制了可以输入6位数的Textview和对应的下标Item,然后上面再被我自定义的EditText所覆盖

    
    /**
     * 验证码控件,去掉传统EditText双击选中EditText的内容
     * 和去掉光标位置会随点击改变
     */
    public class CodeEditTextView extends AppCompatEditText {
        private long lastTime = 0;
    
        public CodeEditTextView(Context context) {
            super(context);
        }
    
        public CodeEditTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public CodeEditTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onSelectionChanged(int selStart, int selEnd) {
            super.onSelectionChanged(selStart, selEnd);
            this.setSelection(this.getText().length());
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN://判断点击事件,防止用户多次点击
                    long currenTime = System.currentTimeMillis();
                    if (currenTime - lastTime < 500) {
                        lastTime = currenTime;
                        return true;
                    } else {
                        lastTime = currenTime;
                    }
                    break;
    
    
            }
            return super.onTouchEvent(event);
        }
    }
    

    自定义的Edittext首先会通过long currenTime = System.currentTimeMillis();获取到当前时间点,判断两个点击时间间隔,从而屏蔽双击事件,长按会走 onSelectionChanged这个方法,所以,设置光标始终在文本后面,也就屏蔽了长按事件
    然后通过对Edittext事件进行TextWatcher监听(后面会详细讲解),依次将输入内容分写到Textview上,就完成了上面的效果.至此,如何使用短信验证就告一段落.
    好了,同通过短信验证就意味着,用户登录过程已经接近尾声甚至已经结束了,但是,在这里,我还是想一起说了吧,因为短信验证不仅仅用于登录,也用于修改密码,而且,用户输入的内容我们要去判断,不可能用户随便输入一个文本我们就要对其进行一次短信验证吧,毕竟,喜欢找Bug的用户也不少,所以,我们要对用户输入的内容进行判断。这就是我么要说的5.对于用户输入信息的判断.在说这个之前,我先向大家介绍一下,什么是正则表达式
    正则表达式是一种查找以及字符串替换操作。正则表达式在文本编辑器中广泛使用,比如正则表达式被用于:

    • 检查文本中是否含有指定的特征词
    • 找出文中匹配特征词的位置
    • 从文本中提取信息,比如:字符串的子串修改文本与文本编辑器相似,几乎所有的高级编程语言都支持正则表达式。在这样的语境下,“文本”也就是一个字符串,可以执行的操作都是类似的。一些编程语言(比如Perl,JavaScript)会检查正则表达式的语法。
      正则表达式的语法是是一种轻量级、简洁、适用于特定领域的编程语言。
      关于详解,建议大家去看这篇文章https://www.jianshu.com/p/67af3eeb6798
      这里给大家常用正则表达式的链接https://www.cnblogs.com/zxin/archive/2013/01/26/2877765.html
      所以,当我们使用正则表达式时候,可利用事件监听
     private void setEditTextLitener() {
            mEditText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
                }
    
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
    
                }
    
                @Override
                public void afterTextChanged(Editable s) {
     
                }
            });
        }
    

    我们可以通过监听afterTextChanged获取到输入内容,从而进行判断,然后去编写我们所需要的逻辑,至此,用户登录过程就完美结束了,欢迎大家留言,提出问题.谢谢大家的阅读。

    相关文章

      网友评论

          本文标题:2018-09-09 关于用户登录的过程(用Fragment去实

          本文链接:https://www.haomeiwen.com/subject/wybdgftx.html