亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android下拉刷新框架實(shí)現(xiàn)代碼實(shí)例

 更新時(shí)間:2016年11月05日 10:42:26   作者:leehong2005  
這篇文章主要介紹了Android下拉刷新框架實(shí)現(xiàn)代碼實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

前段時(shí)間項(xiàng)目中用到了下拉刷新功能,之前在網(wǎng)上也找到過(guò)類(lèi)似的demo,但這些demo的質(zhì)量參差不齊,用戶(hù)體驗(yàn)也不好,接口設(shè)計(jì)也不行。最張沒(méi)辦法,終于忍不了了,自己就寫(xiě)了一個(gè)下拉刷新的框架,這個(gè)框架是一個(gè)通用的框架,效果和設(shè)計(jì)感覺(jué)都還不錯(cuò),現(xiàn)在分享給各位看官。

一. 關(guān)于下拉刷新

下拉刷新這種用戶(hù)交互最早由twitter創(chuàng)始人洛倫•布里切特(Loren Brichter)發(fā)明,有理論認(rèn)為,下拉刷新是一種適用于按照從新到舊的時(shí)間順序排列feeds的應(yīng)用,在這種應(yīng)用場(chǎng)景中看完舊的內(nèi)容時(shí),用戶(hù)會(huì)很自然地下拉查找更新的內(nèi)容,因此下拉刷新就顯得非常合理。大家可以參考這篇文章:有趣的下拉刷新,下面我貼出一個(gè)有趣的下拉刷新的案例。

圖一、有趣的下拉刷新案例(一)
圖二、有趣的下拉刷新案例(二)

二. 實(shí)現(xiàn)原理

上面這些例子,外觀做得再好看,他的本質(zhì)上都一樣,那就是一個(gè)下拉刷新控件通常由以下幾部分組成:

【1】Header

Header通常有下拉箭頭,文字,進(jìn)度條等元素,根據(jù)下拉的距離來(lái)改變它的狀態(tài),從而顯示不同的樣式

【2】Content

這部分是內(nèi)容區(qū)域,網(wǎng)上有很多例子都是直接在ListView里面添加Header,但這就有局限性,因?yàn)楹枚嗲闆r下并不一定是用ListView來(lái)顯示數(shù)據(jù)。我們把要顯示內(nèi)容的View放置在我們的一個(gè)容器中,如果你想實(shí)現(xiàn)一個(gè)用ListView顯示數(shù)據(jù)的下拉刷新,你需要?jiǎng)?chuàng)建一個(gè)ListView旋轉(zhuǎn)到我的容器中。我們處理這個(gè)容器的事件(down, move, up),如果向下拉,則把整個(gè)布局向下滑動(dòng),從而把header顯示出來(lái)。

【3】Footer

Footer可以用來(lái)顯示向上拉的箭頭,自動(dòng)加載更多的進(jìn)度條等。

以上三部分總結(jié)的說(shuō)來(lái),就是如下圖所示的這種布局結(jié)構(gòu):

圖三,下拉刷新的布局結(jié)構(gòu)

關(guān)于上圖,需要說(shuō)明幾點(diǎn):

1、這個(gè)布局?jǐn)U展于LinearLayout,垂直排列

2、從上到下的順序是:Header, Content, Footer

3、Content填充滿(mǎn)父控件,通過(guò)設(shè)置top, bottom的padding來(lái)使Header和Footer不可見(jiàn),也就是讓它超出屏幕外

4、下拉時(shí),調(diào)用scrollTo方法來(lái)將整個(gè)布局向下滑動(dòng),從而把Header顯示出來(lái),上拉正好與下拉相反。

5、派生類(lèi)需要實(shí)現(xiàn)的是:將Content View填充到父容器中,比如,如果你要使用的話,那么你需要把ListView, ScrollView, WebView等添加到容器中。

6、上圖中的紅色區(qū)域就是屏的大小(嚴(yán)格來(lái)說(shuō),這里說(shuō)屏幕大小并不準(zhǔn)確,應(yīng)該說(shuō)成內(nèi)容區(qū)域更加準(zhǔn)確)

三. 具體實(shí)現(xiàn)

明白了實(shí)現(xiàn)原理與過(guò)程,我們嘗試來(lái)具體實(shí)現(xiàn),首先,為了以后更好地?cái)U(kuò)展,設(shè)計(jì)更加合理,我們把下拉刷新的功能抽象成一個(gè)接口:
1、IPullToRefresh<T extends View>

它具體的定義方法如下:

public interface IPullToRefresh<T extends View> { 
  public void setPullRefreshEnabled(boolean pullRefreshEnabled); 
  public void setPullLoadEnabled(boolean pullLoadEnabled); 
  public void setScrollLoadEnabled(boolean scrollLoadEnabled); 
  public boolean isPullRefreshEnabled(); 
  public boolean isPullLoadEnabled(); 
  public boolean isScrollLoadEnabled(); 
  public void setOnRefreshListener(OnRefreshListener<T> refreshListener); 
  public void onPullDownRefreshComplete(); 
  public void onPullUpRefreshComplete(); 
  public T getRefreshableView(); 
  public LoadingLayout getHeaderLoadingLayout(); 
  public LoadingLayout getFooterLoadingLayout(); 
  public void setLastUpdatedLabel(CharSequence label); 
} 

這個(gè)接口是一個(gè)泛型的,它接受View的派生類(lèi),因?yàn)橐诺轿覀兊娜萜髦械牟痪褪且粋€(gè)View嗎?

2、PullToRefreshBase<T extends View>

這個(gè)類(lèi)實(shí)現(xiàn)了IPullToRefresh接口,它是從LinearLayout繼承過(guò)來(lái),作為下拉刷新的一個(gè)抽象基類(lèi),如果你想實(shí)現(xiàn)ListView的下拉刷新,只需要擴(kuò)展這個(gè)類(lèi),實(shí)現(xiàn)一些必要的方法就可以了。這個(gè)類(lèi)的職責(zé)主要有以下幾點(diǎn):

  • 處理onInterceptTouchEvent()和onTouchEvent()中的事件:當(dāng)內(nèi)容的View(比如ListView)正如處于最頂部,此時(shí)再向下拉,我們必須截?cái)嗍录?,然后move事件就會(huì)把后續(xù)的事件傳遞到onTouchEvent()方法中,然后再在這個(gè)方法中,我們根據(jù)move的距離再進(jìn)行scroll整個(gè)View。
  • 負(fù)責(zé)創(chuàng)建Header、Footer和Content View:在構(gòu)造方法中調(diào)用方法去創(chuàng)建這三個(gè)部分的View,派生類(lèi)可以重寫(xiě)這些方法,以提供不同式樣的Header和Footer,它會(huì)調(diào)用createHeaderLoadingLayout和createFooterLoadingLayout方法來(lái)創(chuàng)建Header和Footer創(chuàng)建Content View的方法是一個(gè)抽象方法,必須讓派生類(lèi)來(lái)實(shí)現(xiàn),返回一個(gè)非null的View,然后容器再把這個(gè)View添加到自己里面。
  • 設(shè)置各種狀態(tài):這里面有很多狀態(tài),如下拉、上拉、刷新、加載中、釋放等,它會(huì)根據(jù)用戶(hù)拉動(dòng)的距離來(lái)更改狀態(tài),狀態(tài)的改變,它也會(huì)把Header和Footer的狀態(tài)改變,然后Header和Footer會(huì)根據(jù)狀態(tài)去顯示相應(yīng)的界面式樣。

3、PullToRefreshBase<T extends View>繼承關(guān)系

這里我實(shí)現(xiàn)了三個(gè)下拉刷新的派生類(lèi),分別是ListView、ScrollView、WebView三個(gè),它們的繼承關(guān)系如下:

圖四、PullToRefreshBase類(lèi)的繼承關(guān)系

關(guān)于PullToRefreshBase類(lèi)及其派和類(lèi),有幾點(diǎn)需要說(shuō)明:

對(duì)于ListView,ScrollView,WebView這三種情況,他們是否滑動(dòng)到最頂部或是最底部的實(shí)現(xiàn)是不一樣的,所以,在PullToRefreshBase類(lèi)中需要調(diào)用兩個(gè)抽象方法來(lái)判斷當(dāng)前的位置是否在頂部或底部,而其派生類(lèi)必須要實(shí)現(xiàn)這兩個(gè)方法。比如對(duì)于ListView,它滑動(dòng)到最頂部的條件就是第一個(gè)child完全可見(jiàn)并且first postion是0。這兩個(gè)抽象方法是:

/** 
 * 判斷刷新的View是否滑動(dòng)到頂部 
 * 
 * @return true表示已經(jīng)滑動(dòng)到頂部,否則false 
 */ 
protected abstract boolean isReadyForPullDown(); 
 
/** 
 * 判斷刷新的View是否滑動(dòng)到底 
 * 
 * @return true表示已經(jīng)滑動(dòng)到底部,否則false 
 */ 
protected abstract boolean isReadyForPullUp(); 

創(chuàng)建可下拉刷新的View(也就是content view)的抽象方法是

/** 
 * 創(chuàng)建可以刷新的View 
 * 
 * @param context context 
 * @param attrs 屬性 
 * @return View 
 */ 
protected abstract T createRefreshableView(Context context, AttributeSet attrs); 

4、LoadingLayout

LoadingLayout是刷新Layout的一個(gè)抽象,它是一個(gè)抽象基類(lèi)。Header和Footer都擴(kuò)展于這個(gè)類(lèi)。這類(lèi)抽象類(lèi),提供了兩個(gè)抽象方法:

getContentSize

這個(gè)方法返回當(dāng)前這個(gè)刷新Layout的大小,通常返回的是布局的高度,為了以后可以擴(kuò)展為水平拉動(dòng),所以方法名字沒(méi)有取成getLayoutHeight()之類(lèi)的,這個(gè)返回值,將會(huì)作為松手后是否可以刷新的臨界值,如果下拉的偏移值大于這個(gè)值,就認(rèn)為可以刷新,否則不刷新,這個(gè)方法必須由派生類(lèi)來(lái)實(shí)現(xiàn)。

setState

這個(gè)方法用來(lái)設(shè)置當(dāng)前刷新Layout的狀態(tài),PullToRefreshBase類(lèi)會(huì)調(diào)用這個(gè)方法,當(dāng)進(jìn)入下拉,松手等動(dòng)作時(shí),都會(huì)調(diào)用這個(gè)方法,派生類(lèi)里面只需要根據(jù)這些狀態(tài)實(shí)現(xiàn)不同的界面顯示,如下拉狀態(tài)時(shí),就顯示出箭頭,刷新?tīng)顟B(tài)時(shí),就顯示loading的圖標(biāo)。

可能的狀態(tài)值有:RESET, PULL_TO_REFRESH, RELEASE_TO_REFRESH, REFRESHING, NO_MORE_DATA

LoadingLayout及其派生類(lèi)的繼承關(guān)系如下圖所示:

圖五、LoadingLayout及其派生類(lèi)的類(lèi)圖

我們可以隨意地制定自己的Header和Footer,我們也可以實(shí)現(xiàn)如圖一和圖二中顯示的各種下拉刷新案例中的Header和Footer,只要重寫(xiě)上述兩個(gè)方法getContentSize()和setState()就行了。HeaderLoadingLayout,它默認(rèn)是顯示箭頭式樣的布局,而RotateLoadingLayout則是顯示一個(gè)旋轉(zhuǎn)圖標(biāo)的式樣。

5、事件處理

我們必須重寫(xiě)PullToRefreshBase類(lèi)的兩個(gè)事件相關(guān)的方法onInterceptTouchEvent()和onTouchEvent()方法。由于ListView,ScrollView,WebView它們是放到PullToRefreshBase內(nèi)部的,所在事件先是傳遞到PullToRefreshBase#onInterceptTouchEvent()方法中,所以我們應(yīng)該在這個(gè)方法中去處理ACTION_MOVE事件,判斷如果當(dāng)前ListView,ScrollView,WebView是否在最頂部或最底部,如果是,則開(kāi)始截?cái)嗍录?,一旦事件被截?cái)?,后續(xù)的事件就會(huì)傳遞到PullToRefreshBase#onInterceptTouchEvent()方法中,我們?cè)僭贏CTION_MOVE事件中去移動(dòng)整個(gè)布局,從而實(shí)現(xiàn)下拉或上拉動(dòng)作。

6、滾動(dòng)布局(scrollTo)

如圖三的布局結(jié)構(gòu)可知,默認(rèn)情況下Header和Footer是放置在Content View的最上面和最下面,通過(guò)設(shè)置padding來(lái)讓他跑到屏幕外面去了,如果我們將整個(gè)布局向下滾動(dòng)(scrollTo)一定距離,那么Header就會(huì)被顯示出來(lái),基于這種情況,所以在我的實(shí)現(xiàn)中,最終我是調(diào)用scrollTo來(lái)實(shí)現(xiàn)下拉動(dòng)作的。

總的說(shuō)來(lái),實(shí)現(xiàn)的重要的點(diǎn)就這些,具體的一些細(xì)節(jié)在實(shí)現(xiàn)在會(huì)碰到很多,可以參考代碼。

四. 如何使用

使用下拉刷新的代碼如下

@Override 
  public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
     
    mPullListView = new PullToRefreshListView(this); 
    setContentView(mPullListView); 
     
    // 上拉加載不可用 
    mPullListView.setPullLoadEnabled(false); 
    // 滾動(dòng)到底自動(dòng)加載可用 
    mPullListView.setScrollLoadEnabled(true); 
     
    mCurIndex = mLoadDataCount; 
    mListItems = new LinkedList<String>(); 
    mListItems.addAll(Arrays.asList(mStrings).subList(0, mCurIndex)); 
    mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems); 
     
    // 得到實(shí)際的ListView 
    mListView = mPullListView.getRefreshableView(); 
    // 綁定數(shù)據(jù) 
    mListView.setAdapter(mAdapter);     
    // 設(shè)置下拉刷新的listener 
    mPullListView.setOnRefreshListener(new OnRefreshListener<ListView>() { 
      @Override 
      public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) { 
        mIsStart = true; 
        new GetDataTask().execute(); 
      } 
 
      @Override 
      public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) { 
        mIsStart = false; 
        new GetDataTask().execute(); 
      } 
    }); 
    setLastUpdateTime(); 
     
    // 自動(dòng)刷新 
    mPullListView.doPullRefreshing(true, 500); 
  } 

這是初始化一個(gè)下拉刷新的布局,并且調(diào)用setContentView來(lái)設(shè)置到Activity中。

在下拉刷新完成后,我們可以調(diào)用onPullDownRefreshComplete()和onPullUpRefreshComplete()方法來(lái)停止刷新和加載

五. 運(yùn)行效果
這里列出了demo的運(yùn)行效果圖。

圖六、ListView下拉刷新,注意Header和Footer的樣式

六. Bug修復(fù)

已知bug修復(fù)情況如下,發(fā)現(xiàn)了代碼bug的看官也可以給我反饋,謝謝~~~

1,對(duì)于ListView的下拉刷新,當(dāng)啟用滾動(dòng)到底自動(dòng)加載時(shí),如果footer由隱藏變?yōu)轱@示時(shí),出現(xiàn)顯示異常的情況
這個(gè)問(wèn)題已經(jīng)修復(fù)了,修正的代碼如下:

PullToRefreshListView#setScrollLoadEnabled方法,修正后的代碼如下:
@Override 
public void setScrollLoadEnabled(boolean scrollLoadEnabled) { 
  if (isScrollLoadEnabled() == scrollLoadEnabled) { 
    return; 
  } 
   
  super.setScrollLoadEnabled(scrollLoadEnabled); 
   
  if (scrollLoadEnabled) { 
    // 設(shè)置Footer 
    if (null == mLoadMoreFooterLayout) { 
      mLoadMoreFooterLayout = new FooterLoadingLayout(getContext()); 
      mListView.addFooterView(mLoadMoreFooterLayout, null, false); 
    } 
     
    mLoadMoreFooterLayout.show(true); 
  } else { 
    if (null != mLoadMoreFooterLayout) { 
      mLoadMoreFooterLayout.show(false); 
    } 
  } 
} 

LoadingLayout#show方法,修正后的代碼如下:

/** 
 * 顯示或隱藏這個(gè)布局 
 * 
 * @param show flag 
 */ 
public void show(boolean show) { 
  // If is showing, do nothing. 
  if (show == (View.VISIBLE == getVisibility())) { 
    return; 
  } 
   
  ViewGroup.LayoutParams params = mContainer.getLayoutParams(); 
  if (null != params) { 
    if (show) { 
      params.height = ViewGroup.LayoutParams.WRAP_CONTENT; 
    } else { 
      params.height = 0; 
    } 
     
    requestLayout(); 
    setVisibility(show ? View.VISIBLE : View.INVISIBLE); 
  } 
} 

在更改LayoutParameter后,調(diào)用requestLayout()方法。

圖片旋轉(zhuǎn)兼容2.x系統(tǒng)

我之前想的是這個(gè)只需要兼容3.x以上的系統(tǒng),但發(fā)現(xiàn)有很多網(wǎng)友在使用過(guò)程中遇到過(guò)兼容性問(wèn)題,這次抽空將這個(gè)兼容性一并實(shí)現(xiàn)了。

onPull的修改如下:
    

  @Override 
public void onPull(float scale) { 
  if (null == mRotationHelper) { 
    mRotationHelper = new ImageViewRotationHelper(mArrowImageView); 
  } 
   
  float angle = scale * 180f; // SUPPRESS CHECKSTYLE 
  mRotationHelper.setRotation(angle); 
} 

ImageViewRotationHelper主要的作用就是實(shí)現(xiàn)了ImageView的旋轉(zhuǎn)功能,內(nèi)部作了版本的區(qū)分,實(shí)現(xiàn)代碼如下:

/** 
   * The image view rotation helper 
   * 
   * @author lihong06 
   * @since 2014-5-2 
   */ 
  static class ImageViewRotationHelper { 
    /** The imageview */ 
    private final ImageView mImageView; 
    /** The matrix */ 
    private Matrix mMatrix; 
    /** Pivot X */ 
    private float mRotationPivotX; 
    /** Pivot Y */ 
    private float mRotationPivotY; 
     
    /** 
     * The constructor method. 
     * 
     * @param imageView the image view 
     */ 
    public ImageViewRotationHelper(ImageView imageView) { 
      mImageView = imageView; 
    } 
     
    /** 
     * Sets the degrees that the view is rotated around the pivot point. Increasing values 
     * result in clockwise rotation. 
     * 
     * @param rotation The degrees of rotation. 
     * 
     * @see #getRotation() 
     * @see #getPivotX() 
     * @see #getPivotY() 
     * @see #setRotationX(float) 
     * @see #setRotationY(float) 
     * 
     * @attr ref android.R.styleable#View_rotation 
     */ 
    public void setRotation(float rotation) { 
      if (APIUtils.hasHoneycomb()) { 
        mImageView.setRotation(rotation); 
      } else { 
        if (null == mMatrix) { 
          mMatrix = new Matrix(); 
           
          // 計(jì)算旋轉(zhuǎn)的中心點(diǎn) 
          Drawable imageDrawable = mImageView.getDrawable(); 
          if (null != imageDrawable) { 
            mRotationPivotX = Math.round(imageDrawable.getIntrinsicWidth() / 2f); 
            mRotationPivotY = Math.round(imageDrawable.getIntrinsicHeight() / 2f); 
          } 
        } 
         
        mMatrix.setRotate(rotation, mRotationPivotX, mRotationPivotY); 
        mImageView.setImageMatrix(mMatrix); 
      } 
    } 
  } 

最核心的就是,如果在2.x的版本上,旋轉(zhuǎn)ImageView使用Matrix。

PullToRefreshBase構(gòu)造方法兼容2.x

在三個(gè)參數(shù)的構(gòu)造方法聲明如下標(biāo)注:

@SuppressLint("NewApi")

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論