周记 | 2019.1.14-2019.1.18

前言:
  这周主要还是以学习、看代码、看书为主,就着《Android开发艺术探索》和《Effective Java》下饭,学习了一波关于注解的知识 ,以及主要使用注解加快开发速度的ButterKnife,另外写了几个简单的Demo。也算是回顾了之前所落下的安卓知识了,算是快速充电的一周,另外LeetCode也在刷刷刷。
以下是本周的主要学习记录点:

  • ButterKnife的使用
  • Android Context 的理解
  • Android手机验证码登录的实现
  • Android关于使用相机的点点滴滴
  • Android第三方登录的实现(未实现!要钱!)
  • SharedPreference的应用(登录维持)
  • 刷Leetcode
  • RadioGroup+Fragment实现带底部导航栏的界面切换(回顾:之前项目使用的是BottomBar实现)
  • RxJava从放弃到开始

坑有点多我们慢慢的一个个填

  • ButterKnife的使用

 ButterKnife是一个Android系统的View注入框架,以前总是要写很多findViewById来找到View对象,有了ButterKnife可以很轻松的省去这些步骤。。最重要的一点,使用ButterKnife对性能基本没有损失,因为ButterKnife用到的注解并不是在运行时反射的,而是在编译的时候生成新的class。项目集成起来也是特别方便,使用起来也是特别简单。
 ButterKnife项目地址:https://github.com/JakeWharton/butterknife

▲ ButterKnife的优势:

1、非常强大的View绑定和OnClick绑定,让你的代码瞬间整洁好多好多,再也看不见那么多的btn_function = (Button)findViewById(R.id.btn_func)了!简化代码也算是解放程序员了嘛,还提高了开发效率,何乐而不为呢。

改造后:

    // View绑定
    @BindView(R.id.pi_image)
    public CircleImageView mHeader_iv;
    @BindView(R.id.btn_personal_info_modify_image)
    public Button btn_personal_info_modify_image;
    @BindView(R.id.pi_name)
    public LinearLayout piLl_name;
    @BindView(R.id.pi_league_num)
    public LinearLayout piLl_league_num;
    @BindView(R.id.pi_gender)
    public LinearLayout piLl_gender;
...
    //OnClick绑定
    @OnClick(R.id.pi_name)
    public void pill_name_OnClick(){
        Toast.makeText(this,"点击",Toast.LENGTH_SHORT).show();
        startActivity(new Intent(personalInfo_Activity.this,pi_modifyName_Activity.class));
        Log.i(LOGTAG, "点击");
    }

2、方便的处理Adapter里的ViewHolder绑定问题

3、运行时不会影响APP效率,使用配置方便

4、代码清晰,可读性强

▲ 配置方式
在build.gradle中配置如下:

dependencies {
   ...
      implementation 'com.jakewharton:butterknife:8.8.1'
      annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}

其实最新的butterknife版本已经更新到

implementation 'com.jakewharton:butterknife:10.0.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'

但是呢,当我使用10.0.0的时候,发现了AndroidSupport与Androidx的冲突,报了如下错误:

anifest merger failed : Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
    is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(androidx.core.app.CoreComponentFactory).
    Suggestion: add 'tools:replace="android:appComponentFactory"' to  element at AndroidManifest.xml:36:5-364:19 to override.

按照Androidx和Android support库共存问题解决文章中的两种解决方式,第一是将build tools更新到3.2.0,gradle更新到4.6,依赖库统一更新到28.0.0,再把项目迁移至Androidx,具体操作看前提链接。第二是回退butterKnife版本,我将版本回退至8.8.1后使用正常。

▲使用体会:

1、在Activity 类中绑定 :ButterKnife.bind(this);必须在setContentView();之后绑定;且父类bind绑定后,子类不需要再bind。
2、在非Activity 类(eg:Fragment、ViewHold)中绑定: ButterKnife.bind(this,view);这里的this不能替换成getActivity()。
3、在Activity中不需要做解绑操作,在Fragment 中必须在onDestroyView()中做解绑操作。
4、使用ButterKnife修饰的方法和控件,不能用private or static 修饰,否则会报错。错误: @BindView fields must not be private or static.

▲主要使用:

@BindView—->绑定一个view;id为一个view 变量
@BindViews —-> 绑定多个view;id为一个view的list变量
@BindArray—-> 绑定string里面array数组;@BindArray(R.array.city ) String[] citys ;
@BindBitmap—->绑定图片资源为Bitmap;@BindBitmap( R.mipmap.wifi ) Bitmap bitmap;
@BindBool —->绑定boolean值
@BindColor —->绑定color;@BindColor(R.color.colorAccent) int black;
@BindDimen —->绑定Dimen;@BindDimen(R.dimen.borth_width) int mBorderWidth;
@BindDrawable —-> 绑定Drawable;@BindDrawable(R.drawable.test_pic) Drawable mTestPic;
@BindFloat —->绑定float
@BindInt —->绑定int
@BindString —->绑定一个String id为一个String变量;@BindString( R.string.app_name ) String meg;
@OnClick—->点击事件
@OnCheckedChanged —->选中,取消选中
@OnEditorAction —->软键盘的功能键
@OnFocusChange —->焦点改变
@OnItemClick item—->被点击(注意这里有坑,如果item里面有Button等这些有点击的控件事件的,需要设置这些控件属性focusable为false)
@OnItemLongClick item—->长按(返回真可以拦截onItemClick)
@OnItemSelected —->item被选择事件
@OnLongClick —->长按事件
@OnPageChange —->页面改变事件
@OnTextChanged —->EditText里面的文本变化事件
@OnTouch —->触摸事件
@Optional —->选择性注入,如果当前对象不存在,就会抛出一个异常,为了压制这个异常,可以在变量或者方法上加入一下注解,让注入变成选择性的,如果目标View存在,则注入, 不存在,则什么事情都不做

  • Android Context的理解
    对于Context的理解主要是浏览了Context都没弄明白,还怎么做Android开发?这篇文章,对Android的Context有了一些理解。

 我们一般将Context理解为“上下文”或者“场景”,如果你仍然觉得很抽象,不好理解。在这里我给出一个可能不是很恰当的比喻,希望有助于大家的理解:一个Android应用程序,可以理解为一部电影或者一部电视剧,Activity,Service,Broadcast Receiver,Content Provider这四大组件就好比是这部戏里的四个主角:胡歌,霍建华,诗诗,Baby。他们是由剧组(系统)一开始就定好了的,整部戏就是由这四位主演领衔担纲的,所以这四位主角并不是大街上随随便便拉个人(new 一个对象)都能演的。有了演员当然也得有摄像机拍摄啊,他们必须通过镜头(Context)才能将戏传递给观众,这也就正对应说四大组件(四位主角)必须工作在Context环境下(摄像机镜头)。那Button,TextView,LinearLayout这些控件呢,就好比是这部戏里的配角或者说群众演员,他们显然没有这么重用,随便一个路人甲路人乙都能演(可以new一个对象),但是他们也必须要面对镜头(工作在Context环境下),所以Button mButton=new Button(Context)是可以的。
 在应用程序中Context的具体实现子类就是:Activity,Service,Application。那么Context数量=Activity数量+Service数量+1。

周记 | 2019.1.14-2019.1.18_第1张图片
Context作用域

正确使用Context
 一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出-使用Context的正确姿势:
1.当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2.不要让生命周期长于Activity的对象持有到Activity的引用。
3.尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

  • Android手机验证码登录的实现

login_Activity.java

public class login_Activity extends AppCompatActivity  {

    @BindView(R.id.tv_contract)
    public TextView tv_contract;
    @BindView(R.id.ev_phone_Number)
    public EditText ev_phone_Number;
    @BindView(R.id.ev_validator)
    public EditText ev_validator;
    @BindView(R.id.btn_login_confirm)
    public Button btn_login_confirm;
    @BindView(R.id.btn_get_validater)
    public Button btn_get_validater;

    private TimeCount time_count;
    private EventHandler eh; //事件接收器
    private SpUtil sp;

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

        ButterKnife.bind(this);

        time_count = new TimeCount(60000, 1000); //设置验证码定时器
        sp = new SpUtil(this);
        tv_contractClick(); //点击服务协议和隐私政策的响应
        Handler_Validator_Event(); //处理点击确定按钮后的验证码回调工作
    }

    /**
     * 对同意协议和隐私政策进行特殊处理,使其可点击并处理相应逻辑
     */
    private void tv_contractClick() {
        final SpannableStringBuilder style = new SpannableStringBuilder();
        style.append("在点击“继续”时,我已同意服务协议和隐私政策");

        ClickableSpan clickableSpan = new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(login_Activity.this, "触发点击事件:服务协议!", Toast.LENGTH_SHORT).show();
            }
        };

        ClickableSpan clickableSpan2 = new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(login_Activity.this, "触发点击事件:隐私政策!", Toast.LENGTH_SHORT).show();
            }
        };

        style.setSpan(clickableSpan, 13, 17, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        style.setSpan(clickableSpan2,18,22,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv_contract.setText(style);

        //设置部分文字颜色
        ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.parseColor("#40a9ff"));
        ForegroundColorSpan foregroundColorSpan2 = new ForegroundColorSpan(Color.parseColor("#40a9ff"));
        //设置需要特殊处理的文字
        style.setSpan(foregroundColorSpan, 13, 17, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        style.setSpan(foregroundColorSpan2, 18, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        //配置给TextView
        tv_contract.setMovementMethod(LinkMovementMethod.getInstance());
        tv_contract.setText(style);
    }

    private void bindview() {

        time_count = new TimeCount(60000, 1000);
    }
    @OnClick(R.id.btn_login_confirm)
    public void btn_login_confirm_OnClick(){
      if (!ev_phone_Number.getText().toString().trim().equals("")) {
        if (Textutil.checkTel(ev_phone_Number.getText().toString().trim())) {
            if (!ev_validator.getText().toString().trim().equals("")) {
                SMSSDK.submitVerificationCode("+86",ev_phone_Number.getText().toString().trim(),ev_validator.getText().toString().trim());//提交验证
                Log.e("SMSSDK",ev_validator.getText().toString().trim());
            }else{
                Toast.makeText(login_Activity.this, "请输入验证码", Toast.LENGTH_SHORT).show();
            }
        }else{
            Toast.makeText(login_Activity.this, "请输入正确的手机号码", Toast.LENGTH_SHORT).show();
        }
    }else{
        Toast.makeText(login_Activity.this, "请输入手机号码", Toast.LENGTH_SHORT).show();
    }
}

    @OnClick(R.id.btn_get_validater)
    public void btn_get_validater_OnClick(){
        //SMSSDK.getSupportedCountries();//获取短信目前支持的国家列表
        if(!ev_phone_Number.getText().toString().trim().equals("")){
            if (Textutil.checkTel(ev_phone_Number.getText().toString().trim())) {
                SMSSDK.getVerificationCode("+86",ev_phone_Number.getText().toString());//获取验证码
                time_count.start();
            }else{
                Toast.makeText(login_Activity.this, "请输入正确的手机号码", Toast.LENGTH_SHORT).show();
            }
        }else{
            Toast.makeText(login_Activity.this, "请输入手机号码", Toast.LENGTH_SHORT).show();
        }
    }


    /**
     * 倒数计数器内部类
     */
    class TimeCount extends CountDownTimer {

        public TimeCount(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }
        /**
         * 每一次ontick都会更新UI
         * @param l
         */
        @Override
        public void onTick(long l) {
            btn_get_validater.setClickable(false);
            btn_get_validater.setText(l/1000 + "秒后重新获取");
        }

        /**
         * 当倒数计时器完成时回到获取验证码
         */
        @Override
        public void onFinish() {
            btn_get_validater.setClickable(true);
            btn_get_validater.setText("获取验证码");
        }
    }

    /**
     * 初始化事件接收器
     */
    private void Handler_Validator_Event(){
        eh = new EventHandler(){
            @Override
            public void afterEvent(int event, int result, Object data) {

                if (result == SMSSDK.RESULT_COMPLETE) { //回调完成

                    if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) { //提交验证码成功
                        Log.e("SMSSDK","verifyOK");
                        /**
                         * 这里应该是一个okhttp3请求,和服务器交互,并将用户名和头像信息写入sharePreference进行持久化
                         */
//                        String url = "http://这里写的是接口地址(具体接收格式要看后台怎么给)";
//                        Map params = new HashMap();
//                        params.put("number",ev_phone_Number.getText().toString().trim() );
//                        OkHttpUtil.postAsync(url, params, new OkHttpUtil.DataCallBack() {
//                            @Override
//                            public void requestFailure(Request request, IOException e) {
//                                Log.i("请求失败", "请求失败" + request.toString() + e.toString());
//                            }
//                            @Override
//                            public void requestSuccess(String result) throws Exception {
//                                Toast.makeText(login_Activity.this,"请求成功",Toast.LENGTH_SHORT).show();
//                                Log.i("请求成功", result);
//                            }
//                        });
                    } else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE){ //获取验证码成功


                    } else if (event ==SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES){ //返回支持发送验证码的国家列表

                    }
                } else{
                    sp.saveString("username",ev_phone_Number.getText().toString().trim());
                    sp.saveString("username","机器人");
                    Log.e("SMSSDK","verifyERROR");
                    Looper.prepare();
                    Toast.makeText(login_Activity.this, "验证码输入错误,请重试", Toast.LENGTH_SHORT).show();
                    startActivity(new Intent(login_Activity.this, main_Activity.class)); //页面跳转
                    Looper.loop();
                    ((Throwable)data).printStackTrace();
                }
            }
        };
        SMSSDK.registerEventHandler(eh); //注册短信回调
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        SMSSDK.unregisterEventHandler(eh); //取消短信回调,销毁
    }
}

login_activity.xml


    

    
        
    
        
        

工具类-正则匹配手机号

public class Textutil {
    /**
     * 正则匹配手机号码
     * @param tel
     * @return
     */
    public static boolean checkTel(String tel){
        Pattern p = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$");
        Matcher matcher = p.matcher(tel);
        return matcher.matches();
    }

}

要注意哦,未成为开发者之前每台手机每天只能接受10封短信。主要是在AfterEvent方法的重写去实现验证码验证后回调的逻辑。

你可能感兴趣的:(周记 | 2019.1.14-2019.1.18)