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

jamlee 9年前發布 | 41K 次閱讀 Android Android開發 移動開發 Github

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

層級的劃分

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

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

包名 用途
Base 包含一些基類
domain 存放一些javabean的類
Fragment 顧名思義存放fragment的類
Global 存放網絡的全局類
Utils 工具類
View 改寫后的布局類

它們分工明確,內容一目了然。每個人的習慣都不盡相同,找到自己熟悉的命名方式,結構明了就好。

  • 命名方式
    關于命名的規范
    1.對于類名我們使用:UpperCamelCase風格編寫

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


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

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

這兩個都是駝峰命名,一個是大駝峰(每個單詞的首字母大寫),一個是小駝峰(除第一個單詞以外的單詞首字母大寫)。這種方式命名美觀,簡潔。我縱觀了一下整個項目代碼,發現有些時候不是按照上面的規則,而是例如fragment_left_menu這樣的下劃線命名,所以其實并沒有唯一的標準,只是可以的話盡量這樣命名我們的代碼。

  • 基類的創建
    顧名思義,基類,即為初始類。如果兩個以上的類同時擁有相似的特征,我們就把它抽取出來單獨形成一個類。就好比我們在一個類的兩個不同的函數中,需要使用同一個參數,這時我們也會習慣性的把它變成全局的變量供兩個函數使用。

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

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

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

標題欄大家很熟悉,ImageButton和TextView,下面我們不知道是什么,就用一個幀布局Framelayout代替。以后如果要添加一個不同的布局,它有一個方法addview()便搞定了

上面的代碼,只是一個模板,給大家提供思路,我們需要初始化界面,初始化數據,mMainActivity的作用除了在Inflate函數里充當上下文的參數(應該改為public類型),其實在對象的流動傳遞里也有很大的作用,后面再敘述。除此之外如果你有必要,還可以來個類的初始化public BaseTab(){},又或是把view抽取出來當作公共參數,又或者你必須規定子類實現某個方法,某個函數,那就把整個基類寫成抽象類。

開源項目的使用

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

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


我們隨便百度,都可以搜索到一些很熱門的,很好用的開源項目。當然還是像上一篇說的,你可以直接在github下載下來,然后關聯到你的項目,具體做法上一篇已經講了,現在我就假設你已經導入好了。

  • Sliding Menu
    因為使用過程中,有牽扯到Fragment,所以就一起講了。
    sliding menu顧名思義側邊欄,最直觀的如圖片所示(網絡圖):

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

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 設置拖動的位置 有全屏 不設置 左邊 右邊等

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

方法 用途
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,現在也被強迫著寫了。最后附贈一個思維導圖幫助大家理解它:

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

  • 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,比較直觀的如圖所示(網絡圖):

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

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

如圖,我們先把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
有時我們翻閱圖片,為了有個指示的效果:

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


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

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

我們看到里面有自定義控件的屬性app,所以首先當然把命名空間給引入,在頭文件的地方添加


xmlns:app="http://schemas.android.com/apk/res-auto"
接著初始化,然后綁定viewpager,和上面一樣。它有兩個方法值得提一下, setSnap()填入(true,false)表示點是否跳躍,也就是我們上方展示圖片的效果。onPageSelected()選擇初始停留的頁面。

  • 事件的攔截機制
    為了便于理解,我畫了一個思維導圖

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

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

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


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

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

我們常用到事件攔截,是在重寫一個屬于我們自己的自定義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的對象。于是這里就要我們靈活的進行對象的傳遞:
    private void toggleSlidingMenu() {   
    //獲得slidingmenu對象    
    MainActivity mainActivity= (MainActivity) mActiviy;    
    SlidingMenu slidingMenu = mainActivity.getSlidingMenu();    
    slidingMenu.toggle();
    //切換狀態
    }
    對于數據的傳遞,每個項目都有可能錯綜復雜,在android studio里有個很方便的快捷鍵,幫助我們找到哪些地方用到了這些數據。
    選中要查找的目標,按住alt+F7(find usages):

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

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

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

跳轉到引用的頁面,迅速幫助我們定位,這樣我們就不會暈頭轉向了。

以上就是我重點要講的,如有不足請多多包涵,也希望大家可以支持我,謝謝!


 

文/Jian_LAM(簡書)
 

 本文由用戶 jamlee 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!