帶你封裝自己的MVP+Retrofit+RxJava2框架(一)

前言

文本已經(jīng)收錄到我的Github個(gè)人博客蚤假,歡迎大佬們光臨寒舍:我的GIthub博客

看完本篇文章的当凡,可以看下帶你封裝自己的MVP+Retrofit+RxJava2框架(二)铣焊,里面封裝得到了改進(jìn)

本篇文章需要已經(jīng)具備的知識(shí):

  • MVP的概念和基本使用
  • Retrofit框架的基本使用
  • RxJava2框架的基本使用
  • ButterKnife框架的基本使用
  • Base基類的概念

學(xué)習(xí)清單:

  • ActivityFragment基類的封裝
  • MVP的封裝使用

一.為什么要封裝這套框架呢?

在搞清楚這個(gè)問題之前末贾,我們回顧一下基本概念

RxJava: ReactiveXJVM上的一個(gè)實(shí)現(xiàn)筐钟,ReactiveX使用Observable序列組合異步和基于事件的程序;掌握了它秉继,你可以優(yōu)美地處理異步任務(wù)和事件的回調(diào)

Retrofit:一個(gè) RESTfulHTTP網(wǎng)絡(luò)請(qǐng)求框架的封裝祈噪,網(wǎng)絡(luò)請(qǐng)求的工作本質(zhì)上是OkHttp 完成,而 Retrofit僅負(fù)責(zé) 網(wǎng)絡(luò)請(qǐng)求接口的封裝:掌握了它尚辑,你能優(yōu)美地進(jìn)行網(wǎng)絡(luò)請(qǐng)求辑鲤。

MVP:一種解耦模型和視圖的模式,是現(xiàn)在很多公司的主流模式杠茬。

由此可見月褥,在平時(shí)的開發(fā)中熟練運(yùn)用這種模式,不僅可以滿足生活中大部分應(yīng)用程序的場(chǎng)景瓢喉,還可以為將來的工作積攢寶貴的實(shí)戰(zhàn)經(jīng)驗(yàn)宁赤。

二.核心用法

本項(xiàng)目基于Android X 進(jìn)行構(gòu)建,完整代碼可在我的Github上下載:帶你封裝自己的MVP+Retrofit+RxJava2框架

首先栓票,看一下我們項(xiàng)目的基本結(jié)構(gòu)决左,下面筆者將為大家詳細(xì)介紹每個(gè)類的相關(guān)信息

[圖片上傳失敗...(image-e9a3ee-1583233999196)]

2.1 基類Base

Base基類是封裝了一些基類,方便后面新建新的Activity或者Fragment走贪,減少耦合

2.1.1 BaseActivity

這個(gè)類是Activity的基類佛猛,注意與下面的BaseMvpActivity區(qū)分開

/**
 * Description : BaseActivity 基類活動(dòng)
 *
 * @author XuCanyou666
 * @date 2020/2/2
 */


public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        initPresenter();
        initViews();
        ButterKnife.bind(this);
    }


    /**
     * 抽象方法:實(shí)例化Presenter
     */
    protected abstract void initPresenter();

    /**
     * 抽象方法:初始化控件,一般在BaseActivity中通過ButterKnife來綁定坠狡,所以該方法內(nèi)部一般我們初始化界面相關(guān)的操作
     *
     * @return 控件
     */
    protected abstract void initViews();


    /**
     * 抽象方法:得到布局id
     *
     * @return 布局id
     */
    protected abstract int getLayoutId();


    /**
     * 啟動(dòng)Fragment
     *
     * @param id       id
     * @param fragment 碎片
     */
    protected void startFragment(int id, Fragment fragment) {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.add(id, fragment);
        fragmentTransaction.commit();
    }

}

2.1.2 BaseView

一個(gè)接口语御,說明了每一個(gè)View基本需要的一些操作

package com.users.xucanyou666.rxjava2_retrofit_mvp.base;


/**
 * created by xucanyou666
 * on 2020/1/31 18:26
 * email:913710642@qq.com
 */
public interface BaseView {

    /**
     * 顯示進(jìn)度框
     */
    void showProgressDialog();


    /**
     * 關(guān)閉進(jìn)度框
     */
    void hideProgressDialog();


    /**
     * 出錯(cuò)信息的回調(diào)
     *
     * @param result 錯(cuò)誤信息
     */
    void onError(String result);



}

2.1.3 BaseMvpActivity

  • MVP活動(dòng)的基類

  • 繼承自BaseActivity,它是MVP活動(dòng)的基類贪婉,封裝好了Presenter的相關(guān)操作

package com.users.xucanyou666.rxjava2_retrofit_mvp.base;


/**
 * created by xucanyou666  MVP活動(dòng)的基類蓬推,封裝好了presenter的相關(guān)操作
 * on 2019/12/24 20:53
 * email:913710642@qq.com
 */

public abstract class BaseMvpActivity<V extends BaseView, P extends BasePresenter> extends BaseActivity {

    private P presenter;

    /**
     * 初始化presenter
     */
    @Override
    protected void initPresenter() {
        presenter = createPresenter();
        if (presenter != null) {
            presenter.attachView((V) this);
        }
    }

    /**
     * 創(chuàng)建presenter
     *
     * @return Presenter
     */
    protected abstract P createPresenter();


    /**
     * 得到presenter
     *
     * @return presenter
     */
    protected P getPresenter() {
        return presenter;
    }

    /**
     * 銷毀
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter != null) {
            presenter.detachView();
        }
    }


}

2.1.4 BaseFragment

  • Fragment的基類

  • 需要注意的是,這里用了ButterKnife框架幻锁,對(duì)碎片進(jìn)行了綁定和解綁操作

/**
 * Fragment的基類,封裝了一些Fragment的相關(guān)操作
 * created by xucanyou666
 * on 2020/1/31 16:21
 * email:913710642@qq.com
 */
public abstract class BaseFragment<T extends BasePresenter> extends Fragment implements BaseView {
    protected T mPresenter;
    protected Context mContext;
    protected Bundle mBundle;
    protected Unbinder unbinder;
    protected View view;

    /**
     * 恢復(fù)數(shù)據(jù)
     *
     * @param outState bundle
     */
    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mBundle != null) {
            outState.putBundle("bundle", mBundle);
        }
    }

    /**
     * 綁定activity
     *
     * @param context context
     */
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        mContext = context;
    }

    /**
     * 運(yùn)行在onAttach之后边臼,可以接收別人傳遞過來的參數(shù)哄尔,實(shí)例化對(duì)象
     * 可以解決返回的時(shí)候頁面空白的bug
     *
     * @param savedInstanceState
     */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mBundle = savedInstanceState.getBundle("bundle");
        } else {
            mBundle = getArguments() == null ? new Bundle() : getArguments();
        }
        //初始化presenter
        mPresenter = initPresenter();
    }

    protected T getPresenter() {
        return mPresenter;
    }


    /**
     * 運(yùn)行在onCreate之后,生成View視圖
     *
     * @param inflater
     * @param container
     * @param savedInstanceState
     * @return
     */
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = initView(inflater, container, savedInstanceState);
        unbinder = ButterKnife.bind(this, view);
        return view;
    }

    /**
     * 運(yùn)行在onCreateView之后
     * 加載數(shù)據(jù)
     *
     * @param savedInstanceState
     */
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mPresenter.attachView(this);

    }


    /**
     * 跳轉(zhuǎn)Fragment
     *
     * @param toFragment 跳轉(zhuǎn)去的fragment
     */
    public void startFragment(Fragment toFragment) {
        Log.d(TAG, "haha");
        startFragment(toFragment, null);
    }

    /**
     * 跳轉(zhuǎn)Fragment
     *
     * @param toFragment 跳轉(zhuǎn)到的fragment
     * @param tag        fragment的標(biāo)簽
     */
    public void startFragment(Fragment toFragment, String tag) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.hide(this).add(android.R.id.content, toFragment, tag);
        fragmentTransaction.addToBackStack(tag);
        fragmentTransaction.commitAllowingStateLoss();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        unbinder.unbind();
    }

    /**
     * fragment進(jìn)行回退
     * 類似于activity的OnBackPress
     */
    public void onBack() {
        getFragmentManager().popBackStack();
    }

    @Override
    public void onDetach() {
        mPresenter.detachView();
        super.onDetach();
    }

    /**
     * 初始化Fragment應(yīng)有的視圖
     *
     * @return view
     */
    public abstract View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);


    /**
     * 創(chuàng)建presenter
     *
     * @return <T extends BasePresenter> 必須是BasePresenter的子類
     */
    public abstract T initPresenter();

    /**
     * 得到context
     *
     * @return context
     */
    @Override
    public Context getContext() {
        return mContext;
    }

    /**
     * 得到bundle
     *
     * @return bundle
     */
    public Bundle getBundle() {
        return mBundle;
    }

    /**
     * 得到fragment
     *
     * @return fragment
     */
    public Fragment getFragment() {
        return this;
    }

}

2.1.5 BasePresenter


/**
 * created by xucanyou666
 * on 2020/1/16 17:12
 * email:913710642@qq.com
 */
public abstract class BasePresenter<V extends BaseView> {
    //將所有正在處理的Subscription都添加到CompositeSubscription中堂鲤。統(tǒng)一退出的時(shí)候注銷觀察
    private CompositeDisposable mCompositeDisposable;
    private V baseView;

    /**
     * 和View綁定
     *
     * @param baseView
     */
    public void attachView(V baseView) {
        this.baseView = baseView;
    }

    /**
     * 解綁View,該方法在BaseMvpActivity類中被調(diào)用
     */
    public void detachView() {
        baseView = null;
        // 在界面退出等需要解綁觀察者的情況下調(diào)用此方法統(tǒng)一解綁,防止Rx造成的內(nèi)存泄漏
        if (mCompositeDisposable != null) {
            mCompositeDisposable.dispose();
        }
    }

    /**
     * 獲取View
     *
     * @return view
     */
    public V getMvpView() {
        return baseView;
    }



    /**
     * 將Disposable添加,在每次網(wǎng)絡(luò)訪問之前初始化時(shí)進(jìn)行添加操作
     *
     * @param subscription subscription
     */
    public void addDisposable(Disposable subscription) {
        //csb 如果解綁了的話添加 sb 需要新的實(shí)例否則綁定時(shí)無效的
        if (mCompositeDisposable == null || mCompositeDisposable.isDisposed()) {
            mCompositeDisposable = new CompositeDisposable();
        }
        mCompositeDisposable.add(subscription);
    }

}

2.1.6 MyApplication

  • 封裝了一個(gè)可以全局獲取Context的方法媒峡,參考寫法自:《第一行代碼--第二版》
  • 注意:記得在AndroidManifest中注冊(cè)Application
package com.users.xucanyou666.rxjava2_retrofit_mvp.base;


import android.app.Application;
import android.content.Context;

/**
 * 基類
 * created by xucanyou666
 * on 2019/11/2 14:46
 * email:913710642@qq.com
 * @author xucanyou666
 */
public class MyApplication extends Application {
    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    public static Context getContext() {
        return context;
    }
}

2.2 工具類 Util

2.2.1 RetrofitManager

Retrofit單例工具類

/**
 * Retrofit單例工具類
 * created by xucanyou666
 * on 2020/1/16 16:38
 * email:913710642@qq.com
 */
public class RetrofitManager {
    private Retrofit mRetrofit;


    //構(gòu)造器私有瘟栖,這個(gè)工具類只有一個(gè)實(shí)例
    private RetrofitManager() {
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder.connectTimeout(15, TimeUnit.SECONDS);
        mRetrofit = new Retrofit.Builder()
                .client(httpClientBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();
    }


    /**
     * 靜態(tài)內(nèi)部類單例模式
     *
     * @return
     */
    public static RetrofitManager getInstance() {
        return Inner.retrofitManager;
    }

    private static class Inner {
        private static final RetrofitManager retrofitManager = new RetrofitManager();
    }


    /**
     * 利用泛型傳入接口class返回接口實(shí)例
     *
     * @param ser 類
     * @param <T> 類的類型
     * @return Observable
     */
    public <T> T createRs(Class<T> ser) {
        return mRetrofit.create(ser);
    }
}

2.2.2 RxJavaUtil

RxJava的工具類,執(zhí)行線程調(diào)度工作

/**
 * created by xucanyou666
 * on 2019/11/17 19:20
 * email:913710642@qq.com
 *
 * @author xucanyou666
 */
public class RxJavaUtil {
    /**
     * 線程調(diào)度工作
     *
     * @param observable 被觀察者
     * @param <T>        類型
     */
    public static <T> Observable  toSubscribe(Observable<T> observable) {
        return observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

}

2.3 常量類 Contant

常量池谅阿,特別感謝api open網(wǎng)提供的免費(fèi)API

/**
 * created by xucanyou666
 * on 2019/11/17 19:01
 * email:913710642@qq.com
 */
public class StaticQuality {
    public static final String BASE_URL="https://api.gushi.ci/";
}

2.4 接口管理器 Contract

這里集中了一些Model層,Presenter層,View層的與詩歌相關(guān)的接口

/**
 * 詩歌的接口管理器
 * created by xucanyou666
 * on 2020/2/2 15:33
 * email:913710642@qq.com
 */
public interface IPoetryContract {
    interface IPoetryModel {
        /**
         * 得到詩歌
         *
         * @return 詩歌
         */
        Observable<PoetryEntity> getPoetry();
    }

    interface IPoetryPresenter {
        void getPoetry();
    }

    interface IPoetryView extends BaseView {
        /**
         * @param author 作者
         */
        void searchSuccess(String author);
    }
}

2.5 實(shí)體類 Entity



/**
 * 詩歌的實(shí)體類
 * created by xucanyou666
 * on 2020/1/23 21:23
 * email:913710642@qq.com
 * API返回示例:
 * {
 * "content": "胡瓶落膊紫薄汗半哟,碎葉城西秋月團(tuán)。",
 * "origin": "從軍行七首",
 * "author": "王昌齡",
 * "category": "古詩文-天氣-月亮"
 * }
 */
public class PoetryEntity {
    private String content; //詩歌內(nèi)容
    private String origin; //來源
    private String author; //作者
    private String category; //分類

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getOrigin() {
        return origin;
    }

    public void setOrigin(String origin) {
        this.origin = origin;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
}

2.6 Retrofit接口 iApiService



/**
 * retrofit接口
 * created by xucanyou666
 * on 2020/1/23 21:25
 * email:913710642@qq.com
 */
public interface GetPoetryEntity {
    /**
     * 獲取古詩詞
     *
     * @return 古詩詞
     */
    @GET("all.json")
    Observable<PoetryEntity> getPoetry();
}

2.7 視圖層 View

這里為了減少代碼量签餐,方便讀者們掌握核心操作寓涨,故View層都是用的同一個(gè)PresenterModel,僅作學(xué)習(xí)參考

2.7.1 MainActivity

需要注意的是氯檐,這里BaseMvpActivity<activity, presenter>Activity填入的是當(dāng)前的Activity戒良,Presenter填入的是對(duì)應(yīng)的Presenter


/**
 * Description : MainActivity
 *
 * @author XuCanyou666
 * @date 2020/2/3
 */

public class MainActivity extends BaseMvpActivity<MainActivity, PoetryPresenter> implements IPoetryContract.IPoetryView {

    @BindView(R.id.btn_get_poetry)
    Button btnGetPoetry;
    @BindView(R.id.tv_poetry_author)
    TextView tvPoetryAuthor;
    @BindView(R.id.btn_goto_fragment)
    Button btnGotoFragment;
    @BindView(R.id.ll)
    LinearLayout ll;

    @Override
    protected void initViews() {

    }

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

    @Override
    protected PoetryPresenter createPresenter() {
        return PoetryPresenter.getInstance();
    }

    @Override
    public void searchSuccess(String author) {
        tvPoetryAuthor.setText(author);
    }

    @Override
    public void showProgressDialog() {

    }

    @Override
    public void hideProgressDialog() {

    }

    @Override
    public void onError(String result) {
        Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show();
    }


    @OnClick({R.id.btn_get_poetry, R.id.btn_goto_fragment})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_get_poetry:
                getPresenter().getPoetry();
                break;
            case R.id.btn_goto_fragment:
                startFragment(R.id.ll, new MainFragment());
                break;
            default:
                break;
        }
    }
}

2.7.2 MainFragment


/**
 * Description : MainFragment
 *
 * @author XuCanyou666
 * @date 2020/2/2
 */


public class MainFragment extends BaseFragment<PoetryPresenter> implements IPoetryContract.IPoetryView {

    @BindView(R.id.btn_get_poetry)
    Button btnGetPoetry;
    @BindView(R.id.tv_poetry_author)
    TextView tvPoetryAuthor;

    @Override
    public View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public PoetryPresenter initPresenter() {
        return PoetryPresenter.getInstance();
    }


    @Override
    public void showProgressDialog() {

    }

    @Override
    public void hideProgressDialog() {

    }

    @Override
    public void onError(String result) {
        Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show();

    }

    @OnClick(R.id.btn_get_poetry)
    public void onViewClicked() {
        getPresenter().getPoetry();
    }

    @Override
    public void searchSuccess(String author) {
        tvPoetryAuthor.setText(author);
    }
}

2.8 Presenter



/**
 * created by xucanyou666
 * on 2020/1/16 17:09
 * email:913710642@qq.com
 */
public class PoetryPresenter extends BasePresenter<IPoetryContract.IPoetryView> implements IPoetryContract.IPoetryPresenter {

    private static final String TAG = "PoetryPresenter";

    private PoetryEntity mPoetryEntity;
    private PoetryModel mPoetryModel;

    private PoetryPresenter() {
        mPoetryModel = PoetryModel.getInstance();
    }

    public static PoetryPresenter getInstance() {
        return Inner.instance;
    }

    private static class Inner {
        private static final PoetryPresenter instance = new PoetryPresenter();
    }

    /**
     * 得到詩歌
     */
    @Override
    public void getPoetry() {
        Observable observable = mPoetryModel.getPoetry().doOnSubscribe(new Consumer<Disposable>() {
            @Override
            public void accept(Disposable disposable) throws Exception {
                addDisposable(disposable);
            }
        });
        observable = RxJavaUtil.toSubscribe(observable);
        observable.subscribe(new Observer<PoetryEntity>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onNext(PoetryEntity poetryEntity) {
                mPoetryEntity = poetryEntity;
            }

            @Override
            public void onError(Throwable e) {
                getMvpView().onError(e.getMessage());
                Log.d(TAG, "onError: " + e.getMessage());
            }

            @Override
            public void onComplete() {
                if (mPoetryEntity != null) {
                    getMvpView().searchSuccess(mPoetryEntity.getAuthor());
                }
            }
        });

    }
}


2.9 Model



/**
 * created by xucanyou666
 * on 2020/1/16 17:06
 * email:913710642@qq.com
 */
public class PoetryModel implements IPoetryContract.IPoetryModel {


    private PoetryModel() {

    }

    public static PoetryModel getInstance() {
        return Inner.instance;
    }

    private static class Inner {
        private static final PoetryModel instance = new PoetryModel();
    }


    /**
     * 獲取古詩詞
     *
     * @return 古詩詞
     */
    @Override
    public Observable<PoetryEntity> getPoetry() {
        return RetrofitManager.getInstance().createRs(GetPoetryEntity.class).getPoetry();
    }
}

2.10 app.build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    defaultConfig {
        applicationId "com.users.xucanyou666.rxjava2_retrofit_mvp"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    // RxJava
    implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    // Retrofit和jxjava關(guān)聯(lián)
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    // Retrofit使用Gson轉(zhuǎn)換
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    // RxAndroid
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
    //引入ButterKnife
    implementation "com.jakewharton:butterknife:10.2.0"
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0"

    implementation "com.google.android.material:material:1.0.0"
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.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'
}

三.我在使用中遇到的問題

3.1 網(wǎng)絡(luò)權(quán)限忘記授予

  • 解決措施:加上權(quán)限即可
<uses-permission android:name="android.permission.INTERNET" />

3.2 ButterKnife框架版本問題

使用ButterKnife框架的時(shí)候

當(dāng)是androidX的時(shí)候,需要implementation 10.2.0版本的ButterKnife

//引入ButterKnife
    implementation "com.jakewharton:butterknife:10.2.0"
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0"

當(dāng)是android 28等其他版本的時(shí)候冠摄,可以導(dǎo)入8.4.0版本的ButterKnife(導(dǎo)入10.2.0版本會(huì)出錯(cuò))

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

3.3 ButterKnife需要Java 1.8以上的支持

 compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    } 

3.4 Fragment中點(diǎn)擊事件失效的問題

  • 點(diǎn)擊事件失效發(fā)生的場(chǎng)景:Fragment中初始化控件沒有用ButterKnife框架

解決措施如下:

A:方法一:

  • 將控件的初始化放在onCreateView
  • 將控件的點(diǎn)擊事件的代碼放在onActivityCreated

B:方法二:

  • Fragment中使用ButterKnife框架

如果文章對(duì)您有一點(diǎn)幫助的話糯崎,希望您能點(diǎn)一下贊,您的點(diǎn)贊河泳,是我前進(jìn)的動(dòng)力

本文參考鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拆挥,隨后出現(xiàn)的幾起案子薄霜,更是在濱河造成了極大的恐慌,老刑警劉巖纸兔,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惰瓜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡食拜,警方通過查閱死者的電腦和手機(jī)鸵熟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來负甸,“玉大人流强,你說我怎么就攤上這事痹届。” “怎么了打月?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵队腐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我奏篙,道長(zhǎng)柴淘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任秘通,我火速辦了婚禮为严,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肺稀。我一直安慰自己第股,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布话原。 她就那樣靜靜地躺著夕吻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪繁仁。 梳的紋絲不亂的頭發(fā)上涉馅,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音黄虱,去河邊找鬼稚矿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悬钳,可吹牛的內(nèi)容都是我干的盐捷。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼默勾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼碉渡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起母剥,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤滞诺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后环疼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體习霹,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年炫隶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淋叶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伪阶,死狀恐怖煞檩,靈堂內(nèi)的尸體忽然破棺而出处嫌,到底是詐尸還是另有隱情,我是刑警寧澤斟湃,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布熏迹,位于F島的核電站,受9級(jí)特大地震影響凝赛,放射性物質(zhì)發(fā)生泄漏注暗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一墓猎、第九天 我趴在偏房一處隱蔽的房頂上張望捆昏。 院中可真熱鬧,春花似錦毙沾、人聲如沸屡立。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勇皇,卻和暖如春罩句,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背敛摘。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工门烂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兄淫。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓屯远,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親捕虽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慨丐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361