package com.netease.nim.uikit.common.ui.listview; import android.content.Context; import android.os.Build; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.ListView; import com.netease.nim.uikit.R; import java.util.ArrayList; import java.util.List; public class AutoRefreshListView extends ListView { public enum State { REFRESHING, RESET, } public enum Mode { START, END, BOTH, } public interface OnRefreshListener { public void onRefreshFromStart(); public void onRefreshFromEnd(); } private OnRefreshListener refreshListener; private List<OnScrollListener> scrollListeners = new ArrayList<OnScrollListener>(); private State state = State.RESET; private Mode mode = Mode.START; private Mode currentMode = Mode.START; private boolean refreshableStart = true; private boolean refreshableEnd = true; private ViewGroup refreshHeader; private ViewGroup refreshFooter; private int offsetY; public AutoRefreshListView(Context context) { super(context); init(context); } public AutoRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public AutoRefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public void setMode(Mode mode) { this.mode = mode; } public void setOnRefreshListener(OnRefreshListener refreshListener) { this.refreshListener = refreshListener; } @Override public void setOnScrollListener(OnScrollListener l) { // replaced by addOnScrollListener throw new UnsupportedOperationException("Use addOnScrollListener instead!"); } public void addOnScrollListener(OnScrollListener l) { scrollListeners.add(l); } public void removeOnScrollListener(OnScrollListener l) { scrollListeners.remove(l); } private void init(Context context) { addRefreshView(context); super.setOnScrollListener(new OnScrollListener() { public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { for (OnScrollListener listener : scrollListeners) { listener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } public void onScrollStateChanged(AbsListView view, int scrollState) { for (OnScrollListener listener : scrollListeners) { listener.onScrollStateChanged(view, scrollState); } } }); initRefreshListener(); state = State.RESET; } private void addRefreshView(Context context) { refreshHeader = (ViewGroup) View.inflate(context, R.layout.nim_listview_refresh, null); addHeaderView(refreshHeader, null, false); refreshFooter = (ViewGroup) View.inflate(context, R.layout.nim_listview_refresh, null); addFooterView(refreshFooter, null, false); } private void initRefreshListener() { OnScrollListener listener = new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE && state == State.RESET) { boolean reachTop = (getFirstVisiblePosition() < getHeaderViewsCount() && getCount() > getHeaderViewsCount()); if (reachTop) { onRefresh(true); } else { boolean reachBottom = getLastVisiblePosition() >= getCount() - 1; if (reachBottom) { onRefresh(false); } } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }; addOnScrollListener(listener); } private void onRefresh(boolean start) { if (refreshListener != null) { View firstVisibleChild = getChildAt(getHeaderViewsCount()); if (firstVisibleChild != null) { offsetY = firstVisibleChild.getTop(); } if (start && refreshableStart && mode != Mode.END) { currentMode = Mode.START; state = State.REFRESHING; refreshListener.onRefreshFromStart(); } else if (refreshableEnd && mode != Mode.START) { currentMode = Mode.END; state = State.REFRESHING; refreshListener.onRefreshFromEnd(); } updateRefreshView(); } } private void updateRefreshView() { switch (state) { case REFRESHING: getRefreshView().getChildAt(0).setVisibility(View.VISIBLE); break; case RESET: if (currentMode == Mode.START) { refreshHeader.getChildAt(0).setVisibility(refreshableStart ? View.INVISIBLE : View.GONE); } else { refreshFooter.getChildAt(0).setVisibility(View.GONE); } break; } } private ViewGroup getRefreshView() { switch (currentMode) { case END: return refreshFooter; case START: default: return refreshHeader; } } public void onRefreshStart(Mode mode) { state = State.REFRESHING; currentMode = mode; } /** * 加载完成 */ public void onRefreshComplete(int count, int requestCount, boolean needOffset) { state = State.RESET; resetRefreshView(count, requestCount); if (!needOffset) { return; } if (currentMode == Mode.START) { setSelectionFromTop(count + getHeaderViewsCount(), refreshableStart ? offsetY : 0); } } public void onRefreshComplete() { state = State.RESET; updateRefreshView(); } private void resetRefreshView(int count, int requestCount) { if (currentMode == Mode.START) { /** 如果是第一次加载,如果count<requestCount, 表示没有数据了。 * 如果是后面的加载,为了列表稳定,只有count>0, 就保留header的高度 */ if (getCount() == count + getHeaderViewsCount() + getFooterViewsCount()) { refreshableStart = (count == requestCount); } else { refreshableStart = (count > 0); } } else { refreshableEnd = (count > 0); } updateRefreshView(); } /** * handle over scroll when no more data */ private boolean isBeingDragged = false; private int startY = 0; @Override public boolean onTouchEvent(MotionEvent event) { if (Build.VERSION.SDK_INT < 11) { try { return onTouchEventInternal(event); } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); return false; } } else { return onTouchEventInternal(event); } } private boolean onTouchEventInternal(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: onTouchBegin(event); break; case MotionEvent.ACTION_MOVE: onTouchMove(event); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: onTouchEnd(); break; } return super.onTouchEvent(event); } private void onTouchBegin(MotionEvent event) { int firstItemIndex = getFirstVisiblePosition(); if (!refreshableStart && firstItemIndex <= getHeaderViewsCount() && !isBeingDragged) { isBeingDragged = true; startY = (int) event.getY(); } } private void onTouchMove(MotionEvent event) { /** check state again */ onTouchBegin(event); if (!isBeingDragged) { return; } /** scroll to dragged position */ int offsetY = (int) (event.getY() - startY); offsetY = Math.max(offsetY, 0) / 2; refreshHeader.setPadding(0, offsetY, 0, 0); } private void onTouchEnd() { if (isBeingDragged) { refreshHeader.setPadding(0, 0, 0, 0); } isBeingDragged = false; } }