Android自定義ListView實(shí)現(xiàn)下拉刷新上拉加載更多
Listview現(xiàn)在用的很少了,基本都是使用Recycleview,但是不得不說Listview具有劃時(shí)代的意義,拓展性很強(qiáng),我們可以自己添加下拉刷新,上拉加載更多功能。他和recycleview不同,他生來具有addHeaderView和addFooterView的功能,這也導(dǎo)致同樣都是列表控件,實(shí)現(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
頭部樣式,我寫的是最簡(jiǎn)單的,根據(jù)業(yè)務(wù)需求來定,下拉的時(shí)候無非就是幾種
- 產(chǎn)品logo作為箭頭轉(zhuǎn)動(dòng)的icon
- 添加刷新時(shí)間
- 加入其他具有特色的動(dòng)效

<?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)自動(dòng)檢測(cè)頭部高度
heightHeader = viewHeader.getMeasuredHeight();
header_text = viewHeader.findViewById(R.id.header_text);
viewHeader.setPadding(0, -heightHeader, 0, 0);
this.addHeaderView(viewHeader);
}一些控件我定義成全局的是因?yàn)橄旅鏁?huì)用到。setPadding可以設(shè)置顯示位置,左上右下,上為負(fù)數(shù)就是隱藏在頂部。我們需要手指下拉去控制他緩慢顯示,就用到了OnTouchListener,我們實(shí)現(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中添加
這時(shí)候我們需要對(duì)event的down,move,up事件進(jìn)行邏輯處理,當(dāng)手指按在屏幕時(shí)會(huì)觸發(fā)事件,一個(gè)down事件,0~無數(shù)次move事件,一個(gè)up事件,這里面著重對(duì)move事件做處理,我們記錄一下down事件的Y,因?yàn)槭巧舷吕瓌?dòng),沒必要計(jì)算X。然后diffY就是手指滑動(dòng)的距離,我們需要處理一下這個(gè)值,因?yàn)橹堤?,而且值是整?shù),會(huì)讓我們下拉的時(shí)候產(chǎn)生錯(cuò)亂,我們本意是讓其從-100到0緩慢滑出(比如頭部高度是100,從隱藏到顯示就是-100到0),小伙伴都可以試試viewHeader.setPadding(0, diffY, 0, 0);和viewHeader.setPadding(0, paddY, 0, 0);效果是不一樣的,diff/3是讓其有種阻尼的感覺,不然的話會(huì)很塊就被拉出來了。還有事件消費(fèi)的話一定要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;
//滑動(dòng)的距離
paddY = - heightHeader + diffY / 3;
viewHeader.setPadding(0, paddY, 0, 0);
return true;
//break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}我們下面要做下拉時(shí)給人的反饋,我們下拉時(shí)有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;//當(dāng)前狀態(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;
}
}我們只需要判斷是否處于刷新中,如果不是,則計(jì)算是不是第一個(gè)item可見并且滑動(dòng)距離大于0,證明手指滑動(dòng)了。然后不同滑出高度顯示不同的文字即可。
@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;
//滑動(dòng)的距離
paddY = - heightHeader + diffY / 3;
//如果是正在刷新中不做任何操作
if (pull_current_state == PULL_REFRESHING) {
return false;
}
// 下拉//第一條,并且滑動(dòng)距離大于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、接口回調(diào)
我們需要狀態(tài)根據(jù)業(yè)務(wù)來動(dòng)態(tài)調(diào)整,在可以刷新的時(shí)候做一些邏輯處理,同時(shí)處理完了,調(diào)整狀態(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;
}準(zhǔn)備工作做好后,我們?cè)诟聽顟B(tài)的地方調(diào)用
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中使用,來一個(gè)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();
}
}到這下拉刷新就結(jié)束了。
上拉加載更多也是如此
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);
}到這里也布局算是添加完畢了,加載更多實(shí)現(xiàn)方式不同,我羅列兩種
- 結(jié)合OnTouchListener
- 結(jié)合OnScrollListener
方式一
這里我先說方式一,因?yàn)槲覀兿吕彩怯玫腛nTouchListener,上拉加載也有幾種狀態(tài),有加載中,還有暫無數(shù)據(jù),普遍大家會(huì)寫, 已經(jīng)到底了~,一直顯示在最底部,提示用戶沒數(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;//當(dāng)前狀態(tài)
只有一點(diǎn)需要注意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;
//滑動(dòng)的距離
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) {
//沒有滾動(dòng),或者已經(jīng)用戶觸摸滾動(dòng)動(dòng)畫結(jié)束
if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING){
//最后一個(gè)可見
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);
}到此這篇關(guān)于Android自定義ListView實(shí)現(xiàn)下拉刷新上拉加載更多的文章就介紹到這了,更多相關(guān)Android ListView下拉刷新上拉加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- android RecycleView實(shí)現(xiàn)下拉刷新和上拉加載
- 解決android viewmodel 數(shù)據(jù)刷新異常的問題
- Android巧用XListView實(shí)現(xiàn)萬能下拉刷新控件
- Android自定義view仿微信刷新旋轉(zhuǎn)小風(fēng)車
- Android自定義控件ListView下拉刷新的代碼
- Android ExpandableListView實(shí)現(xiàn)下拉刷新和加載更多效果
- Android RecyclerView的刷新分頁的實(shí)現(xiàn)
- android使用SwipeRefreshLayout實(shí)現(xiàn)ListView下拉刷新上拉加載
- android使用PullToRefresh框架實(shí)現(xiàn)ListView下拉刷新上拉加載更多
- Android 中RecyclerView頂部刷新實(shí)現(xiàn)詳解
- Android四種方式刷新View的操作方法
相關(guān)文章
Android 將 android view 的位置設(shè)為右下角的解決方法
Android 將 android view 的位置設(shè)為右下角的解決方法,需要的朋友可以參考一下2013-05-05
Android中使用ViewFlipper進(jìn)行手勢(shì)切換實(shí)例
這篇文章主要介紹了Android中使用ViewFlipper進(jìn)行手勢(shì)切換的方法,以實(shí)例形式詳細(xì)講述了XML文件的定義及功能函數(shù)的實(shí)現(xiàn)過程,需要的朋友可以參考下2014-10-10
Android 啟動(dòng)第三方程序的方法總結(jié)
這篇文章主要介紹了Android 啟動(dòng)第三方程序的方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android開發(fā)中的重力傳感器用法實(shí)例詳解
這篇文章主要介紹了Android開發(fā)中的重力傳感器用法,簡(jiǎn)單分析了Android重力傳感器的基本功能、使用方法,并結(jié)合實(shí)例形式分析了Android基于重力傳感器實(shí)現(xiàn)橫豎屏切換的相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
使用Chrome瀏覽器調(diào)試Android App詳解
這篇文章主要介紹了使用Chrome瀏覽器調(diào)試Android App詳解,本網(wǎng)講解了使用Facebook開源Stetho實(shí)現(xiàn)在Chrome中調(diào)試Android App中,需要的朋友可以參考下2015-05-05
如何使用Flutter實(shí)現(xiàn)手寫簽名效果
Flutter插件提供了用于繪制平滑簽名的簽名板,下面這篇文章主要給大家介紹了關(guān)于如何使用Flutter實(shí)現(xiàn)手寫簽名效果的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
Android開發(fā)中怎樣調(diào)用系統(tǒng)Email發(fā)送郵件(多種調(diào)用方式)
在Android中調(diào)用其他程序進(jìn)行相關(guān)處理,幾乎都是使用的Intent,所以,Email也不例外,所謂的調(diào)用Email,只是說Email可以接收Intent并做這些事情2013-06-06

