仿写一个登录页(Java代码层)|青训营笔记

简介: 本文接前文的 布局篇 ,会讲述java代码层面相关的问题

前置知识

Constraintlayout:最全面的ConstraintLayout教程 (qq.com)

ImageFilterViewConstraintLayout官方提供圆角ImageFilterView

Shape 标签Android-Shape标签使用 - 简书 (jianshu.com)

ImageFilterView圆角控件之ImageFilterView详解 - 简书 (jianshu.com)

MVP架构带你封装MVP架构(上)|青训营笔记

你会收获

  1. 如何编写一个美观的登录界面
  2. 如何提示用户查看 协议
  3. 如何载入跳转登录抖音,且回调回对应的页面

前言

本文接前文的 布局篇 ,会讲述java代码层面相关的问题

首先我们分析以下业务逻辑,其逻辑如下

  1. 进入登录页
  2. 勾选已阅读协议的按钮才可成功触发登录点击按钮的事件
  3. 成功触发登录事件后跳转去抖音sdk的授权页
  4. 抖音授权后带着是否成功的消息返回登录页
  5. 登录页对返回的消息做判断,授权成功则存储好Token跳转去个人主页,否则留在登录页

确定完对应逻辑后,开工!

编码流程

导入抖音SDK的依赖

要使用抖音的sdk,需要先去 抖音开放平台 (open-douyin.com) 进行申请。申请了之后创建对应的移动应用且审批通过后,即可获得对应的 key 和 secret 。

做完上面的一步后,我们便可导入依赖

//项目级build.gradle
repositories {
    maven { url 'https://artifact.bytedance.com/repository/AwemeOpenSDK' }
}
//app/build.gradle
dependencies {
  //抖音
    implementation 'com.bytedance.ies.ugc.aweme:opensdk-china-external:0.1.9.0'
    implementation 'com.bytedance.ies.ugc.aweme:opensdk-common:0.1.9.0'
}
复制代码
//AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xxx.xxxx">
  <queries>
    <!--允许查询抖音和抖音极速版的软件包信息-->
    <package android:name="com.ss.android.ugc.aweme" />
    <package android:name="com.ss.android.ugc.aweme.lite" />
  </queries>
</manifest>
复制代码

做完上面的,便完成了依赖的导入

Application层初始化

// 抖音授权
Context context = getApplicationContext();
String clientkey = context.getString(R.string.value_client_key);//对应的key需要就是申请得到的
DouYinOpenApiFactory.init(new DouYinOpenConfig(clientkey));
复制代码

V层编码(Activity层)

我们阅读文档会知道,回调的activity需要继承一个接口,需要重写的方法中就可以获得到授权的对应信息。话不多说,直接上代码。

public class LoginActivity extends BaseActivity<LoginPresenter, ActivityLoginBinding> implements ILoginView, IApiEventHandler {
    DouYinOpenApi douYinOpenApi;
    private long mExitTime = 0;
    private boolean is_clicked_user_agreement = false;
    /**
     * 初始化presenter,也是与Activity的绑定
     *
     * @return 返回new的Presenter层的值
     */
    @Override
    protected LoginPresenter createPresenter() {
        return new LoginPresenter(this);
    }
    /**
     * 载入view的一些操作
     */
    @Override
    protected void initView() {
        getBinding().btnLogin.setOnClickListener(v -> {
            if (is_clicked_user_agreement) {
                sendAuth();
            } else {
                showAnimator();
            }
        });
        getBinding().checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                is_clicked_user_agreement = b;
            }
        });
        getBinding().tvUserAgreement.setOnClickListener(view -> ToastUtil.showToast("待添加用户协议"));
    }
    /**
     * 载入数据操作
     */
    @Override
    protected void initData() {
        douYinOpenApi = DouYinOpenApiFactory.create(this);
        douYinOpenApi.handleIntent(getIntent(), this);
    }
    @Override
    public boolean sendAuth() {
        Authorization.Request request = new Authorization.Request();
        request.scope = "user_info,trial.whitelist";                          // 用户授权时必选权限
        // 用户授权时可选权限(默认选择)
        request.optionalScope1 = "fans.list," +
                "following.list," +
                "video.list," +
                "video.data";
//        request.optionalScope0 = mOptionalScope1;    // 用户授权时可选权限(默认不选)
        request.state = "ww";                                   // 用于保持请求和回调的状态,授权请求后原样带回给第三方。
        request.callerLocalEntry = "com.qxy.potato.module.mine.activity.LoginActivity";
        return douYinOpenApi.authorize(request);               // 优先使用抖音app进行授权,如果抖音app因版本或者其他原因无法授权,则使用wap页授权
    }
    private void showAnimator() {
        RxToast.info(MyUtil.getString(R.string.read_and_click), 500);
        ObjectAnimator animatorLeft = ObjectAnimator.ofFloat(getBinding().checkBox,
                "translationX", -20f, 0f, 20f, 0f);
        ObjectAnimator animatorRight = ObjectAnimator.ofFloat(getBinding().tvUserAgreement,
                "translationX", -20f, 0f, 20f, 0f);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setDuration(300);
        animatorSet.play(animatorLeft).with(animatorRight);
        animatorSet.start();
    }
    /**
     * 成功登录的操作
     */
    @Override
    public void loginSuccess() {
        ActivityUtil.startActivity(HomeActivity.class, true);
    }
    /**
     * 登录失败的操作
     *
     * @param msg
     */
    @Override
    public void loginFailed(String msg) {
        ToastUtil.showToast(msg);
    }
    /**
     * Called when the activity has detected the user's press of the back
     * key. The {@link #getOnBackPressedDispatcher() OnBackPressedDispatcher} will be given a
     * chance to handle the back button before the default behavior of
     * {@link Activity#onBackPressed()} is invoked.
     *
     * @see #getOnBackPressedDispatcher()
     */
    @Override
    public void onBackPressed() {
        int OVER_TIME = 2000;
        if ((System.currentTimeMillis() - mExitTime) > OVER_TIME) {
            ToastUtil.showToast(getResources().getString(R.string.double_quit) + getResources().getString(R.string.app_name));
            mExitTime = System.currentTimeMillis();
        } else {
            super.onBackPressed();
            ActivityUtil.closeAllActivity();
        }
    }
    @Override
    public void onReq(BaseReq baseReq) {
    }
    @Override
    public void onResp(BaseResp baseResp) {
        if (baseResp.getType() == CommonConstants.ModeType.SEND_AUTH_RESPONSE) {
            Authorization.Response response = (Authorization.Response) baseResp;
            if (baseResp.isSuccess()) {
                LogUtil.d("onRES");
                presenter.getAccessToken(response.authCode);
            } else {
                ToastUtil.showToast("授权失败");
            }
        }
    }
    @Override
    public void onErrorIntent(Intent intent) {
    }
}
复制代码

下面我来浅析以下V层的代码

  1. 首先,我们定义三个全局变量 ,其分别是 抖音登录的相关类的变量、记录返回键点击事件的 int 变量、记录是否点击了阅读协议的勾选
  2. 然后,initView 的类中,分别设置了 登录按钮、勾选框以及用户协议的相关点击监听。
  3. 点击登录按钮后会判断是否勾选了选择框,未勾选的话就会进行动画提示,勾选了的话就会跳转到对应的登录跳转的方法
  4. 登录跳转的方法中,callerLocalEntry 是设置了回调的路径,设置的是登录页自己,这样子跳转去授权后又会跳转会当前页面了
  5. 动画提示的方法中,使用的是属性动画里面的组合动画,可以让提示行的文字左右震动
  6. 接下来是接口类重写的方法,事实上我们只需要重写 onResp(BaseResp baseResp) 即可。对回调的值进行获取和判断,成功就传入 authCodepresenter 层的获取 Token
  7. 在  onBackPressed() 中,我们设定滑动两次才退出APP,因为此时的登录页已经是 APP 唯一的一个页面了

P层编码(Presenter层)

public class LoginPresenter extends BasePresenter<ILoginView> {
    MMKV mmkv = MMKV.defaultMMKV();
    public LoginPresenter(ILoginView baseView) {
        super(baseView);
    }
    /**
     * 获取到 AccessToken 并存储
     *
     * @param authCode 授权码
     */
    public void getAccessToken(String authCode) {
        HashMap<String, String> map = new HashMap<>();
        map.put(MyUtil.getString(R.string.client_secret), MyUtil.getString(R.string.value_client_secret));
        map.put(MyUtil.getString(R.string.code), authCode);
        map.put(MyUtil.getString(R.string.grant_type), MyUtil.getString(R.string.authorization_code));
        map.put(MyUtil.getString(R.string.client_key), MyUtil.getString(R.string.value_client_key));
        addDisposable(apiServer.PostAccessToken(map),
                new BaseObserver<BaseBean<AccessToken>>(baseView, true) {
                    @Override
                    public void onError(String msg) {
                        baseView.loginFailed(msg);
                    }
                    @Override
                    public void onSuccess(BaseBean<AccessToken> o) {
                        mmkv.encode(GlobalConstant.ACCESS_TOKEN, o.data.getAccess_token());
                        mmkv.encode(GlobalConstant.REFRESH_TOKEN, o.data.getRefresh_token());
                        mmkv.encode(GlobalConstant.OPEN_ID, o.data.getOpen_id());
                        mmkv.encode(GlobalConstant.IS_LOGIN, true);
                        LogUtil.d(o.data.getAccess_token());
                        baseView.loginSuccess();
                    }
                });
    }
}
复制代码

关于P层的代码中,发起了网络请求后,若请求成功,则存储到 Token 值以及 open_id 值,然后执行V层的 loginSuccess() ;否则执行 loginFailed(msg)

效果展示

做完上边的工作就大功告成了,下边看一下展示效果

1.webp.jpg


相关文章
|
6天前
|
搜索推荐 Java 索引
|
6天前
|
搜索推荐 Java 索引
|
6天前
|
搜索推荐 Java 索引
|
6天前
|
搜索推荐 Java 索引
|
2天前
|
Java 开发者
Java中的Lambda表达式:简化你的代码之旅
【8月更文挑战第17天】 在编程的海洋中,简洁是航行的风帆。Lambda表达式,作为Java 8的一大亮点,为开发者提供了一种更为紧凑、易读的编码方式。本篇文章将带你领略Lambda表达式的魅力,从基础概念到实际应用,让你的代码像诗句一样流畅。
12 4
|
1天前
|
Java 开发者
在Java编程的广阔天地中,if-else与switch语句犹如两位老练的舵手,引领着代码的流向,决定着程序的走向。
在Java编程中,if-else与switch语句是条件判断的两大利器。本文通过丰富的示例,深入浅出地解析两者的特点与应用场景。if-else适用于逻辑复杂的判断,而switch则在处理固定选项或多分支选择时更为高效。从逻辑复杂度、可读性到性能考量,我们将帮助你掌握何时选用哪种语句,让你在编程时更加得心应手。无论面对何种挑战,都能找到最适合的解决方案。
5 1
|
6天前
|
搜索推荐 Java
|
6天前
|
搜索推荐 Java
|
3天前
|
存储 算法 Java
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
这篇文章详细介绍了在Java项目中如何使用MD5算法对用户密码进行加密存储和登录验证,包括加入依赖、编写MD5工具类、注册时的密码加密和登录时的密码验证等步骤,并通过示例代码和数据库存储信息展示了测试效果。
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。