Android----騰訊微博(下拉刷新+點擊更多)listview的實現 (android)


 

Lance老師UI系列教程第二課->騰訊微博(下拉刷新+點擊更多)listview的實現 (android)

分類: android UI教程 android   982人閱讀   評論(1)   收藏   舉報
                                                     UI系列教程第二課:騰訊微博下拉刷新listview

 

今天藍老師要講的是關於騰訊微博下拉刷新listview的實現。如上圖所示,這是一個用戶體驗非常好的操作方式,在騰訊微博,新浪微博等等應用中相當常見,相信很多童鞋以后做應用也都會碰到。關於其實現原理,其實網上已有很多demo,鼻祖要數GitHubJ兄了,J兄的做法主要是重寫 OnScroll,根據各種狀態修改headviewsize;還有種則是在OnTouchEvent里做文章,同樣是對headview設置padding改變其大小;可能對於多數人后者比較容易理解,藍老師就第二種實現方式展開本節課程,示例demo是藍老師重構代碼后同時加以擴展添加底部點擊獲取更多的操作方式,完成騰訊微博的listview實現效果,廢話不多說,先上效果圖:

 

大致跟童鞋們說下思路,實現原理是給listview加上一個headview,然后headview有四種狀態

[java]   view plain copy
  1. public interface IListViewState  
  2. {  
  3.     int LVS_NORMAL = 0;                 //  普通狀態  
  4.     int LVS_PULL_REFRESH = 1;           //  下拉刷新狀態  
  5.     int LVS_RELEASE_REFRESH = 2;        //  松開刷新狀態  
  6.     int LVS_LOADING = 3;                //  加載狀態  
  7. }  

 

所以關鍵在於MotionEvent.ACTION_MOVE時根據手勢情況切換各種headview狀態

MotionEvent.ACTION_UP時根據當前狀態確定是該進入加載狀態還是普通狀態

OK,先貼上touchevent代碼:

[java]   view plain copy
  1. @Override  
  2.     public boolean onTouchEvent(MotionEvent ev) {  
  3.         // TODO Auto-generated method stub  
  4.                   
  5.         if (mOnRefreshListener != null)  
  6.         {  
  7.             switch (ev.getAction()) {  
  8.             case MotionEvent.ACTION_DOWN:         
  9.                 doActionDown(ev);  
  10.                 break;  
  11.             case MotionEvent.ACTION_MOVE:  
  12.                 doActionMove(ev);  
  13.                 break;  
  14.             case MotionEvent.ACTION_UP:  
  15.                 doActionUp(ev);  
  16.                 break;  
  17.             default:  
  18.                 break;  
  19.             }     
  20.         }  
  21.           
  22.                   
  23.         return super.onTouchEvent(ev);  
  24.     }  
  25.       
  26.     private void doActionDown(MotionEvent ev)  
  27.     {  
  28.         if(mIsRecord == false && mFirstItemIndex == 0)  
  29.         {  
  30.             mStartY = (int) ev.getY();  
  31.             mIsRecord = true;  
  32.         }  
  33.     }  
  34.       
  35.     private void doActionMove(MotionEvent ev)  
  36.     {  
  37.         mMoveY = (int) ev.getY();  
  38.           
  39.         if(mIsRecord == false && mFirstItemIndex == 0)  
  40.         {  
  41.             mStartY = (int) ev.getY();  
  42.             mIsRecord = true;  
  43.         }  
  44.           
  45.         if (mIsRecord == false || mViewState == IListViewState.LVS_LOADING)  
  46.         {  
  47.             return ;  
  48.         }     
  49.           
  50.         int offset = (mMoveY - mStartY) / RATIO;      
  51.           
  52.         switch(mViewState)  
  53.         {  
  54.             case IListViewState.LVS_NORMAL:  
  55.             {  
  56.                 if (offset > 0)  
  57.                 {         
  58.                     mHeadView.setPadding(0, offset - mHeadContentHeight, 00);  
  59.                     switchViewState(IListViewState.LVS_PULL_REFRESH);  
  60.                 }  
  61.             }  
  62.                 break;  
  63.             case IListViewState.LVS_PULL_REFRESH:  
  64.             {  
  65.                 setSelection(0);  
  66.                 mHeadView.setPadding(0, offset - mHeadContentHeight, 00);  
  67.                 if (offset < 0)  
  68.                 {  
  69.                     switchViewState(IListViewState.LVS_NORMAL);  
  70.                 }else if (offset > mHeadContentHeight)  
  71.                 {  
  72.                     switchViewState(IListViewState.LVS_RELEASE_REFRESH);  
  73.                 }  
  74.             }  
  75.                 break;  
  76.             case IListViewState.LVS_RELEASE_REFRESH:  
  77.             {  
  78.                 setSelection(0);  
  79.                 mHeadView.setPadding(0, offset - mHeadContentHeight, 00);  
  80.                 if (offset >= 0 && offset <= mHeadContentHeight)  
  81.                 {  
  82.                     mBack = true;  
  83.                     switchViewState(IListViewState.LVS_PULL_REFRESH);  
  84.                 }else if (offset < 0)  
  85.                 {  
  86.                     switchViewState(IListViewState.LVS_NORMAL);  
  87.                 }else{  
  88.                   
  89.                 }  
  90.                   
  91.             }  
  92.                 break;  
  93.             default:  
  94.                 return;  
  95.         };        
  96.           
  97.     }  
  98.       
  99.     private void doActionUp(MotionEvent ev)  
  100.     {  
  101.         mIsRecord = false;  
  102.         mBack = false;  
  103.           
  104.         if (mViewState == IListViewState.LVS_LOADING)  
  105.         {  
  106.             return ;  
  107.         }  
  108.           
  109.         switch(mViewState)  
  110.         {  
  111.         case IListViewState.LVS_NORMAL:  
  112.           
  113.             break;  
  114.         case IListViewState.LVS_PULL_REFRESH:  
  115.             mHeadView.setPadding(0, -1 * mHeadContentHeight, 00);  
  116.             switchViewState(IListViewState.LVS_NORMAL);  
  117.             break;  
  118.         case IListViewState.LVS_RELEASE_REFRESH:  
  119.             mHeadView.setPadding(0000);  
  120.             switchViewState(IListViewState.LVS_LOADING);  
  121.             onRefresh();  
  122.             break;  
  123.         }     
  124.           
  125.     }  

 

看下面這個代碼段

[java]   view plain copy
  1. if(mIsRecord == false && mFirstItemIndex == 0)  
  2.     {  
  3.         mStartY = (int) ev.getY();  
  4.         mIsRecord = true;  
  5.     }  

其實就是在一次完整的手勢過程中,當headview第一次出現時,記錄當前觸摸點Y坐標值,而在之后的MotionEvent.ACTION_MOVE過程中,我們當前的觸摸點與原始記錄值會有一個偏移量offset

int offset = (mMoveY - mStartY) / RATIO;

這個RATIO值姑且不管它,把它當成一先,假設headview原始高度為size,那么offsetsize就有以下三種關系:

Offset < 0                                   headview不可見                                    NORMAL狀態

Offset > 0 && offset < size         headview可見且不超過原始高度                      PULL_REFRESH狀態

Offset > size                              headview可見且超過原始高度                         RELEASE_REFRESH

 

我們要做的就是根據當前狀態再結合offset值來改變headview的尺寸以及確定是否需要切換headview狀態

Switch的這段代碼已經描述的很明朗了

 

[java]   view plain copy
  1. switch(mViewState)  
  2.         {  
  3.             case IListViewState.LVS_NORMAL:  
  4.             {  
  5.                 if (offset > 0)  
  6.                 {         
  7.                     mHeadView.setPadding(0, offset - mHeadContentHeight, 00);  
  8.                     switchViewState(IListViewState.LVS_PULL_REFRESH);  
  9.                 }  
  10.             }  
  11.                 break;  
  12.             case IListViewState.LVS_PULL_REFRESH:  
  13.             {  
  14.                 setSelection(0);  
  15.                 mHeadView.setPadding(0, offset - mHeadContentHeight, 00);  
  16.                 if (offset < 0)  
  17.                 {  
  18.                     switchViewState(IListViewState.LVS_NORMAL);  
  19.                 }else if (offset > mHeadContentHeight)  
  20.                 {  
  21.                     switchViewState(IListViewState.LVS_RELEASE_REFRESH);  
  22.                 }  
  23.             }  
  24.                 break;  
  25.             case IListViewState.LVS_RELEASE_REFRESH:  
  26.             {  
  27.                 setSelection(0);  
  28.                 mHeadView.setPadding(0, offset - mHeadContentHeight, 00);  
  29.                 if (offset >= 0 && offset <= mHeadContentHeight)  
  30.                 {  
  31.                     mBack = true;  
  32.                     switchViewState(IListViewState.LVS_PULL_REFRESH);  
  33.                 }else if (offset < 0)  
  34.                 {  
  35.                     switchViewState(IListViewState.LVS_NORMAL);  
  36.                 }else{  
  37.                   
  38.                 }  
  39.                   
  40.             }  
  41.                 break;  
  42.             default:  
  43.                 return;  
  44.         };        

 

部分童鞋可能會好奇,為神馬在PULL_REFRESH以及RELEASE_REFRESH狀態為何要設置setselection(0)

主要是因為當列表長度超過屏幕時候,在往上推動時候會感覺好像是推動速度比下拉速度快,實際上是因為,當列表高度超過屏幕時候,我們在往上推動時候,除了執行我們自己的方法之外,列表也在向下滑動,這樣就產生了推動比下拉速度快的錯覺,也導致了headview部分視圖被滾出屏外而不會被完全顯示,所以需要在 PULL_REFRESH和RELEASE_REFRESH狀態setSelection(0)headview按當前尺寸完全顯示,不信大家把它注釋掉試一試就知道了。

那么在switchViewState里就只是更改一些顯示元素而已了

[java]   view plain copy
  1. private void switchViewState(int state)  
  2.     {     
  3.           
  4.           
  5.         switch(state)  
  6.         {  
  7.             case IListViewState.LVS_NORMAL:  
  8.             {  
  9.                 Log.e("!!!!!!!!!!!""convert to IListViewState.LVS_NORMAL");  
  10.                 mArrowImageView.clearAnimation();  
  11.                 mArrowImageView.setImageResource(R.drawable.arrow);  
  12.             }  
  13.                 break;  
  14.             case IListViewState.LVS_PULL_REFRESH:  
  15.             {  
  16.                 Log.e("!!!!!!!!!!!""convert to IListViewState.LVS_PULL_REFRESH");  
  17.                 mHeadProgressBar.setVisibility(View.GONE);  
  18.                 mArrowImageView.setVisibility(View.VISIBLE);  
  19.                 mRefreshTextview.setText("下拉可以刷新");  
  20.                 mArrowImageView.clearAnimation();  
  21.   
  22.                 // 是由RELEASE_To_REFRESH狀態轉變來的  
  23.                 if (mBack)  
  24.                 {  
  25.                     mBack = false;  
  26.                     mArrowImageView.clearAnimation();  
  27.                     mArrowImageView.startAnimation(reverseAnimation);  
  28.                 }  
  29.             }  
  30.                 break;  
  31.             case IListViewState.LVS_RELEASE_REFRESH:  
  32.             {  
  33.                 Log.e("!!!!!!!!!!!""convert to IListViewState.LVS_RELEASE_REFRESH");  
  34.                 mHeadProgressBar.setVisibility(View.GONE);  
  35.                 mArrowImageView.setVisibility(View.VISIBLE);  
  36.                 mRefreshTextview.setText("松開獲取更多");  
  37.                 mArrowImageView.clearAnimation();  
  38.                 mArrowImageView.startAnimation(animation);  
  39.             }  
  40.                 break;  
  41.             case IListViewState.LVS_LOADING:  
  42.             {  
  43.                 Log.e("!!!!!!!!!!!""convert to IListViewState.LVS_LOADING");  
  44.                 mHeadProgressBar.setVisibility(View.VISIBLE);  
  45.                 mArrowImageView.clearAnimation();  
  46.                 mArrowImageView.setVisibility(View.GONE);  
  47.                 mRefreshTextview.setText("載入中...");  
  48.             }  
  49.                 break;  
  50.                 default:  
  51.                     return;  
  52.         }     
  53.           
  54.         mViewState = state;  
  55.           
  56.     }  

再回到剛剛RATIO值的問題,其實就是為了更良好的用戶體驗,本例設置該值為2,也就意味着雖然移動了100距離,但實際headview尺寸值只改變了50,這樣就有一種橡皮筋的感覺

下面再看看activity里面的使用

[java]   view plain copy
  1. public void initData()  
  2.    {  
  3.   
  4.     data = new LinkedList<String>();  
  5.     String string = "";  
  6.     for(int i = 0; i < 6; i++)  
  7.     {  
  8.         string = "genius" + i;  
  9.         data.addFirst(string);  
  10.     }  
  11.       
  12.     adapter = new MyListViewAdapter(this, data);  
  13.   
  14.       
  15.       
  16.     mListView.setAdapter(adapter);  
  17.     mListView.setOnRefreshListener(this);  
  18.     mListView.setOnLoadMoreListener(this);  
  19.   
  20.    }  
[java]   view plain copy
  1. public void OnRefresh() {  
  2.         // TODO Auto-generated method stub  
  3.       
  4.         mRefreshAsynTask = new RefreshDataAsynTask();  
  5.         mRefreshAsynTask.execute(null);  
  6.     }  
[java]   view plain copy
  1. class RefreshDataAsynTask extends AsyncTask<Void , Void, Void>  
  2.     {  
  3.   
  4.         @Override  
  5.         protected Void doInBackground(Void... arg0) {  
  6.               
  7.               
  8.               
  9.             try {  
  10.                 Thread.sleep(2000);  
  11.             } catch (InterruptedException e) {  
  12.                 // TODO Auto-generated catch block  
  13.                 e.printStackTrace();  
  14.             }  
  15.               
  16.             index++;  
  17.             data.addFirst("genius" + index);  
  18.               
  19.               
  20.             return null;  
  21.         }  
  22.   
  23.         @Override  
  24.         protected void onPostExecute(Void result) {  
  25.             // TODO Auto-generated method stub  
  26.           
  27.             adapter.refreshData(data);  
  28.             mListView.onRefreshComplete();  
  29.         }  
  30.           
  31.           
  32.           
  33.     }  

加載完數據后更新adapter並刷新listview狀態就可以了

 

如果不需要下拉效果就把 mListView.setOnRefreshListener(this);屏蔽掉

如果不需要footview,就調用listview.removeFootView移除掉它

很智能有木有~

其它就沒啥好說的了,footview的處理比較簡單,童鞋們自個兒下代碼看吧

下面附上代碼工程鏈接:

http://download.csdn.net/detail/geniuseoe2012/4446532

 

溫馨提示:學而不思則罔,思而不學則殆,拿來主義固然要發揮,不過童鞋們也要有自己的思考,不然很難進步的哦

欲知更多Android-UI技巧,請關注窩的下一堂課,更多精彩盡在http://blog.csdn.net/geniuseoe2012


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM