Android自定義ListView實現(xiàn)下拉刷新上拉加載更多
Listview現(xiàn)在用的很少了,基本都是使用Recycleview,但是不得不說Listview具有劃時代的意義,拓展性很強,我們可以自己添加下拉刷新,上拉加載更多功能。他和recycleview不同,他生來具有addHeaderView和addFooterView的功能,這也導致同樣都是列表控件,實現(xiàn)上拉下拉的方式缺截然不同。
效果圖
1、創(chuàng)建刷新控件
public class MyListview extends ListView { public MyListview(Context context) { this(context,null); } public MyListview(Context context, AttributeSet attrs) { this(context,attrs,0); } public MyListview(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } public void init(Context context) { }
1.1、創(chuàng)建頭部View
頭部樣式,我寫的是最簡單的,根據(jù)業(yè)務需求來定,下拉的時候無非就是幾種
- 產品logo作為箭頭轉動的icon
- 添加刷新時間
- 加入其他具有特色的動效
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" tools:ignore="MissingDefaultResource"> <ProgressBar android:layout_width="20dp" android:layout_height="match_parent" style="?android:attr/progressBarStyle" android:layout_marginEnd="10dp" android:indeterminateTint="#E8AD56" /> <TextView android:id="@+id/header_text" android:layout_width="wrap_content" android:layout_height="50dp" android:text="我是頭部" android:gravity="center" android:textColor="#E8AD56"/> </LinearLayout>
樣式寫好后,我們需要添加到我們控件中,addHeaderView就起到作用了
public void init(Context context) { //添加頭部 viewHeader = View.inflate(context, R.layout.view_header, null); viewHeader.measure(0, 0); //讓系統(tǒng)自動檢測頭部高度 heightHeader = viewHeader.getMeasuredHeight(); header_text = viewHeader.findViewById(R.id.header_text); viewHeader.setPadding(0, -heightHeader, 0, 0); this.addHeaderView(viewHeader); }
一些控件我定義成全局的是因為下面會用到。setPadding可以設置顯示位置,左上右下,上為負數(shù)就是隱藏在頂部。我們需要手指下拉去控制他緩慢顯示,就用到了OnTouchListener,我們實現(xiàn)OnTouch方法做一些事件的分發(fā)處理。
1.2、下拉事件
public class MyListview extends ListView implements View.OnTouchListener{ @Override public boolean onTouch(View v, MotionEvent event) { return false; } public void init(Context context){ setOnTouchListener(this); } }
注意:別忘記了setOnTouchListener在init中添加
這時候我們需要對event的down,move,up事件進行邏輯處理,當手指按在屏幕時會觸發(fā)事件,一個down事件,0~無數(shù)次move事件,一個up事件,這里面著重對move事件做處理,我們記錄一下down事件的Y,因為是上下拉動,沒必要計算X。然后diffY就是手指滑動的距離,我們需要處理一下這個值,因為值太大,而且值是整數(shù),會讓我們下拉的時候產生錯亂,我們本意是讓其從-100到0緩慢滑出(比如頭部高度是100,從隱藏到顯示就是-100到0),小伙伴都可以試試viewHeader.setPadding(0, diffY, 0, 0);和viewHeader.setPadding(0, paddY, 0, 0);效果是不一樣的,diff/3是讓其有種阻尼的感覺,不然的話會很塊就被拉出來了。還有事件消費的話一定要return true。
@Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: moveY = (int) event.getY(); diffY = moveY - downY; //滑動的距離 paddY = - heightHeader + diffY / 3; viewHeader.setPadding(0, paddY, 0, 0); return true; //break; case MotionEvent.ACTION_UP: break; } return false; }
我們下面要做下拉時給人的反饋,我們下拉時有3種狀態(tài)分別是
- 開始刷新 (下拉距離不超過100)
- 釋放刷新 (下拉距離超過100)
- 刷新中 (手指釋放,up事件處理)
//屬性-開始刷新狀態(tài) private final int PULL_REFRESH_STATE = 0; private final int PULL_REFRESH_RELEASE = 1;//釋放刷新 private final int PULL_REFRESHING = 2;//正在刷新 private int pull_current_state = PULL_REFRESH_STATE;//當前狀態(tài)
public void updateHeaderState() { switch (pull_current_state) { //開始 case PULL_REFRESH_STATE: header_text.setText("開始刷新"); viewHeader.setPadding(0, -heightHeader, 0, 0); break; //釋放 case PULL_REFRESH_RELEASE: header_text.setText("釋放刷新"); break; //正在 case PULL_REFRESHING: header_text.setText("刷新中......."); viewHeader.setPadding(0, 0, 0, 0); break; } }
我們只需要判斷是否處于刷新中,如果不是,則計算是不是第一個item可見并且滑動距離大于0,證明手指滑動了。然后不同滑出高度顯示不同的文字即可。
@Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: moveY = (int) event.getY(); diffY = moveY - downY; //滑動的距離 paddY = - heightHeader + diffY / 3; //如果是正在刷新中不做任何操作 if (pull_current_state == PULL_REFRESHING) { return false; } // 下拉//第一條,并且滑動距離大于0 if (getFirstVisiblePosition() == 0 && diffY > 0) { if (paddY > 0 && pull_current_state != PULL_REFRESH_RELEASE) { //釋放刷新 pull_current_state = PULL_REFRESH_RELEASE; updateHeaderState(); } else if (paddY < 0 && pull_current_state != PULL_REFRESH_STATE) { pull_current_state = PULL_REFRESH_STATE; header_text.setText("開始刷新"); updateHeaderState(); } viewHeader.setPadding(0, paddY, 0, 0); return true; } break; case MotionEvent.ACTION_UP: if (pull_current_state == PULL_REFRESH_STATE) { updateHeaderState(); } else if (pull_current_state == PULL_REFRESH_RELEASE) { pull_current_state = PULL_REFRESHING; updateHeaderState(); } break; } return false; }
1.3、接口回調
我們需要狀態(tài)根據(jù)業(yè)務來動態(tài)調整,在可以刷新的時候做一些邏輯處理,同時處理完了,調整狀態(tài)。
public void setPullDownFinish() { pull_current_state = PULL_REFRESH_STATE; viewHeader.setPadding(0, -heightHeader, 0, 0); }
public IPullDownRefreshService iPullDownRefreshService; public interface IPullDownRefreshService { void onPullDownRefresh();//下拉刷新 void onLoadMore();//上拉加載更多刷新 } public void setOnRefreshListener(IPullDownRefreshService iPullDownRefreshService) { this.iPullDownRefreshService = iPullDownRefreshService; }
準備工作做好后,我們在更新狀態(tài)的地方調用
public void updateHeaderState() { switch (pull_current_state) { //開始 case PULL_REFRESH_STATE: break; //釋放 case PULL_REFRESH_RELEASE: break; //正在 case PULL_REFRESHING: header_text.setText("刷新中......."); viewHeader.setPadding(0, 0, 0, 0); if (iPullDownRefreshService != null) { iPullDownRefreshService.onPullDownRefresh(); } break; } }
Activity中使用,來一個3秒刷新完成
mBinding.listview.setOnRefreshListener(new MyListview.IPullDownRefreshService() { @Override public void onPullDownRefresh() { refreshSuccess(); Toast.makeText(ActivityRefresh.this, "下拉-加載中.....", Toast.LENGTH_SHORT).show(); } @Override public void onLoadMore() { Toast.makeText(ActivityRefresh.this, "more-加載中.....", Toast.LENGTH_SHORT).show(); } }); CountDownTimer countDownTimer; public void refreshSuccess() { if (countDownTimer == null) { countDownTimer = new CountDownTimer(3000, 1000) { @Override public void onTick(long millisUntilFinished) { } @Override public void onFinish() { mBinding.listview.setPullDownFinish(); countDownTimer.cancel(); countDownTimer = null; } }.start(); } }
到這下拉刷新就結束了。
上拉加載更多也是如此
2、上拉加載更多
2.1、底部樣式
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" tools:ignore="MissingDefaultResource"> <ProgressBar android:id="@+id/footer_prggress" android:layout_width="20dp" android:layout_height="match_parent" style="?android:attr/progressBarStyle" android:layout_marginEnd="10dp" android:indeterminateTint="#E8AD56" /> <TextView android:id="@+id/footer_text" android:layout_width="wrap_content" android:layout_height="70dp" android:text="正在加載更多" android:gravity="center" android:textColor="#E8AD56"/> </LinearLayout>
2.2、布局添加
public void init(Context context) { viewFooter = View.inflate(context, R.layout.view_footer, null); viewFooter.measure(0, 0); footer_text = viewFooter.findViewById(R.id.footer_text); footer_prggress = viewFooter.findViewById(R.id.footer_prggress); heightFooter = viewFooter.getMeasuredHeight(); viewFooter.setPadding(0, -heightFooter, 0, 0); this.addFooterView(viewFooter); }
到這里也布局算是添加完畢了,加載更多實現(xiàn)方式不同,我羅列兩種
- 結合OnTouchListener
- 結合OnScrollListener
方式一
這里我先說方式一,因為我們下拉也是用的OnTouchListener,上拉加載也有幾種狀態(tài),有加載中,還有暫無數(shù)據(jù),普遍大家會寫, 已經到底了~,一直顯示在最底部,提示用戶沒數(shù)據(jù)了,從而我們控件的上拉事件不可觸發(fā)狀態(tài)。
private final int MORE_LOAD_STATE = 10; private final int MORE_LOADING = 11; private final int MORE_NO = 12;//已加載全部數(shù)據(jù) private int more_current_state = MORE_LOAD_STATE;//當前狀態(tài)
只有一點需要注意paddFooterY = paddFooterY > heightFooter ? 0 : paddFooterY;來判斷不能滑出底部的高度。
@Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: moveY = (int) event.getY(); diffY = moveY - downY; //滑動的距離 paddFooterY = heightFooter - diffY/3 ; //上拉加載更多 if (getLastVisiblePosition() == getCount() - 1 && more_current_state != MORE_NO) { if( more_current_state!=MORE_LOADING){ more_current_state = MORE_LOADING; if (iPullDownRefreshService != null) { iPullDownRefreshService.onLoadMore(); } } paddFooterY = paddFooterY > heightFooter ? 0 : paddFooterY; viewFooter.setPadding(0,paddFooterY , 0, 0); return true; } break; case MotionEvent.ACTION_UP: break; } return false; }
方式二
public class MyListview extends ListView implements View.OnTouchListener, AbsListView.OnScrollListener{ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //沒有滾動,或者已經用戶觸摸滾動動畫結束 if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING){ //最后一個可見 if(getLastVisiblePosition() == getCount()-1){ viewFooter.setPadding(0,0,0,0); if(iPullDownRefreshService!=null){ iPullDownRefreshService.onLoadMore(); } } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }
注意:在init方法中添加
public void init(Context context) { setOnTouchListener(this); ...... this.setOnScrollListener(this); }
到此這篇關于Android自定義ListView實現(xiàn)下拉刷新上拉加載更多的文章就介紹到這了,更多相關Android ListView下拉刷新上拉加載內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- android RecycleView實現(xiàn)下拉刷新和上拉加載
- 解決android viewmodel 數(shù)據(jù)刷新異常的問題
- Android巧用XListView實現(xiàn)萬能下拉刷新控件
- Android自定義view仿微信刷新旋轉小風車
- Android自定義控件ListView下拉刷新的代碼
- Android ExpandableListView實現(xiàn)下拉刷新和加載更多效果
- Android RecyclerView的刷新分頁的實現(xiàn)
- android使用SwipeRefreshLayout實現(xiàn)ListView下拉刷新上拉加載
- android使用PullToRefresh框架實現(xiàn)ListView下拉刷新上拉加載更多
- Android 中RecyclerView頂部刷新實現(xiàn)詳解
- Android四種方式刷新View的操作方法
相關文章
Android實現(xiàn)捕獲TextView超鏈接的方法
這篇文章主要介紹了Android實現(xiàn)捕獲TextView超鏈接的方法,涉及Android查找TextView中超鏈接的相關實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-10-10Android高手進階教程(二十六)之---Android超仿Path菜單的功能實現(xiàn)!
本篇文章主要主要介紹了Android超仿Path菜單的功能實現(xiàn),現(xiàn)在分享給大家,也給大家做個參考。感興趣的可以了解一下。2016-11-11AndroidStudio Gradle基于友盟的多渠道打包方法
這篇文章主要介紹了AndroidStudio Gradle基于友盟的多渠道打包方法,需要的朋友可以參考下2017-09-09Android編程處理窗口控件大小,形狀,像素等UI元素工具類
這篇文章主要介紹了Android編程處理窗口控件大小,形狀,像素等UI元素工具類,可實現(xiàn)像素與dp的轉換、窗口寬度設置、彈出窗口中l(wèi)istview高度設置等功能,需要的朋友可以參考下2017-12-12Android實現(xiàn)在列表List中顯示半透明小窗體效果的控件用法詳解
這篇文章主要介紹了Android實現(xiàn)在列表List中顯示半透明小窗體效果的控件用法,結合實例形式分析了Android半透明提示框的實現(xiàn)與設置技巧,需要的朋友可以參考下2016-06-06Android使用addView動態(tài)添加組件的方法
這篇文章主要為大家詳細介紹了Android使用addView動態(tài)添加組件的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09利用smsmanager實現(xiàn)后臺發(fā)送短信示例
這篇文章主要介紹了android利用SmsManager可以實現(xiàn)后臺發(fā)送短信的方法,最近有使用說明,大家可以參考使用2014-01-01Android ToolBar 修改邊距的實現(xiàn)方法
這篇文章主要介紹了Android ToolBar 修改邊距的實現(xiàn)方法的相關資料,通過此文希望能幫助到大家,需要的朋友可以參考下2017-08-08