中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android ListView 實現上拉加載的示例代碼

發布時間:2020-09-16 22:05:57 來源:腳本之家 閱讀:309 作者:蔣志碧 欄目:移動開發

本文介紹了Android ListView 實現上拉加載的示例代碼,分享給大家,具體如下:

Android ListView 實現上拉加載的示例代碼

我們先分析一下如何實現 ListView 上拉加載。

  • 當我們上拉的時候,會出現一個提示界面,即 ListView 的 Footer 布局。
  • ListView 要實現滾動,所以要監聽 ListView 滾動事件,即 OnScrollListener() 事件。
  • 當我們開始滾動時,Footer 布局才慢慢顯示出來,所以需要監聽 ListView 的 onTouch() 事件。

實現思路

  1. 首先判斷 ListView 加載時機,當 ListView 的 lastVisibleItem == totalItemCount 時表示當前處于 ListView 最底端,此時允許下拉。
  2. 自定義一個 FooterView,將 FooterView 添加到 ListView 底部,在上拉時候的顯示和完成時候的隱藏。
  3. 定義一個加載接口,當上拉動作完成時候回調,用于標記狀態并加載最新數據進行展示。

1、定義 Footer

Footer 要實現的效果:

第一次上拉時,Footer 逐漸顯示,文字顯示為下拉可以加載,箭頭向上,進度條隱藏。

當松開加載的時候,箭頭隱藏,進度條展示,文字改為正在加載。

Android ListView 實現上拉加載的示例代碼

1、Footer 加載時狀態變化

定義一個如上圖所示的 Footer 的 XML 文件 footer_layout.xml

<?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="wrap_content"
  android:paddingBottom="10dp"
  android:paddingTop="10dp">

  <LinearLayout
    android:id="@+id/layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="10dp"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
      android:id="@+id/tv_tip"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="下拉可以刷新"
      android:textSize="12sp" />
  </LinearLayout>

  <ImageView
    android:id="@+id/img_arrow"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginRight="10dp"
    android:layout_toLeftOf="@+id/layout"
    android:src="@drawable/pull_to_refresh_arrow" />

  <ProgressBar
    android:id="@+id/progress"
    
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginRight="10dp"
    android:layout_toLeftOf="@+id/img_arrow"
    android:visibility="gone"
    tools:visibility="visible" />
</RelativeLayout>

2、初始化布局

定義一個 RefreshListView 類繼承 ListView,重寫構造函數,并將 Footer 添加到 ListView 中。

public class RefreshListView extends ListView {
  private View header;
  private int headerHeight;//頂部布局高度
  private int firstVisibleItem;//當前第一個 Item 可見位置
  private float startY;//按下時開始的Y值
  private int scrollState;//當前滾動狀態
  
  private View footer;
  private int footerHeight;//底部布局高度
  private float lastY;
  private boolean canLoadMoreEnabled;//是否允許加載更多
  
  public RefreshListView(Context context) {
    super(context);
    initView(context);
  }

  public RefreshListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
  }

  public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initView(context);
  }


  private void initView(Context context) {
    header = LayoutInflater.from(context).inflate(R.layout.header_layout, null);
    footer = LayoutInflater.from(context).inflate(R.layout.footer_layout, null);
    measureView(header);
    measureView(footer);
    //這里獲取高度的時候需要先通知父布局header占用的空間
    headerHeight = header.getMeasuredHeight();
    footerHeight = footer.getMeasuredHeight();
    topPadding(-headerHeight);
    bottomPadding(-footerHeight);//用于隱藏 Footer
    this.addHeaderView(header);
    this.addFooterView(footer);
    this.setOnScrollListener(this);
  }
  
  /**
   * 設置 Footer 布局的下邊距
   * 以隱藏 Footer
   * @param topPadding
   */
  private void bottomPadding(int bottomPadding) {
    footer.setPadding(footer.getPaddingLeft(), footer.getPaddingTop(),
        footer.getPaddingRight(),
        bottomPadding);
    footer.invalidate();
  }
}

3、實現上拉加載

給 ListView 設置監聽

public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
  private int firstVisibleItem;//當前第一個 Item 可見位置
  private int scrollState;//當前滾動狀態
  
  private void initView(Context context) {
    header = LayoutInflater.from(context).inflate(R.layout.header_layout, null);
    footer = LayoutInflater.from(context).inflate(R.layout.footer_layout, null);
    measureView(header);
    measureView(footer);
    //這里獲取高度的時候需要先通知父布局header占用的空間
    headerHeight = header.getMeasuredHeight();
    footerHeight = footer.getMeasuredHeight();
    topPadding(-headerHeight);
    bottomPadding(-footerHeight);
    this.addHeaderView(header);
    this.addFooterView(footer);
    this.setOnScrollListener(this);
  }
  
  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {
    this.scrollState = scrollState;
  }

  @Override
  public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    this.firstVisibleItem = firstVisibleItem;
    this.lastVisibleItem = firstVisibleItem + visibleItemCount;
    this.totalItemCount = totalItemCount;
  }
}

加載的時機是判斷lastVisibleItem == totalItemCount,而上拉事件我們需要重寫 onTouchEvent() 事件,首先定義幾個狀態。

private float lastY;

private static int state;//當前狀態
private final static int NONE = 0;//正常狀態
private final static int PULL = 1;//下拉狀態
private final static int RELEASE = 2;//釋放狀態
private final static int REFRESHING = 3;//正在刷新狀態

在 onTouchEvent 中,在 ACTION_DOWN 時,記錄最開始的 Y 值,然后在 ACTION_MOVE 事件中實時記錄移動距離 space,不斷刷新 FooterView 的 bootomPadding,讓它跟隨滑動距離進行顯示,繼續滑動,當 space 大于了 FooterHeight 時,狀態給為 RELEASE,表示可以釋放進行刷新操作。

@Override
  public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //最頂部
        if (firstVisibleItem == 0) {//刷新
          canRefreshEnabled = true;
          startY = ev.getY();
        } else if (lastVisibleItem == totalItemCount) {//加載更多
          canLoadMoreEnabled = true;
          lastY = ev.getY();
        }
        break;
      case MotionEvent.ACTION_MOVE:
        onMove(ev);
        break;
      case MotionEvent.ACTION_UP:
        if (state == RELEASE) {//如果已經釋放,則可以提示刷新數據
          state = REFRESHING;
          if (iRefreshListener != null) {
            iRefreshListener.onRefresh();
          }
          if (iLoadMoreListener != null) {
            iLoadMoreListener.onLoadMore();
          }
        } else if (state == PULL) {//如果是在下拉狀態,不刷新數據
          state = NONE;
        }
        if (canRefreshEnabled) {
          refreshViewByState();
        }
        if (canLoadMoreEnabled) {
          loadViewByState();
        }
        canLoadMoreEnabled = false;
        canRefreshEnabled = false;
        break;
    }
    return super.onTouchEvent(ev);
  }

  /**
   * 判斷移動過程中的操作
   *
   * @param ev
   */
  private void onMove(MotionEvent ev) {
    int tempY = (int) ev.getY();
    int refreshSpace = (int) (tempY - startY);//向下移動的距離
    int topPadding = refreshSpace - headerHeight;//在移動過程中不斷設置 topPadding
    int loadSpace = (int) (lastY - tempY);//向上移動的距離
    int bottomPadding = loadSpace - footerHeight;//在移動過程中不斷設置 bottomPadding
    switch (state) {
      case NONE:
        //下拉移動距離大于0
        if (refreshSpace > 0) {
          state = PULL; //狀態變成下拉狀態
          refreshViewByState();
        }
        //上拉移動距離大于0
        if (loadSpace > 0) {
          state = PULL;//狀態變成下拉狀態
          loadViewByState();
        }
        break;
      case PULL:
        if (canRefreshEnabled) {
          topPadding(topPadding);//在移動過程中不斷設置 topPadding,讓 Header 隨著下拉動作慢慢顯示
        }
        if (canLoadMoreEnabled) {
          bottomPadding(bottomPadding);//在移動過程中不斷設置 bottomPadding,讓 Footer 隨著上拉動作慢慢顯示
        }
        //移動距離大于headerHeight并且正在滾動
        if (canRefreshEnabled && refreshSpace > (headerHeight + 30) && scrollState == SCROLL_STATE_TOUCH_SCROLL) {
          state = RELEASE;//提示釋放
          refreshViewByState();
        }
        //移動距離大于footerHeight并且正在滾動
        if (canLoadMoreEnabled && loadSpace > footerHeight + 30 && scrollState == SCROLL_STATE_TOUCH_SCROLL) {
          state = RELEASE;//提示釋放
          loadViewByState();//刷新footer布局
        }
        break;
      case RELEASE:
        if (canRefreshEnabled) {
          topPadding(topPadding);
          //移動距離小于headerHeight
          if (refreshSpace < headerHeight + 30) {
            state = PULL;//提示下拉
          } else if (refreshSpace <= 0) {
            state = NONE;
          }
          refreshViewByState();//更新header
        }
        if (canLoadMoreEnabled) {
          bottomPadding(bottomPadding);
          //移動距離小于footerHeight
          if (loadSpace < footerHeight + 30) {
            state = PULL;//提示下拉
          } else if (loadSpace <= 0) {
            state = NONE;
          }
          loadViewByState();//更新footer
        }
        break;
    }
  }

加載數據的時候,要根據狀態不斷改變 FooterView 的顯示,箭頭定義一個旋轉動畫讓其跟隨滑動距離實現旋轉,進度條也設置了逐幀動畫實現自定義進度條。

private void loadViewByState() {
    TextView tip = footer.findViewById(R.id.tv_tip);
    ImageView arrow = footer.findViewById(R.id.img_arrow);
    ProgressBar progressBar = footer.findViewById(R.id.progress);
    progressBar.setBackgroundResource(R.drawable.custom_progress_bar);
    AnimationDrawable animationDrawable = (AnimationDrawable) progressBar.getBackground();
    //給箭頭設置動畫
    RotateAnimation anim = new RotateAnimation(0, 180,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    RotateAnimation anim1 = new RotateAnimation(180, 0,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    anim.setDuration(200);
    anim.setFillAfter(true);
    anim1.setDuration(200);
    anim1.setFillAfter(true);
    switch (state) {
      case NONE://正常,footer不顯示
        bottomPadding(-footerHeight);
        arrow.clearAnimation();
        break;
      case PULL://下拉狀態
        arrow.setVisibility(VISIBLE);//箭頭顯示,進度條隱藏
        progressBar.setVisibility(GONE);
        if (animationDrawable.isRunning()) {
          animationDrawable.stop();
        }
        tip.setText("上拉可以加載");
        arrow.clearAnimation();
        arrow.setAnimation(anim);//箭頭向下
        break;
      case RELEASE://釋放狀態
        arrow.setVisibility(VISIBLE);//箭頭顯示,進度條隱藏
        progressBar.setVisibility(GONE);
        if (animationDrawable.isRunning()) {
          //停止動畫播放
          animationDrawable.stop();
        }
        tip.setText("松開開始加載");
        arrow.clearAnimation();
        arrow.setAnimation(anim);//箭頭向上
        break;
      case REFRESHING://刷新狀態
        bottomPadding(50);
        arrow.setVisibility(GONE);//箭頭顯示,進度條隱藏
        progressBar.setVisibility(VISIBLE);
        animationDrawable.start();
        tip.setText("正在加載...");
        arrow.clearAnimation();
        break;
    }
  }

4、下拉刷新完成回調

當上拉加載完成時,我們需要實現數據的刷新,并且要通知 Adapter 刷新數據,這里我們定義一個監聽接口實現回調即可。回調在 ACTION_UP 的 RELEASE 狀態下進行注冊。

private ILoadMoreListener iLoadMoreListener;

  public void setILoadMoreListener(ILoadMoreListener iLoadMoreListener) {
    this.iLoadMoreListener = iLoadMoreListener;
  }

  public interface ILoadMoreListener {
    void onLoadMore();
  }

5、測試

package com.dali.refreshandloadmorelistview;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;

import java.util.ArrayList;

public class MainActivity extends Activity implements RefreshListView.IRefreshListener, RefreshListView.ILoadMoreListener {

  private ArrayList<ApkEntity> apk_list;
  private ListAdapter adapter;
  private RefreshListView listView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setData();
    showList(apk_list);
  }

  private void showList(ArrayList<ApkEntity> apk_list) {
    if (adapter == null) {
      listView = (RefreshListView) findViewById(R.id.listview);
      listView.setIRefreshListener(this);
      listView.setILoadMoreListener(this);
      adapter = new ListAdapter(this, apk_list);
      listView.setAdapter(adapter);
    } else {
      adapter.onDateChange(apk_list);
    }
  }

  private void setData() {
    apk_list = new ArrayList<ApkEntity>();
    for (int i = 0; i < 10; i++) {
      ApkEntity entity = new ApkEntity();
      entity.setName("默認數據" + i);
      entity.setDes("這是一個神奇的應用");
      entity.setInfo("50w用戶");
      apk_list.add(entity);
    }
  }

  private void setRefreshData() {
    for (int i = 0; i < 2; i++) {
      ApkEntity entity = new ApkEntity();
      entity.setName("默認數據 + 刷新" + i);
      entity.setDes("這是一個神奇的應用");
      entity.setInfo("50w用戶");
      apk_list.add(0, entity);
    }
  }

  private void setLoadData() {
    for (int i = 0; i < 2; i++) {
      ApkEntity entity = new ApkEntity();
      entity.setName("默認數據 + 加載" + (adapter.getCount() + i));
      entity.setDes("這是一個神奇的應用");
      entity.setInfo("50w用戶");
      apk_list.add(entity);
    }
  }

  @Override
  public void onRefresh() {
    //添加刷新動畫效果
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        //獲取最新數據
        setRefreshData();
        //通知界面顯示數據
        showList(apk_list);
        //通知 ListView 刷新完成
        listView.refreshComplete();
      }
    }, 2000);
  }

  @Override
  public void onLoadMore() {
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        //獲取最新數據
        setLoadData();
        //通知界面顯示數據
        showList(apk_list);
        //通知 ListView 刷新完成
        listView.loadMoreComplete();
      }
    }, 2000);
  }
}

GitHub 源碼

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

龙胜| 梧州市| 平潭县| 祁连县| 车致| 台北县| 郎溪县| 铁岭县| 利津县| 长海县| 吉木乃县| 永修县| 太康县| 长宁县| 仁布县| 江口县| 横峰县| 伊金霍洛旗| 梓潼县| 砀山县| 新巴尔虎右旗| 清水河县| 晋中市| 香格里拉县| 黄梅县| 黎平县| 西乌珠穆沁旗| 军事| 淮北市| 衡南县| 澄城县| 蒲城县| 乌苏市| 措勤县| 当雄县| 大英县| 宜阳县| 汾西县| 西宁市| 淮安市| 莎车县|