GitHub安卓(Android)熱門開源資源在項目中的使用及項目總結

層級的劃分
我們寫一個稍微復雜的App的時候,不可能只有一個包,必定分門別類。而分類的標準,大部分應該是遵循這三類view(UI層),bussiness(邏輯處理層),還有data(數據層)。然后我們根據這個標準把他們放在不同的包里面。這樣一來,結構,邏輯都很清晰。例如這個項目里面:

| 包名 | 用途 |
|---|---|
| Base | 包含一些基類 |
| domain | 存放一些javabean的類 |
| Fragment | 顧名思義存放fragment的類 |
| Global | 存放網絡的全局類 |
| Utils | 工具類 |
| View | 改寫后的布局類 |
它們分工明確,內容一目了然。每個人的習慣都不盡相同,找到自己熟悉的命名方式,結構明了就好。
- 命名方式
關于命名的規范
1.對于類名我們使用:UpperCamelCase風格編寫

2.對于方法名我們使用:LowerCamelCase風格編寫

這兩個都是駝峰命名,一個是大駝峰(每個單詞的首字母大寫),一個是小駝峰(除第一個單詞以外的單詞首字母大寫)。這種方式命名美觀,簡潔。我縱觀了一下整個項目代碼,發現有些時候不是按照上面的規則,而是例如fragment_left_menu這樣的下劃線命名,所以其實并沒有唯一的標準,只是可以的話盡量這樣命名我們的代碼。
- 基類的創建
顧名思義,基類,即為初始類。如果兩個以上的類同時擁有相似的特征,我們就把它抽取出來單獨形成一個類。就好比我們在一個類的兩個不同的函數中,需要使用同一個參數,這時我們也會習慣性的把它變成全局的變量供兩個函數使用。

假如我們現在要寫5個頁面,顯然界面各不相同,但是我們要為它們編寫一個共同的基類。我們發現無論頁面怎么變,它的標題欄是大同小異的。

標題欄大家很熟悉,ImageButton和TextView,下面我們不知道是什么,就用一個幀布局Framelayout代替。以后如果要添加一個不同的布局,它有一個方法addview()便搞定了
上面的代碼,只是一個模板,給大家提供思路,我們需要初始化界面,初始化數據,mMainActivity的作用除了在Inflate函數里充當上下文的參數(應該改為public類型),其實在對象的流動傳遞里也有很大的作用,后面再敘述。除此之外如果你有必要,還可以來個類的初始化public BaseTab(){},又或是把view抽取出來當作公共參數,又或者你必須規定子類實現某個方法,某個函數,那就把整個基類寫成抽象類。
開源項目的使用


我們隨便百度,都可以搜索到一些很熱門的,很好用的開源項目。當然還是像上一篇說的,你可以直接在github下載下來,然后關聯到你的項目,具體做法上一篇已經講了,現在我就假設你已經導入好了。
- Sliding Menu
因為使用過程中,有牽扯到Fragment,所以就一起講了。
sliding menu顧名思義側邊欄,最直觀的如圖片所示(網絡圖):

public class MainActivity extends SlidingFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
/* 都聲明為framelayout */
setContentView(R.layout.mainactiviy);
//設置側邊欄
setBehindContentView(R.layout.left_menu);
//獲取側邊欄對象
SlidingMenu slidingMenu=getSlidingMenu();
//設置全屏拖動
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
//設置屏幕的預留寬度
slidingMenu.setBehindOffset(400);
initFragment();
}
現在結合代碼說下最簡單的用法,我們首先繼承SlidingFragmentActivity,然后我們為主頁面和側邊欄都設置一個xml的布局,這兩個布局都只有一個 framelayout 方便我們之后換成其他的布局,我們的主頁面和側邊欄的頁面都用fragment實現,等下再說。
接著我們要獲取側邊欄對象,它有個方法getSlidingMenu(),用as的朋友,直接再quick fix一下(Alt+Enter),對象就出來了。我們為它設置參數:
| 方法 | 用途 |
|---|---|
| setBehindContentView | 設置側邊欄的布局文件 |
| 方法 | 用途 |
|---|---|
| setTouchModeAbove | 設置拖動的位置 有全屏 不設置 左邊 右邊等 |

| 方法 | 用途 |
|---|---|
| setBehindOffset | 設置屏幕的預留寬度 其實也就是變向設置你拖出來的大小 |
| 方法 | 用途 |
|---|---|
| toggle | 隱藏側邊欄 |
接下來我們要創建兩個Fragment去填補上面的framelayout。
- Fragment
記得以前學習Fragment的時候,一頭霧水,分為什么靜態和動態。但隨著時間推移,慢慢的就清晰很多了。所以當你遇到一時理解不了的問題,放一放,過一陣子再回頭看,會明白許多。
我們現在只講如何動態創建Fragment:
大致流程應該是這樣
1.我們的Activity 的布局文件只有一個幀布局
2.創建管理者
3.開啟事物
4.替換
5.提交事務
具體代碼如下:
private void initFragment(){
//管理者
FragmentManager fm = getSupportFragmentManager();
//開啟事物
FragmentTransaction transaction = fm.beginTransaction();
//替換
transaction.replace(R.id.fl_left_menu,new Fragment_left_menu(),FRAGMENT_LEFT_MENU);
transaction.replace(R.id.fl_content, new Fragment_content(), FRAGMENT_CONTENT);
//提交
transaction.commit();}
我們在替換這個函數里面,3個參數分別代表:replace(對應framelayout的id,要替換的類,Tag標簽)
如果你只是一個fragment類,寫完替換進去就搞定了,現在我們這個是兩個Fragment,我們可以學以致用,寫一個BaseFragment基類繼承fragment。
到此我們已經動態創建完了,那設置Tag有什么用呢。我們設置標簽,當然是為了獲取它的對象。假如我們想獲取側邊欄的Fragment對象,我們可以這樣做:
private static final String FRAGMENT_LEFT_MENU ="fragment_left_menu" ;
我們在開頭聲明一個靜態的變量
這樣我們在替換的時候就設置好了他們對應的tag值
最后只要寫一個函數,通過管理者就可以輕松獲得Fragment對象了:
public Fragment_left_menu getleftMenuFragment(){
//管理者
FragmentManager fm = getSupportFragmentManager();
Fragment_left_menu fragment = (Fragment_left_menu) fm.findFragmentByTag(FRAGMENT_LEFT_MENU);
return fragment;
}
官方是非常推薦我們使用fragment的,所以大家趕緊學起來吧。就好像以前大家都不習慣寫Viewholder,現在也被強迫著寫了。最后附贈一個思維導圖幫助大家理解它:

- xutils
工具包:提供一些方便的工具為我們使用,有四大模塊:DbUtils,ViewUtils,HttpUtils,BitmapUtils
1.注釋
為了解決頻繁的findviewbyid,于是就有了這個ViewUtils(其實好像也沒快多少)
用法(來自官方文檔):
//在Activity中注入:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ViewUtils.inject(this);
//注入view和事件 ...
textView.setText("some text...");
...
}
//在Fragment中注入:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bitmap_fragment, container, false);
// 加載fragment布局
ViewUtils.inject(this, view);
//注入view和事件 ...
}
最后只需在你需要UI綁定或者事件綁定的語句前加個注釋即可如:
@ViewInject(R.id.textView)
TextView textView;
@OnClick(R.id.test_button)
public void testButtonClick(View v) {
...}
2.網絡工具
我們可以通過HttpUtils請求網絡,方便的獲取網絡數據。
以解析json為例,你可以這樣做:
private void getDatafromServer() {
HttpUtils httpUtils=new HttpUtils();
httpUtils.send(HttpRequest.HttpMethod.GET, murl, new RequestCallBack<String>()
{
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
System.out.println("頁簽結果是" + result);
parsedata(result);
}
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg,Toast.LENGTH_SHORT).show();
error.printStackTrace();
}
});
}
我們首先創建一個HttpUtils對象,在send方法里面,有三個參數分別是(請求的方法(get,post),地址,放回的類型),在匿名內部類中有兩個函數:一個是成功,獲得數據后,我們可以讓它進一步對數據解析。一個是失敗,我們可以讓他在logcat里打印錯誤日志并toast出問題在哪。
這個時候我們假設已成功獲取了json數據,接著對它進行解析:
protected void parsedata(String result) {
Gson gson=new Gson();
tabDetaildata = gson.fromJson(result,Tabdata.class);
....
}
我們借助gson的fromjson方法傳入剛獲得的結果和一個javabean類,就可以成功解析json數據了。
注意:getDatafromServer()這個方法是運行在主線程的,所以在里面的方法當然可以隨意對UI進行設置
3.解析網絡圖片
我們以前常使用線程,異步加載的方式下載網絡圖片,還要擔心內存溢出等問題,現在只需使用BitmapUtils幾行代碼就可以決解這樣復雜的問題。
以listview里加載網絡圖片為例:
/* 新聞列表的適配器 */
class NewslistAdapter extends BaseAdapter{
private BitmapUtils utils;
public NewslistAdapter(){
utils=new BitmapUtils(mActivity);
utils.configDefaultLoadingImage(R.mipmap.pic_item_list_default);
}
···
}
我們在內部初始化的時候傳入當前上下文(mActivity)聲明一個全局的BitmapUtils對象,其中configDefaultLoadingImage方法是用來預加載圖片。
utils.display(hodler.listIcon,tabnewsData.listimage);
最后在適配器的getview函數中,對UI進行設置。它有個display()函數分別填入view和要添加的圖片數據,這樣就大功告成了。
- ViewpagerIndicator
1.我們首先介紹的是viewpage的導航指示器:TabPageIndicator,比較直觀的如圖所示(網絡圖):


如圖,我們先把TabPageIndicator的路徑拷貝下來,在所在viewpage的xml上添加對應的布局:
<com.viewpagerindicator.TabPageIndicator
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
最后,只需在初始view的時候,綁定一下UI
myIndicator= (TabPageIndicator)view.findViewById(R.id.indicator);
并且在viewpager添加完適配器之后,指示器再綁定viewpager
//添加適配器
mviewPager.setAdapter(new MenudetailAdapter());
viewpagermyIndicator.setViewPager(mviewPager);
還有一點需要注意的是,如果指示器和viewpager綁定以后,如果viewpager要設置監聽事件,這時我們應該對指示器設置而不是viewpager自身。
2.CirclePageIndicator
有時我們翻閱圖片,為了有個指示的效果:

這時我們就用到CirclePageIndicator
和上面的一樣,我們首先在布局文件中將其添加

我們看到里面有自定義控件的屬性app,所以首先當然把命名空間給引入,在頭文件的地方添加
xmlns:app="http://schemas.android.com/apk/res-auto"
接著初始化,然后綁定viewpager,和上面一樣。它有兩個方法值得提一下, setSnap()填入(true,false)表示點是否跳躍,也就是我們上方展示圖片的效果。onPageSelected()選擇初始停留的頁面。
- 事件的攔截機制
為了便于理解,我畫了一個思維導圖

由Activity把事件分發出去->到了最外層子控件->最外層子控件通過dispatchTouchevent接收分發的事件->傳給onInterceptTouchevent,由它決定是否攔截,若攔截就交給onTouchevent對事件進行處理,若不攔截就繼續傳到內層的子控件->若所有子控件都不處理,則返回給activity處理。
我們現在回到實際的項目中,來討論事件在具體的例子中如何實現攔截:

這個app的界面中,最左邊有個側邊欄(Sliding menu),最外面是一個由tab來切換的viewpager1,第二層是由viewpagerIndicator來切換的viewpager2,最里面的那層是一個圖片切換的Viewpager3。這應該是一個比較復雜的事件攔截的案例了,我們再來畫一個清晰的圖:

我們常用到事件攔截,是在重寫一個屬于我們自己的自定義view的時候。例如在寫最外層的Viewpager1的時候,我們希望它沒有滾動這個功能,而是通過tab來控制頁面的翻閱,而且我們希望它不攔截事件,而是讓內層的viewpager可以接受到事件,就可以在重寫的viewpager里這樣做:
//停止滾動
public boolean onTouchEvent(MotionEvent ev) { return false;}
//事件是否攔截
public boolean onInterceptTouchEvent(MotionEvent ev) { return false;}
在寫viewpager2的時候,遇到了問題,我們發現如果我們往右劃的時候,slidingmenu這個父控件會攔截我們的右劃事件。所以這個時候我們必須重寫一個viewpager類,來規定事件的攔截。
//請求父類不攔截觸摸事件
public boolean dispatchTouchEvent(MotionEvent ev) {
if(getCurrentItem()!=0) {
getParent().requestDisallowInterceptTouchEvent(true);
}else {
getParent().requestDisallowInterceptTouchEvent(false);
}
return super.dispatchTouchEvent(ev);
}
我們在事件分發的這個方法中,調用requestDisallowInterceptTouchEvent這個方法,來請求父類不攔截觸摸事件,所以當當前的頁面不是第一頁的時候,我們就不攔截,否則我們攔截。
之后我們寫到viewpager3, 我們希望可以自由的劃動里面的圖片, 所以不希望父類攔截我們的事件,在重寫的viewpager里:
public boolean dispatchTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
可是問題就來了,你這里不希望被攔截,但是它的父類viewpager2在第一個頁簽往右劃的時候會規定會被sliding menu攔截,所以我們發現viewpager3在第一個頁簽往右劃的時候還是會引出sliding menu,而不是往前面翻圖片。出現了矛盾,這里我們就需要對代碼進行重構:我們不給viewpage2重寫類,而是給它設置監聽(setOnPageChangeListener)
public void onPageSelected(int position) {
//判斷頁面 控制slidingmenu是否劃出
MainActivity mainActivity= (MainActivity) mActivity;
SlidingMenu slidingMenu = mainActivity.getSlidingMenu();
if(position==0){
slidingMenu.setTouchModeAbove(slidingMenu.TOUCHMODE_FULLSCREEN);
}else{
slidingMenu.setTouchModeAbove(slidingMenu.TOUCHMODE_NONE);
}
}
viewpager3的重寫類,我們也做出新的事件攔截規定,我們規定
1.第一個圖片往右滑動 跳到上一個頁簽(或側邊欄)
2.最后一個圖片往左滑動 跳到下一個頁簽
3.向下劃的時候
需要攔截,于是:
public boolean dispatchTouchEvent(MotionEvent ev) {
switch(ev.getAction()){
case MotionEvent.ACTION_DOWN:
//先讓父類不攔截子類的事件
getParent().requestDisallowInterceptTouchEvent(true);
//獲取子類坐標
startX = (int) ev.getRawX();
startY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int endX= (int) ev.getRawX();
int endY = (int) ev.getRawY();
//開始分類作比較 左劃 右劃 下劃
if(Math.abs(endX-startX)>Math.abs(endY-startY)){
//左右劃
if(endX>startX){
//右劃 第一個頁簽 就攔截
if(getCurrentItem()==0){
getParent().requestDisallowInterceptTouchEvent(false);
}
}
else{//左劃
if(getCurrentItem()==getAdapter().getCount()-1){
getParent().requestDisallowInterceptTouchEvent(false); }
}
}else{
//下劃 攔截
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
到此我們就管理好了這個復雜的事件攔截家族
- 傳遞
我們在開頭的時候,說到mActivity是充當當前上下文的作用,除此之外我們還可以拿它來傳遞對象。比如我們要在后面的頁簽類里,寫個函數控制sliding menu的拖出與隱藏,我們前面說過這個函數是toggle。但是我們必須獲得一個是sliding menu的對象,但是要獲得sliding menu對象,我們首先得獲得MainAcitvity的對象。于是這里就要我們靈活的進行對象的傳遞:
對于數據的傳遞,每個項目都有可能錯綜復雜,在android studio里有個很方便的快捷鍵,幫助我們找到哪些地方用到了這些數據。private void toggleSlidingMenu() { //獲得slidingmenu對象 MainActivity mainActivity= (MainActivity) mActiviy; SlidingMenu slidingMenu = mainActivity.getSlidingMenu(); slidingMenu.toggle(); //切換狀態 }
選中要查找的目標,按住alt+F7(find usages):

這個時候我們還可以右鍵:

跳轉到引用的頁面,迅速幫助我們定位,這樣我們就不會暈頭轉向了。
以上就是我重點要講的,如有不足請多多包涵,也希望大家可以支持我,謝謝!
文/Jian_LAM(簡書)