preface

This article is a further encapsulation of the MVP+Retrofit+RxJava2 framework (I) that you can encapsulate yourself with. It is recommended that you eat it after reading the previous article!

This article has been included on my Github blog. Welcome to my humble abode: my Github blog

This article requires existing knowledge:

  • MVPThe concept and basic use of
  • RetrofitBasic use of the framework
  • RxJava2Basic use of the framework
  • ButterKnifeBasic use of the framework
  • BaseThe concept of base classes
  • Simple use of YUtils
  • BaseRecyclerViewAdapterHelper simple to use

Study list:

  • BaseEncapsulation of entity classes
  • BaseEncapsulation of exception classes
  • BaseObserver encapsulation
  • RxJavaThread automatic scheduling tips
  • Perform network request auto display loading
  • Complete network request auto shutdown loading
  • Automatically handle exception information
  • CookieAutomatic persistence andRetrofitSynergistic use of
  • Interface managementRetrofitA graceful way to request an interface

Why encapsulate the framework

As mentioned in the previous article, when the MVP model is becoming more and more popular, packaging an MVP framework is not only convenient for daily development, but also can accumulate skills in the future work in advance. Moreover, good packaging and standard use can also reduce the development of various troublesome bugs.

Some might ask, “Didn’t you write about the MVP framework in your last article? Is this one the same? Are you doing the same thing?”

In fact, an article on the beginning I thought I already have good encapsulation MVP framework, but, in one day to see the author yechaoa great god play android Java source code, impressed by its encapsulation MVP framework, so the first time to write this article, want to share, I learn from experience, hope to be able to help you!

Here are some improvements to encapsulating your MVP+Retrofit+RxJava2 framework (I) :

  • Simplified the Activity base classAnd the original twoBaseActivityandBaseMvpActivityReduced to oneBaseActivity
  • Repair theWhen inheritedActivityBase class, not addedPresenterCan lead toNull pointertheBug
  • Added network request can be selected to automatically display loading and automatically close the loading function
  • Added the function of automatically handling exception information
  • Encapsulate aBeanThe object’sThe base class
  • To streamline the RxJavaSo it can be omittedModelThe writing of the class
  • encapsulationaObserverThe base class
  • Added automatic cookie persistence
  • To improve thetheRetrofitServiceThe encapsulation willRetrofitThe instantiation of an interface introduces a base class

Core usage and sample analysis

This project is built based on Android X, and the complete code has been uploaded to my Github repository

First, I would like to introduce the basic structure of the author’s project

In order to simulate the function of automatic Cookie acquisition, the author designed a Demo with login, registration and collection functions

Here’s a special thanks for playing android’s API

The framework I used in the Demo is as follows

	implementation fileTree(dir: 'libs'.include: ['*.jar'])
    implementation 'androidx. Appcompat: appcompat: 1.1.0'
    implementation 'androidx. Legacy: legacy support - v4:1.0.0'
    implementation 'androidx. Constraintlayout: constraintlayout: 1.1.3'
    testImplementation 'junit: junit: 4.12'
    androidTestImplementation 'androidx. Test. Ext: junit: 1.1.1'
    androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.2.0'
    implementation 'com. Google. Android. Material: material: 1.1.0'

    //cardView
    implementation 'androidx. Cardview: cardview: 1.0.0'

    / * retrofit, rxjava * /
    implementation 'com. Squareup. Retrofit2: retrofit: 2.6.2'
    implementation 'com. Squareup. Retrofit2: adapter - rxjava2:2.4.0'
    implementation 'com. Squareup. Retrofit2: converter - gson: 2.4.0'
    implementation 'the IO. Reactivex. Rxjava2: rxandroid: 2.0.2'
    implementation 'com. Squareup. Okhttp3: logging - interceptor: 3.4.1 track'
	implementation 'com. Squareup. Okhttp3: logging - interceptor: 3.4.1 track'

    /*glide*/
    implementation 'com. Making. Bumptech. Glide: glide: 4.10.0'
    annotationProcessor 'com. Making. Bumptech. Glide: the compiler: 4.10.0'

    /*butterknife*/
    implementation 'com. Jakewharton: butterknife: 10.2.0'
    annotationProcessor 'com. Jakewharton: butterknife - compiler: 10.2.0'

    /*YUtils*/
    implementation 'com. Making. Yechaoa: YUtils: 2.1.0'

    /*BRVAH*/
    implementation 'com. Making. CymChad: BaseRecyclerViewAdapterHelper: 2.9.50'

    /*banner*/
    implementation 'com. Youth. The banner, the banner: 1.4.10'
Copy the code

Below, I will introduce the relevant information of each class in detail

2.1 BaseThe base class

2.1.1 BaseActivity

BaseActivity improvements over my previous MVP framework:

  • Combine the two base classesActivityMerge into oneBaseActivity
  • It encapsulates the method of showing and hiding the progress bar

/**
 * Description : BaseActivity
 *
 * @author XuCanyou666
 * @date2020/2/7 * /

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView {

    protected P presenter;

    protected abstract P createPresenter(a);

    protected abstract int getLayoutId(a);

    protected abstract void initView(a);

    protected abstract void initData(a);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Set portrait
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        setContentView(LayoutInflater.from(this).inflate(getLayoutId(), null));
        ButterKnife.bind(this);
        presenter = createPresenter();
        initView();
        initData();
    }

    @Override
    protected void onResume(a) {
        super.onResume();
        initListener();
    }

    @Override
    protected void onDestroy(a) {
        super.onDestroy();
        // When destroyed, unbind
        if(presenter ! =null) { presenter.detachView(); }}protected void initListener(a) {}@Override
    public void showLoading(a) {
        YUtils.showLoading(this."Loading");
    }

    @Override
    public void hideLoading(a) {
        YUtils.dismissLoading();
    }

    /** * can handle exceptions */
    @Override
    public void onErrorCode(BaseBean bean) {}/** * Starts the activity **@paramActivity Current activity *@paramIsFinish Whether to end the current activity */
    public void startActivity(Class<? > activity,boolean isFinish) {
        Intent intent = new Intent(this, activity);
        startActivity(intent);
        if(isFinish) { finish(); }}}Copy the code

2.1.2 BaseFragment


/**
 * Description : BaseFragment
 *
 * @author XuCanyou666
 * @date2020/2/7 * /


public abstract class BaseFragment<P extends BasePresenter> extends Fragment implements BaseView {

    private Unbinder unbinder;
    protected Context mContext;

    protected P presenter;

    protected abstract P createPresenter(a);

    protected abstract int getLayoutId(a);

    protected abstract void initView(a);

    protected abstract void initData(a);

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(getLayoutId(), container, false);
        unbinder = ButterKnife.bind(this, view);
        // Get the context, which can be called directly in subclasses Fragment
        mContext = ActivityUtil.getCurrentActivity();
        presenter = createPresenter();
        initView();
        initData();
        return view;
    }

    @Override
    public void onResume(a) {
        super.onResume();
        initListener();
    }

    @Override
    public void onDestroyView(a) {
        super.onDestroyView();
        //do something
        unbinder.unbind();
        // When destroyed, unbind
        if(presenter ! =null) { presenter.detachView(); }}private void initListener(a) {}@Override
    public void onErrorCode(BaseBean bean) {}/**
     * 显示加载中
     */
    @Override
    public void showLoading(a) {
        YUtils.showLoading(ActivityUtil.getCurrentActivity(), "Loading");
    }

    /** ** hidden load */
    @Override
    public void hideLoading(a) { YUtils.dismissLoading(); }}Copy the code

2.1.3 BasePresenter

BasePresenter is an improvement over my previous MVP framework:

  • The thread’s schedule is writtenaddDisposableIn the
  • Rewrite theaddDisposableMethod to make the invocation more simple and elegant


/**
 * Description : BasePresenter
 *
 * @author XuCanyou666
 * @date2020/2/7 * /


public class BasePresenter<V extends BaseView> {

    private CompositeDisposable compositeDisposable;
    public V baseView;

    /** * Example: apiserver.login (username, password); * /
    protected API.WAZApi apiServer = RetrofitService.getInstance().getApiService();

    public BasePresenter(V baseView) {
        this.baseView = baseView;
    }

    /** * unbind */
    public void detachView(a) {
        baseView = null;
        removeDisposable();
    }

    /** * return view */
    public V getBaseView(a) {
        return baseView;
    }

    public void addDisposable(Observable
        observable, BaseObserver observer) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable
                .add(observable.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribeWith(observer));
    }

    private void removeDisposable(a) {
        if(compositeDisposable ! =null) { compositeDisposable.dispose(); }}}Copy the code

2.1.4 BaseObserver

  • Observer base class that provides methods for automatically displaying and hiding progress bars

  • Internally we handle onStart, onError, and onComplete methods

  • Only onSuccess and onError methods are provided externally, which conforms to common user habits


/**
 * Description : BaseObserver
 *
 * @author XuCanyou666
 * @date2020/2/7 * /


public abstract class BaseObserver<T> extends DisposableObserver<T> {

    protected BaseView view;
    private boolean isShowDialog;

    protected BaseObserver(BaseView view) {
        this.view = view;
    }

    /** * Initialization method with progress bar **@param view         view
     * @paramIsShowDialog displays a progress bar */
    protected BaseObserver(BaseView view, boolean isShowDialog) {
        this.view = view;
        this.isShowDialog = isShowDialog;
    }

    @Override
    protected void onStart(a) {
        if(view ! =null&& isShowDialog) { view.showLoading(); }}@Override
    public void onNext(T o) {
        onSuccess(o);
    }

    @Override
    public void onError(Throwable e) {
        if(view ! =null && isShowDialog) {
            view.hideLoading();
        }
        BaseException be;

        if(e ! =null) {
            // Custom exception
            if (e instanceof BaseException) {
                be = (BaseException) e;
                // Callback to the View layer or depending on the project
                if(view ! =null) {
                    // Handle login failure updates
                    view.onErrorCode(new BaseBean(be.getErrorCode(), be.getErrorMsg()));
                } else {
                    onError(be.getErrorMsg());
                }
                // The system is abnormal
            } else {
                if (e instanceof HttpException) {
                    / / HTTP error
                    be = new BaseException(BaseException.BAD_NETWORK_MSG, e);
                } else if (e instanceof ConnectException || e instanceof UnknownHostException) {
                    // Connection error
                    be = new BaseException(BaseException.CONNECT_ERROR_MSG, e);
                } else if (e instanceof InterruptedIOException) {
                    // Connection timed out
                    be = new BaseException(BaseException.CONNECT_TIMEOUT_MSG, e);
                } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
                    // Parsing error
                    be = new BaseException(BaseException.PARSE_ERROR_MSG, e);
                } else {
                    be = newBaseException(BaseException.OTHER_MSG, e); }}}else {
            be = new BaseException(BaseException.OTHER_MSG);
        }
        onError(be.getErrorMsg());
    }

    @Override
    public void onComplete(a) {
        if(view ! =null&& isShowDialog) { view.hideLoading(); }}public abstract void onSuccess(T o);

    public abstract void onError(String msg);

}

Copy the code

2.1.5 BaseException

The base class for the exception


/**
 * Description : BaseException
 *
 * @author XuCanyou666
 * @date2020/2/7 * /


public class BaseException extends IOException {

    /** * Failed to parse data */
    public static final String PARSE_ERROR_MSG = "Failed to parse data";
    /** * Network problem */
    public static final String BAD_NETWORK_MSG = "Network problems";
    /** * connection error */
    public static final String CONNECT_ERROR_MSG = "Connection error";
    /** * Connection timed out */
    public static final String CONNECT_TIMEOUT_MSG = "Connection timed out";
    /** * Unknown error */
    public static final String OTHER_MSG = "Unknown error";

    private String errorMsg;
    private int errorCode;

    public String getErrorMsg(a) {
        return errorMsg;
    }

    public int getErrorCode(a) {
        return errorCode;
    }

    public BaseException(String message) {
        this.errorMsg = message;
    }

    public BaseException(String errorMsg, Throwable cause) {
        super(errorMsg, cause);
        this.errorMsg = errorMsg;
    }

    public BaseException(int errorCode, String message) {
        this.errorMsg = message;
        this.errorCode = errorCode; }}Copy the code

2.1.6 BaseBean

The base class of the entity class to handle the returned Json data, depending on each API


/** * Description: BaseBean entity class base class **@author XuCanyou666
 * @date2020/2/7 * /

public class BaseBean<T> implements Serializable {


    /** * data : * errorCode : 0 * errorMsg : */

    public int errorCode;
    public String errorMsg;
    public T data;

    public BaseBean(int code, String data) {
        this.errorCode = code;
        this.data = (T) data; }}Copy the code

2.1.7 BaseView


/**
 * Description : BaseView
 *
 * @author XuCanyou666
 * @date2020/2/7 * /


public interface BaseView {

    void showLoading(a);

    void hideLoading(a);

    void onErrorCode(BaseBean bean);

}
Copy the code

2.2 http

2.2.1 cookie

Persistent cookie, because there are too many codes, here only show the code of a class, the detailed code please go to my Github to check

package com.users.xucanyou666.rxjava2_retrofit_mvp2.http.cookie;

import android.content.Context;

import java.util.List;

import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;

/** * Created by yechao on 2019/11/19/019. * Describe : */
public class CookiesManager implements CookieJar {

    private final PersistentCookieStore cookieStore;

    public CookiesManager(Context context) {
        cookieStore = new PersistentCookieStore(context);
    }

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        if (cookies.size() > 0) {
            for(Cookie item : cookies) { cookieStore.add(url, item); }}}@Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        returncookieStore.get(url); }}Copy the code

2.2.2 gson

Rewrite ResponseBodyConverter for Json preprocessing, here only show a class of code, detailed code please go to my Github



/** * Created by yechao on 2019/11/18/018. * Describe: Write ResponseBodyConverter preprocessing */
public class BaseResponseBodyConverter<T> implements Converter<ResponseBody.T> {
    private final TypeAdapter<T> adapter;

    /** * Login invalid */
    private static final int LOG_OUT_TIME = -1001;

    BaseResponseBodyConverter( TypeAdapter<T> adapter) {
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        String jsonString = value.string();
        try {
            JSONObject object = new JSONObject(jsonString);
            int code = object.getInt("errorCode");
            if (0! = code) { String data;// Error message
                if (code == LOG_OUT_TIME) {
                    data = "Login is invalid. Please log in again.";
                } else {
                    data = object.getString("errorMsg");
                }
                // Exception handling
                throw new BaseException(code, data);
            }
            // Return the entire JSON correctly
            return adapter.fromJson(jsonString);

        } catch (JSONException e) {
            e.printStackTrace();
            // Data parsing exception meaning json format changed
            throw new BaseException(BaseException.PARSE_ERROR_MSG);
        } finally{ value.close(); }}}Copy the code

2.2.3 API

  • Reason: As projects get bigger and more requests come in, it’s impossible to use an interface for every request, which would be wasteful and unmanageable
  • Effect: Create a new oneAPIAs aRetrofitManage all network requests with a single interface, which can significantly improve code quality

/** * Description: API * interface management class **@author XuCanyou666
 * @date2020/2/7 * /


public class API {

    static final String BASE_URL = "https://www.wanandroid.com/";

    public interface WAZApi {

        / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- page related 】 【 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --


        {} is the number of pages filled in
        @GET("article/list/{page}/json")
        Observable<BaseBean<Article>> getArticleList(@Path("page") Integer page);


        / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- login register 】 【 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

        / / login
        @FormUrlEncoded
        @POST("user/login")
        Observable<BaseBean<User>> login(@Field("username") String username, @Field("password") String password);

        / / register
        @FormUrlEncoded
        @POST("user/register")
        Observable<BaseBean<User>> register(@Field("username") String username, @Field("password") String password, @Field("repassword") String repassword);


        / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "collection" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

        // Collect articles from the website
        @POST("lg/collect/{id}/json")
        Observable<BaseBean> collectIn(@Path("id") Integer id);

        // Unfavorites -- list of articles
        @POST("lg/uncollect_originId/{id}/json")
        Observable<BaseBean> uncollect(@Path("id") Integer id); }}Copy the code

2.2.4 RetrofitService

Retrofit’s configuration classes, which initialize apiServer objects and configure logging, timeout, Cookie persistence, use the singleton pattern of static inner classes


/**
 * Description : RetrofitService
 *
 * @author XuCanyou666
 * @date2020/2/8 * /


public class RetrofitService {

    private volatile static RetrofitService apiRetrofit;
    private API.WAZApi apiServer;

    /** * singleton calls **@return RetrofitService
     */
    public static RetrofitService getInstance(a) {
        if (apiRetrofit == null) {
            synchronized (Object.class) {
                if (apiRetrofit == null) {
                    apiRetrofit = newRetrofitService(); }}}return apiRetrofit;
    }


    /** * get the API object **@return* / API objects
    public API.WAZApi getApiService(a) {
        return apiServer;
    }


    /** * Initializes retrofit */
    private RetrofitService(a) {

        // Configure okHttp and set the time, log information, and cookies
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                // Set the timeout period
                .connectTimeout(15, TimeUnit.SECONDS)
                // Set Cookie persistence
                .cookieJar(new CookiesManager(XUtil.getApplication()))
                .build();

        // Associate okHttp with rxJava and Gson configuration and baseUrl
        Retrofit retrofit = newRetrofit.Builder() .client(okHttpClient) .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(BaseConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(API.BASE_URL) .build(); apiServer = retrofit.create(API.WAZApi.class); }}Copy the code

2.3 bean

  • The nested entity class here looks complicated, but can actually be passedAStheGsonFormatPlug-in one-click generation
  • Note: Do not takedata.errorCode.errorMsgInto the entity class

2.3.1 Article

Entity class for article content



/**
 * Description : Article
 *
 * @author XuCanyou666
 * @date2020/2/8 * /


public class Article {

    / * * * curPage: 2 * datas: [{" apkLink ":" ", "the author:" should be "/", "chapterId:" 67, "chapterName" : "network base..."}] * offset: 20 * over : false * pageCount : 62 * size : 20 * total : 1224 */

    public int curPage;
    public int offset;
    public boolean over;
    public int pageCount;
    public int size;
    public int total;
    public List<DataDetailBean> datas;

    public static class DataDetailBean {
        /** * apkLink: * author: chapterId: 67 * chapterName: network foundation * collect: false * courseId: 13 * desc: * envelopePic : * fresh : false * id : 2809 * link : https://www.jianshu.com/p/6d2f324c8f42 * niceDate : 2018-04-12 * origin : * projectLink : * publishTime : 1523532264000 * superChapterId : 98 * superChapterName : Network Access * tags: [] * title: Building a Web server on an Android device * Type: 0 * Visible: 1 * zan: 0 */

        public String apkLink;
        public String author;
        public int chapterId;
        public String chapterName;
        public boolean collect;
        public int courseId;
        public String desc;
        public String envelopePic;
        public boolean fresh;
        public int id;
        public int originId;
        public String link;
        public String niceDate;
        public String origin;
        public String projectLink;
        public long publishTime;
        public int superChapterId;
        public String superChapterName;
        public String title;
        public int type;
        public int visible;
        public int zan;
        public List<?> tags;
    }

}

Copy the code

2.3.2 User



/** * GitHub : https://github.com/yechaoa * CSDN : http://blog.csdn.net/yechaoa * 

* Created by yechao on 2018/5/2. * Describe : */

public class User { /** * collectIds : [] * email : * icon : * id : 3 * password : 111111 * type : 0 * username : 111111 */ public String email; public String icon; public int id; public String password; public int type; public String username; publicList<? > collectIds;public String repassword; } Copy the code

2.4 module

  • This is modular management, BenDemoThere areLogin.Register.HomeThree modules in total
  • Due to space limitation, only one module is described here. Other modules are written in a similar way. You can check the specific writing method on Github

Against 2.4.1login

2.4.1.1 ILoginView

LoginView layer interface


/**
 * Description : ILoginView
 *
 * @author XuCanyou666
 * @date2020/2/8 * /

public interface ILoginView extends BaseView {

    /** * The login succeeded **@paramSuccessMessage successMessage */
    void showLoginSuccess(String successMessage);

    /** * login failed **@paramErrorMessage Indicates a failure */
    void showLoginFailed(String errorMessage);

    void doSuccess(BaseBean<User> user);

}
Copy the code
2.4.1.2 LoginPresenter

Because RxJava is encapsulated and the Model layer code is relatively compact, we write the Model directly to Presenter to save work



/**
 * Description : LoginPresenter
 *
 * @author XuCanyou666
 * @date2020/2/8 * /


class LoginPresenter extends BasePresenter<ILoginView> {

    LoginPresenter(ILoginView baseView) {
        super(baseView);
    }


    /** * login **@param username         username
     * @param password         password
     * @paramUsernameCountMax Specifies the maximum number of characters *@paramPasswordCountMax Maximum number of characters */
    void login(String username, String password, int usernameCountMax, int passwordCountMax) {
        YUtils.closeSoftKeyboard();
        // Check whether the entered account password complies with the specifications
        if (isValid(username, password, usernameCountMax, passwordCountMax)) {
            addDisposable(apiServer.login(username, password), new BaseObserver<BaseBean<User>>(baseView, true) {
                @Override
                public void onSuccess(BaseBean<User> bean) {
                    baseView.showLoginSuccess("Login succeeded ( ̄▽ ̄)");
                    // Save the login account into sp
                    SpUtil.setBoolean(GlobalConstant.IS_LOGIN, true);
                    SpUtil.setString(GlobalConstant.USERNAME, bean.data.username);
                    SpUtil.setString(GlobalConstant.PASSWORD, bean.data.password);
                    baseView.doSuccess();
                }

                @Override
                public void onError(String msg) {
                    baseView.showLoginFailed(msg + "(° ∀ °) ノ"); }}); }else {
            baseView.showLoginFailed("Fill in error (°∀°) Blue"); }}/** * Check whether the entered account password complies with specifications **@param userName         username
     * @param password         password
     * @paramUsernameCountMax Specifies the maximum number of characters *@paramPasswordCountMax Maximum number of characters *@returnCompliance */
    private boolean isValid(String userName, String password, int usernameCountMax, int passwordCountMax) {
        return check(userName, usernameCountMax) && check(password, passwordCountMax);
    }

    /** * Check whether the input is canonical **@paramString Indicates the input *@paramTilCounterMaxLength Maximum length of input characters * for the textInputLayout control@returnCompliance */
    private boolean check(String string, int tilCounterMaxLength) {
        return! TextUtils.isEmpty(string) && string.length() <= tilCounterMaxLength && tilCounterMaxLength /2<= string.length(); }}Copy the code
2.4.1.3 LoginTextWatcher

The listener for the login interface input box


/** * TextInputLayout listener * created by xucanyou666 * on 2020/2/718:09 * email: [email protected] */
 public class LoginTextWatcher implements android.text.TextWatcher {
    private TextInputLayout mTilUsername;
    private TextInputLayout mTilPassword;

     LoginTextWatcher(TextInputLayout username, TextInputLayout password) {
        mTilUsername = username;
        mTilPassword = password;
    }



    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}@Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}@Override
    public void afterTextChanged(Editable s) {
        checkInput(mTilUsername);
        checkInput(mTilPassword);
    }

    /** * Check whether the input is valid **@param textInputLayout textInputLayout
     */
    public static void checkInput(TextInputLayout textInputLayout) {
        if(textInputLayout ! =null) {
            if (textInputLayout.getEditText().getText().length() > textInputLayout.getCounterMaxLength()) {
                textInputLayout.setError("Input exceeds upper limit");
            } else if (textInputLayout.getEditText().getText().length() < textInputLayout.getCounterMaxLength() / 2) {
                textInputLayout.setError("Minimum of 6");
            } else {
                textInputLayout.setError(null); }}}}Copy the code
2.4.1.4 LoginActivity

/**
 * Description : LoginActivity
 *
 * @author XuCanyou666
 * @date2020/2/8 * /


public class LoginActivity extends BaseActivity<LoginPresenter> implements ILoginView {

    @BindView(R.id.et_username)
    EditText mEtUsername;
    @BindView(R.id.til_username)
    TextInputLayout mTilUsername;
    @BindView(R.id.et_password)
    EditText mEtPassword;
    @BindView(R.id.til_password)
    TextInputLayout mTilPassword;
    @BindView(R.id.btn_login)
    Button mBtnLogin;
    @BindView(R.id.btn_register)
    Button mBtnRegister;


    @Override
    protected LoginPresenter createPresenter(a) {
        return new LoginPresenter(this);
    }


    @Override
    protected int getLayoutId(a) {
        return R.layout.activity_login;
    }

    @Override
    protected void initData(a) {}@Override
    protected void initView(a) {
        LoginTextWatcher textWatcher = new LoginTextWatcher(mTilUsername, mTilPassword);
        mEtUsername.addTextChangedListener(textWatcher);
        mEtPassword.addTextChangedListener(textWatcher);
    }


    @Override
    public void showLoginSuccess(String successMessage) {
        ToastUtil.showToast(successMessage);
    }

    @Override
    public void showLoginFailed(String errorMessage) {
        ToastUtil.showToast(errorMessage);
    }

    @Override
    public void doSuccess(a) {
        startActivity(MainActivity.class, true);
    }


    @OnClick({R.id.btn_login, R.id.btn_register})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_login:
                String username = mEtUsername.getText().toString().trim();
                String password = mEtPassword.getText().toString().trim();
                int tilUsernameCounterMaxLength = mTilUsername.getCounterMaxLength();
                int tilPasswordCounterMaxLength = mTilPassword.getCounterMaxLength();
                presenter.login(username, password, tilUsernameCounterMaxLength, tilPasswordCounterMaxLength);
                break;
            case R.id.btn_register:
                YUtils.closeSoftKeyboard();
                startActivity(RegisterActivity.class, false);
                break;
            default:
                break; }}}Copy the code

Three. I encountered problems in the use

3.1 Color resource file error

One day, WHEN I clicked on my colors.xml resource file, IT looked something like this

Then when the mouse cursor moves to the red mark, it finds

The color “colorPrimary” in values has no declaration in The base values Folder; This can lead to crashes when the resource is queried in a configuration that does not match this qualifier less…

Then I translated:

The color “colorPrimary” in the value is not declared in the base value FOLde

Meng forced, I am not a statement…. Finally, Baidu arrived at the results

The solution is to cut out the Colors file and paste it back.

Feels like a BUG in AS…. I’m using version 3.5.1 of AS

3.2 Prompt for Importing DependenciesFailed to resolve

The solution is to add Maven to build. Gradle in the root directory

So here it is

3.3 When entering the article list interface, the progress bar will not be hidden automatically

Problem scenario: The progress bar is automatically shown and hidden when a Presenter requests a list of articles, but cannot be hidden when the list of articles is requested

After browsing the code, I found that I requested the article list method to write one more time, solution: only save onResume inside one time

4. Quick experience

If you want to use this framework more easily, I have prepared the MVP framework I have packaged for you, you just need to import dependencies to enjoy the above quick development experience, see Github for details

Github address: github.com/LoveLifeEve…

5. Use it in the project

After reading, some readers may have doubts, said so cattle force, in the project is how to use it, there will be a bug!! Don’t worry, the author immediately according to this framework, developed a simple and easy to use beautiful “play Android”! Hope to solve your confusion HHH


If the article is a little help to you, I hope you can click on it. Your click on it is my motivation

References:

  • wanandroid_java
  • Bug in android Studio colors file
  • Encapsulation of the Android base class BaseActivity
  • Play with the Android open API
  • Failed to resolve:com.github. XXX error appears after package dependency