国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android scrollToTop實現點擊回到頂部(兼容PullTorefreshScrollview)

2019-12-12 03:26:47
字體:
來源:轉載
供稿:網友

前言

最近因為項目組需求,特研究了一下“回到頂部”效果,即:頁面里有scrollview,內容很多,當滑動到頁面下面或者更深時,需要回到頂部,即可點擊出現的按鈕,省得回滑N久。我沒有搜,或許網上有很多這樣的例子,此文寫的不好的地方,望指點。

效果圖如下

實現方法

初一看是不是覺得很簡答?沒錯,當時我也是這樣想的頁面內容很長,就弄個scrollview,回到頂部按鈕需要固定在右下角,故大概的布局代碼:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tools="http://schemas.android.com/tools"  android:layout_width="match_parent"  android:layout_height="match_parent"  tools:context="com.znke.pulltorefresh_top.MainActivity">   <!--<com.znke.pulltorefresh_top.tool.ToTopScrollView .../>-->  <ScrollView   android:id="@+id/scrollView"   android:layout_width="match_parent"   android:layout_height="wrap_content"   android:layout_below="@+id/tv_top"   android:scrollbars="none">    <LinearLayout    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="vertical">     <TextView     android:layout_width="match_parent"     android:layout_height="50dp"     android:gravity="center"     android:text="111111111111"     android:textSize="20sp" />     ...........     <View     android:layout_width="match_parent"     android:layout_height="2dp"     android:background="#AAAAAA" />     <TextView     android:layout_width="match_parent"     android:layout_height="80dp"     android:gravity="center"     android:text="12000000000000"     android:textSize="20sp" />   </LinearLayout>  </ScrollView>   <com.znke.pulltorefresh_top.tool.ToTopImageView   android:id="@+id/imageView_to_top"   android:layout_width="50dp"   android:layout_height="50dp"   android:layout_alignParentBottom="true"   android:layout_alignParentRight="true"   android:layout_marginBottom="5dp"   android:layout_marginRight="5dp"   android:background="@drawable/to_top" /> </RelativeLayout> 

然后在activity中設置下面事件不就完了嘛?。?!

mScrollView.setOnScrollChangeListener(); 

很遺憾,報錯。alt+enter搞定錯誤不就完了嘛!很遺憾,運行繼續報錯,報空指針,神奇吧,找不出來。翻閱資料后發現,scrollview的onScrollChanged方法是受保護的。故按照網上的資料,自定義ScrollView類,暴露出onScrollChanged方法:

public class ToTopScrollView extends ScrollView {  private OnMyScrollListener onMyScrollListener;   public void setOnMyScrollListener(OnMyScrollListener onMyScrollListener) {   this.onMyScrollListener = onMyScrollListener;  }   ...   @Override  protected void onScrollChanged(int l, int t, int oldl, int oldt) {   super.onScrollChanged(l, t, oldl, oldt);   if(onMyScrollListener != null)    onMyScrollListener.onScrollChanged(l,t,oldl,oldt);  }   public interface OnMyScrollListener{   void onScrollChanged(int x, int y, int oldx, int oldy);  } } 

然后在activity中設置setOnMyScrollListener()方法,就可以監控scrollview的狀態了。剩下的就是自定義imageview的邏輯了。

可是,自定義ToTopImageView里面怎么弄呢?它需要有一個高度臨界值,與activity傳遞scrollview的scrollY值比較,來判定ToTopImageView是否顯示。代碼簡單,搞定。

只是這樣有個小問題,onScrollChanged方法是監控滾動狀態的,沒有說停止。如果在里面判斷超過臨界值就顯示與隱藏imageview,那么會一直設置imageview。這樣肯定不是最佳的方法。如果能在滾動停止時再判定是否需要顯示與隱藏imageview就好了。此時我還沒有想太多,動手簡單實現了剛才的分析。

實現后,感覺挺爽。然而在準備加到項目中時發現,我們項目用了PullToRefresh刷新代碼庫,也簡單,把自定義的Scrollview替換就可以了。運行,糟糕,沒有效果,然后調試,事件沒有處罰,可能是PullToRefresh庫把事件屏蔽了,咋辦?找onTouchListener監聽方法唄。

于是改用了測試:

mScrollView.setOnTouchListener() 

我去,沒有調傭,把pullToRefreshScrollView里面各種監聽方法都試遍了,沒用。他只提供了頂部和底部的上拉、下拉刷新監聽,毛用。

于是查看其源碼,發現把事件攔截了。而且pullToRefreshScrollView根本就不是scrollview,看源碼:

  @Override protected ScrollView createRefreshableView(Context context, AttributeSet attrs) {  ScrollView scrollView;  if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {   scrollView = new InternalScrollViewSDK9(context, attrs);  } else {   scrollView = new ScrollView(context, attrs);  }   scrollView.setId(R.id.scrollview);  return scrollView; } 

他里面提供了一個方法可以或得到Scrollview:

final ScrollView scrollView = mScrollView.getRefreshableView(); 

回想起了以前項目也這么用過,只是當時不明白這句話干啥的,白寫。

然后就用上面這種方法得到scrollview,再設置onscrollchanged方法監聽滑動。運行報錯,同樣無效。于是只能換onTouchListener監聽了。

但是這樣問題來了,我們只能監聽到手勢,即何時按下、移動和彈起。當快速滑動手指彈起后,scrollview還在滾動的,什么時候去拿到它的scrollY值呢?犯愁了。

此時不得不用最開始分析的方法,在自定義imageview里面定義線程,掃描當前scrollY和上一次保存的對比,不一樣即說明仍在滾動,一樣即表明scrollview滾動停止了。

什么時候開啟線程呢?在onTouch回調中down、move或者up時調用。

試想下:

如果在down中調用時,用戶只在scrollview上點擊或短距離滑動,imageview里面要不停地開啟線程?浪費資源。

如果在up中調用時,當用戶按著屏幕一口氣滑過臨界值,還不松手呢?還不顯示imageview嗎?也行,個人覺得不太好。
于是,我選擇在move中調用imageview地線程。有人會想,這樣會不會啟動N多個線程呢?move一直在移動呢。“在iamgeview判斷下線程的狀態即可,如果已經啟動了,就不啟動唄”。或許這么寫不太好,但我認為是實時的,用戶體驗好。

看代碼:

/**   * 獲取待監控的view對象   * 實時調起線程,監控是否scroll停止,來判斷是否需要顯示imageView   * @param targetView 需要監控的對象   */  public void tellMe(View targetView) {   if (targetView == null)    throw new IllegalArgumentException("please set targetView who to scrollTo");   if (this.targetView == null)    this.targetView = targetView;   if (!isStarting) {    new Thread(scanThread).start();   }  } 

此處注意,我偷懶了,沒有單獨設置方法傳遞需要滾動的scrollview,在此處引進來了。線程加了判斷。此處不要傳遞scrollview的scrollY值進來。比喻當你手指離開屏幕后,之前傳遞進來的scrollY就已經過時了,scrollview仍在滑動。在消息回調里面實時獲取再判斷

private class MyCallback implements Runnable {   @Override   public void run() {    /**     * 獲取實時的卷動值,不要傳遞scroll值給我     */    endScrollX = targetView.getScrollX();    int scrollY = targetView.getScrollY();    if (endScrollY != scrollY) {     endScrollY = scrollY;    } else {     if (endScrollY >= limitHeight) {      if (!thisStateVisible)       visible();     } else {      if (thisStateVisible)       gone();     }     /**      * 已判定,卷動停止,顯示或隱藏當前view已完成      * 退出監控scroll線程      */     clearCallBacks();    }   }  } 

由于是用線程來檢測scrollview的滾動狀態,我用了延時消息。此時又有另外一個潛在bug。在自定義imageview中創建了handler屬于主線程,子線程中需要發延時消息。如果延時消息發出后,activity退出了呢?反復這么弄呢?有人會說沒誰會這么無聊的。但這畢竟還是潛在的OOM。于是我簡單的做了線程控制和消息清除的代碼,過于簡單。感謝評論。

主要思路和邏輯都分析完了,使用起來很簡答,你不用關心自定義imageview里面的邏輯(除非你想改進)。

在activity中

private PullToRefreshScrollView mScrollView;  private ToTopImageView imageView_to_top;  @Override  protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.activity_pull_to_refresh_scroll_view);    imageView_to_top = (ToTopImageView) findViewById(R.id.imageView_to_top);   imageView_to_top.setLimitHeight(800);   mScrollView = (PullToRefreshScrollView) findViewById(R.id.scrollView);   final ScrollView scrollView = mScrollView.getRefreshableView();   //mScrollView.setOnTouchListener(); 無效   scrollView.setOnTouchListener(new View.OnTouchListener() {    @Override    public boolean onTouch(View v, MotionEvent event) {     switch (event.getAction()){      case MotionEvent.ACTION_MOVE:       imageView_to_top.tellMe(scrollView);       break;     }     return false;    }   });  }   @Override  protected void onDestroy() {   imageView_to_top.clearCallBacks();   super.onDestroy();  } 

頁面上,在你覺得合適的位置:

<com.znke.pulltorefresh_top.tool.ToTopImageView   android:id="@+id/imageView_to_top"   android:layout_width="50dp"   android:layout_height="50dp"   android:layout_alignParentBottom="true"   android:layout_alignParentRight="true"   android:layout_marginBottom="5dp"   android:layout_marginRight="5dp"   android:background="@drawable/to_top" /> 

完事。

總結

以上就是這篇文章的全部內容了,希望本文的內容對各位Android開發者們能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 巧家县| 大渡口区| 长泰县| 桑日县| 当阳市| 邓州市| 甘泉县| 保德县| 益阳市| 荥经县| 正蓝旗| 虹口区| 顺昌县| 通化市| 安丘市| 株洲县| 利津县| 霍山县| 乐亭县| 景宁| 怀化市| 江川县| 霸州市| 许昌县| 临江市| 闵行区| 舒兰市| 灵山县| 灵丘县| 绥中县| 赣州市| 甘泉县| 福安市| 辽阳县| 格尔木市| 石屏县| 嘉黎县| 饶阳县| 天镇县| 天气| 进贤县|