先扯兩句
原本這篇博客是要繼續寫《一個Android工程的從零開始》的,不過看到了0502Leeyuu丶在簡書上給我指出的問題。
你好,爲什麼要用compile ‘com.jakewharton:butterknife:5.1.1’,而不用最新版的?
爲什麼我使用這個版本的原因,正文中會予以說明,下面我就將自己解決的方法,以及0502Leeyuu丶爲我提供的方法一同展示出來,也方便大家多角度選擇。
還是厚着臉皮先將自己的github地址貼出來,大家可以去看一下我的源碼。
MyBaseApplication (https://github.com/BanShouWeng/MyBaseApplication)
並給大家展示個神器,叫Android知識點——目錄,好了,閒言少敘,下面進入正題。
正文
###ButterKnife集成###
其實呢,看了我之前博客的朋友應該知道,我前面實際上並沒有使用到ButterKnife,可項目中爲什麼會有這個呢,還是因爲我一貫偷懶的一個嘗試,也就是Android ButterKnife Zelezny插件的運用。
不過這個插件暫且不提,在文章後面會與大家分享,先說一下使用這個插件的一個前提,就是需要我們添加一下ButterKnife的庫。
而Android Studio爲了照顧我這種比較喜歡偷懶的人,專門給提供了一些添加開源庫的方式。
按以上五步操作,會出現如下對話框。
在搜索框中輸入我們想要查詢的控件,點Shift+Enter(這個是鼠標移動到放大鏡提示的,但是操作中,我點Enter也是可以的),就可以進行搜索,然後在所有搜索結果中選擇出我們想要的開源庫,點擊OK即可。
好了博客原本的內容到這裏就應該結束了,可是Android Studio實在是給我這樣的懶人出了一個大難題啊,那就是如下圖:
當我輸入了“butter”時,下面的提示是Nothing to show,如果輸入bu呢:
當然現在我的Android Studio出現的情況是輸入ButterKnife或者butterknife的時候都可以找到我們要找到的開源庫,當時當時出現的卻是上面那個5.1.1的版本,具體原因暫時未知。
於是偷懶的我呢就直接拿來嘗試了一下Android ButterKnife Zelezny,可用。不過由於沒有到ButterKnife的部分就沒有去搜索最新版。
這裏呢,爲了這次粗心向大家道歉,希望大家發現我博客中存在的問題以及不足也能同樣提出來,謝謝大家了,更要感謝0502Leeyuu丶的認真嚴謹。
下面呢,就給出0502Leeyuu丶和我個人的兩種解決方式:
1、貼出0502Leeyuu丶給出的解決方法:
0502Leeyuu丶: @半壽翁 一起學習,我也弄android沒多久,要不你試試把全稱com.jakewharton:butterknife:8.7.0放進去搜索一下,我之前弄cardview的時候也搜索不到,輸入全稱搜到了,不知道會不會起作用。而且最新版本的ButterKnife中的inject方法沒了。
其中說到的inject類似理解爲當前新版本中的bind,我嘗試的過程中發現,將版本好去掉,也可以搜出結果。
2、GitHub上找到對應運用:
其中給我們提供了一個鏈接:http://jakewharton.github.io/butterknife/但是隻在其中講解了ButterKnife如何使用,並沒有找到最新版本的相關信息。
不過返回到https://github.com/JakeWharton/butterknife頁面向下翻,可以看到如下部分:
將dependencies中的代碼粘到我們build.gradle(module: app)文件中的dependencies中。
可以看到文件上方出現如下提示:
點擊右上角的Sync Now即可完成開源庫的加載。
當然,以上兩種方式都是應急時使用,還是希望大家可以直接在Library Dependency中添加成功。
###ButterKnife使用###
既然已經集成了,那麼下面我們就來看看費這麼到力氣去集成ButterKnife,它會怎麼幫我偷懶,才值得這麼折騰。
它的作用,在http://jakewharton.github.io/butterknife/中可以查到,一言以蔽之就是資源的綁定,包括圖片、文字等,也有控件和點擊事件。
當然,一般常用的情況還是控件的綁定,以及點擊事件。
使用之前,需要我們先將ButterKnife與當前的Activity或者Fragment綁定,也就是在onCreate方法中添加如下代碼:
ButterKnife.bind(this);
控件綁定
//系統綁定
TextView firstName = (TextView)findViewById( R.id.first_name);
//ButterKnife綁定
//綁定方法1
@BindView(R.id.first_name) TextView firstName;
//綁定方法2
TextView firstName = ButterKnife.findById(view, R.id.first_name);
關於以上兩種綁定的方法,還是有所不同的(好吧, 我承認這句是廢話),至於如何不同,綁定方法1還是比較簡單的,只要直接用就好,只是簡單自然有簡單的代價,那就是綁定方法1所使用的方法不能綁定私有控件或者是靜態控件,所以有一些要求封裝嚴謹的這種方法也就不適用了。
而綁定方法2,我嘗試了創建私有控件,沒有問題,靜態的沒有嘗試,有興趣的可以自己嘗試一下,也可以看得出來,相對於系統提供的方法,他省去的部分是強轉,可是這也就帶來了另一個問題,那就是findById中的第一個參數——view:
baseScrollView = ButterKnife.findById(getLayoutInflater().inflate(R.layout.activity_base, null), R.id.base_scroll_view);
這部分代碼我是將整體的內容合在了一起寫,而view也就相當於getLayoutInflater().inflate(R.layout.activity_base, null),這自然就是我們整個佈局的解析。
當然,如果在Fragment中,畢竟在onCreateView中原本也是需要解析一下佈局的,就直接保存下來,在onViewCreated方法中直接使用解析。
在Activity中,就建議使用傳遞Activity參數的方式了,至於第三中Dialog的暫時沒有嘗試過,需要大家自行去探索了。
點擊事件:
//系統點擊事件
findViewById( R.id.first_name).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast("點擊了");
}
});
//
@OnClick(R.id.first_name)
public void sayHi(TextView firstName) {
Toast("點擊了");
}
當然,點擊事件也是允許我們設置多個id的,如下:
@OnClick({R.id.first_name, R.id.last_name})
private void click(View view) {
switch (view.getId()){
case R.id.first_name:
break;
case R.id.last_name:
break;
}
}
對應做處理即可,如果需要對所點擊控件操作的話,強轉view即可。
其他更靈活的運用大家還是看一下官方提供的說明吧。
###Android ButterKnife Zelezny ###
####Android ButterKnife Zelezny插件安裝 ####
扯了這麼多,終於該上我們的終極大Boss了,偷懶神器Android ButterKnife Zelezny。
首先第一步,自然是需要將這個插件安裝到我們的Android Studio中。
快捷鍵 Ctrl + Alt + S打開設置頁面:
依圖打開Browse Repositories
如圖找到我們要添加的插件Android ButterKnife Zelezny(這搜索纔是我要的生活),點擊install後會下載安裝,成功後會提示重啓Android Studio:
點擊Restart Android Studio後會出現如下彈窗:
點擊Restart,之後坐等Android Studio自動重啓就好。
####Android ButterKnife Zelezny的使用 ####
使用的部分我就在自己的BaseActivity中進行了,首先是將之前創建的initView方法註釋掉,因爲Android ButterKnife Zelezny完成的就是控件初始化的操作。
在對應需要使用Activity或者Fragment中,將光標放置在layout對應的名稱上,如下圖的“activity_base”,然後點擊Alt + Insert快捷鍵(光標在其他位置找不到所需要的選項):
可以看到出現了Generate ButterKnife Injections的選項,點擊打開對話框(可以看到其後也有快捷鍵,不過與搜狗輸入法的顏文字衝突,關閉後再次點擊,還與其他軟件衝突,一個一個關實在太費事了,也就放棄了直接一步到位,對於我這種懶人來說,這個絕對也是含淚完成的啊)
可以看到對應的命名都已經幫我們按照駝峯規則完成,同時也可以自行選擇那些需要創建OnClick方法,我這裏選擇了四個。
其左下角有兩個可選項:第一個是是否創建ViewHolder,這裏暫時不需要;第二個是是否將點擊事件的方法分開,自然也沒有必要,所以就直接點擊了Confirm。
處理完成後,可以看到我們的代碼中多出瞭如下兩部分代碼:
//控件綁定
@BindView(R.id.base_back)
ImageView baseBack;
@BindView(R.id.base_title)
TextView baseTitle;
@BindView(R.id.base_right_icon2)
ImageView baseRightIcon2;
@BindView(R.id.base_right_icon1)
ImageView baseRightIcon1;
@BindView(R.id.base_right_text)
TextView baseRightText;
@BindView(R.id.base_title_layout)
RelativeLayout baseTitleLayout;
@BindView(R.id.base_main_layout)
LinearLayout baseMainLayout;
@BindView(R.id.base_scroll_view)
ScrollView baseScrollView;
//點擊事件
@OnClick({R.id.base_back, R.id.base_right_icon2, R.id.base_right_icon1, R.id.base_right_text})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.base_back:
break;
case R.id.base_right_icon2:
break;
case R.id.base_right_icon1:
break;
case R.id.base_right_text:
break;
}
}
可以看得出來,明顯要比之前使用的方法要簡單的多,爲了配合這部分使用,我將BaseActivity中的返回鍵也做了接口監聽,代碼參見附錄2。
ps:不過由於前面說過,綁定方法1、2的利弊,所以這裏只是演示了一下ButterKnife的使用效果,在BaseActivity的封裝中,使用的還是原本的系統的方法解析控件,不過在後續的Activity和 Fragment中,如果對封裝要求沒有那麼嚴謹的時候,使用ButterKnife確實是一個不錯的選擇,畢竟可以偷懶嘛。
附錄
附錄1
附錄2
package com.banshouweng.mybaseapplication.base;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.banshouweng.mybaseapplication.R;
import com.banshouweng.mybaseapplication.event.NetBroadcastReceiver;
import com.banshouweng.mybaseapplication.ui.activity.MainActivity;
import com.banshouweng.mybaseapplication.widget.CustomProgressDialog;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class BaseActivity extends AppCompatActivity implements NetBroadcastReceiver.NetEvevt {
/**
* 網絡狀態監聽接受者
*/
private static NetBroadcastReceiver.NetEvevt evevt;
/**
* 用於傳遞的上下文信息
*/
public Context context;
public Activity activity;
@BindView(R.id.base_back)
ImageView baseBack;
@BindView(R.id.base_title)
TextView baseTitle;
@BindView(R.id.base_right_icon2)
ImageView baseRightIcon2;
@BindView(R.id.base_right_icon1)
ImageView baseRightIcon1;
@BindView(R.id.base_right_text)
TextView baseRightText;
@BindView(R.id.base_title_layout)
RelativeLayout baseTitleLayout;
@BindView(R.id.base_main_layout)
LinearLayout baseMainLayout;
@BindView(R.id.base_scroll_view)
ScrollView baseScrollView;
/**
* 是否重置返回按鈕點擊事件
*/
private boolean isResetBack = false;
/**
* 點擊回調方法
*/
private OnClickRightIcon1CallBack onClickRightIcon1;
private OnClickRightIcon2CallBack onClickRightIcon2;
private OnClickRightTextCallBack onClickRightText;
private OnClickBackCallBack onClickBack;
/**
* 當前打開Activity存儲List
*/
private static List<Activity> activities = new ArrayList<>();
/**
* 加載提示框
*/
private CustomProgressDialog customProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
if (!(this instanceof MainActivity)) {
activities.add(this);
}
ButterKnife.bind(this);
context = getApplicationContext();
activity = this;
customProgressDialog = new CustomProgressDialog(activity, R.style.progress_dialog_loading, "玩命加載中。。。");
// initView();
}
/**
* 隱藏返回鍵
*/
private void hideBack() {
baseBack.setVisibility(View.GONE);
}
/**
* 設置標題
*
* @param title 標題的文本
*/
public void setTitle(String title) {
baseTitle.setText(title);
}
public void setBaseBack(OnClickBackCallBack onClickBack) {
this.onClickBack = onClickBack;
isResetBack = true;
}
/**
* 設置右側圖片1(最右側)
*
* @param resId 圖片的資源id
* @param alertText 提示文本
* @param onClickRightIcon1 點擊處理接口
*/
public void setBaseRightIcon1(int resId, String alertText, OnClickRightIcon1CallBack onClickRightIcon1) {
this.onClickRightIcon1 = onClickRightIcon1;
baseRightIcon1.setImageResource(resId);
baseRightIcon1.setVisibility(View.VISIBLE);
//語音輔助提示的時候讀取的信息
baseRightIcon1.setContentDescription(alertText);
}
/**
* 設置右側圖片2(右數第二個圖片)
*
* @param resId 圖片的資源id
* @param alertText 提示文本
*/
public void setBaseRightIcon2(int resId, String alertText, OnClickRightIcon2CallBack onClickRightIcon2) {
this.onClickRightIcon2 = onClickRightIcon2;
baseRightIcon2.setImageResource(resId);
baseRightIcon2.setVisibility(View.VISIBLE);
//語音輔助提示的時候讀取的信息
baseRightIcon2.setContentDescription(alertText);
}
/**
* 設置右側文本信息
*
* @param text 所需要設置的文本
*/
public void setBaseRightText(String text, OnClickRightTextCallBack onClickRightText) {
this.onClickRightText = onClickRightText;
baseRightText.setText(text);
baseRightText.setVisibility(View.VISIBLE);
}
/**
* 引用頭部佈局
*
* @param layoutId 佈局id
*/
public void setBaseContentView(int layoutId) {
//當子佈局高度值不足ScrollView時,用這個方法可以充滿ScrollView,防止佈局無法顯示
((ScrollView) findViewById(R.id.base_scroll_view)).setFillViewport(true);
LinearLayout layout = (LinearLayout) findViewById(R.id.base_main_layout);
//獲取佈局,並在BaseActivity基礎上顯示
final View view = getLayoutInflater().inflate(layoutId, null);
//關閉鍵盤
hideKeyBoard();
//給EditText的父控件設置焦點,防止鍵盤自動彈出
view.setFocusable(true);
view.setFocusableInTouchMode(true);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
layout.addView(view, params);
}
/**
* 隱藏鍵盤
*/
public void hideKeyBoard() {
View view = activity.getWindow().peekDecorView();
if (view != null) {
InputMethodManager inputmanger = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
inputmanger.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
/**
* 跳轉頁面
*
* @param clz 所跳轉的目的Activity類
*/
public void startActivity(Class<?> clz) {
startActivity(new Intent(this, clz));
}
/**
* 跳轉頁面
*
* @param clz 所跳轉的目的Activity類
* @param bundle 跳轉所攜帶的信息
*/
public void startActivity(Class<?> clz, Bundle bundle) {
Intent intent = new Intent(this, clz);
if (bundle != null) {
intent.putExtra("bundle", bundle);
}
startActivity(intent);
}
/**
* 跳轉頁面
*
* @param clz 所跳轉的Activity類
* @param requestCode 請求碼
*/
public void startActivityForResult(Class<?> clz, int requestCode) {
startActivityForResult(new Intent(this, clz), requestCode);
}
/**
* 跳轉頁面
*
* @param clz 所跳轉的Activity類
* @param bundle 跳轉所攜帶的信息
* @param requestCode 請求碼
*/
public void startActivityForResult(Class<?> clz, int requestCode, Bundle bundle) {
Intent intent = new Intent(this, clz);
if (bundle != null) {
intent.putExtra("bundle", bundle);
}
startActivityForResult(intent, requestCode);
}
/**
* 消息提示框
*
* @param message 提示消息文本
*/
public void showToast(String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
/**
* 消息提示框
*
* @param messageId 提示消息文本ID
*/
public void showToast(int messageId) {
Toast.makeText(context, messageId, Toast.LENGTH_SHORT).show();
}
/**
* 關閉所有Activity(除MainActivity以外)
*/
public void finishActivity() {
for (Activity activity : activities) {
activity.finish();
}
}
/**
* 跳轉到指定的Activity
*
* @param clz 指定的Activity對應的class
*/
public void goTo(Class<?> clz) {
if (clz.equals(MainActivity.class)) {
finishActivity();
} else {
for (int i = activities.size() - 1; i >= 0; i--) {
if (clz.equals(activities.get(i).getClass())) {
break;
} else {
activities.get(i).finish();
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
activities.remove(this);
}
/**
* 網絡變化回調方法
*
* @param mobileNetState 當前的網絡狀態
*/
@Override
public void onNetChanged(int mobileNetState) {
}
/**
* 顯示加載提示框
*/
public void showLoadDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
customProgressDialog.show();
}
});
}
/**
* 隱藏加載提示框
*/
public void hideLoadDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (customProgressDialog != null && customProgressDialog.isShowing()) {
customProgressDialog.dismiss();
}
}
});
}
@OnClick({R.id.base_back, R.id.base_right_icon2, R.id.base_right_icon1, R.id.base_right_text})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.base_back:
if (isResetBack) {
onClickBack.clickBack();
} else {
finish();
}
break;
case R.id.base_right_icon2:
onClickRightIcon2.clickRightIcon2();
break;
case R.id.base_right_icon1:
onClickRightIcon1.clickRightIcon1();
break;
case R.id.base_right_text:
onClickRightText.clickRightText();
break;
}
}
/**
* 圖片一點擊回調接口
*/
public interface OnClickRightIcon1CallBack {
void clickRightIcon1();
}
/**
* 圖片二點擊回調接口
*/
public interface OnClickRightIcon2CallBack {
void clickRightIcon2();
}
/**
* 右側文字點擊回調接口
*/
public interface OnClickRightTextCallBack {
void clickRightText();
}
/**
* 返回鍵點擊回調接口
*/
public interface OnClickBackCallBack {
void clickBack();
}
}