下拉刷新上拉加载+左滑编辑、删除等
2017-06-28 本文已影响175人
CrayfishXu
最近忙着赶项目,忙了将近两个星期了,总算是有了空闲的时间,来新增一篇。这次最重要的功能就是<b>下拉刷新上拉加载+左滑编辑、删除</b>了,现在实现的方式很多,比如recyclerview+SwipeRefreshLayout,这需要加新的兼容包,所以我想在已有包的基础上修改,查了下项目中正好有PullToRefreshListView和SwipeMenuListView。就此开始调查合并之路。
调查
调查之后还是有挺多的资料的。最好用的如下实现
public class SwipeMenuPullToRefreshListView extends
PullToRefreshAdapterViewBase<SwipeMenuListView>
```
###实现代码
代码挺长的,如下
````java
public class SwipeMenuPullToRefreshListView extends
PullToRefreshAdapterViewBase<SwipeMenuListView>{
private LoadingLayout mHeaderLoadingView;
private LoadingLayout mFooterLoadingView;
private FrameLayout mLvFooterLoadingFrame;
// 轮播广告区域
private View interceptView;
private boolean mListViewExtrasEnabled;
public SwipeMenuPullToRefreshListView(Context context) {
super(context);
}
public SwipeMenuPullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwipeMenuPullToRefreshListView(Context context, Mode mode) {
super(context, mode);
}
public SwipeMenuPullToRefreshListView(Context context, Mode mode,
AnimationStyle style) {
super(context, mode, style);
}
@Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
public void setInterceptView(View interceptView) {
SwipeMenuPullToRefreshListView.this.interceptView = interceptView;
}
@Override
protected void onRefreshing(final boolean doScroll) {
/**
* If we're not showing the Refreshing view, or the list is empty, the
* the header/footer views won't show so we use the normal method.
*/
ListAdapter adapter = getRefreshableView().getAdapter();
if (!mListViewExtrasEnabled || !getShowViewWhileRefreshing()
|| null == adapter || adapter.isEmpty()) {
super.onRefreshing(doScroll);
return;
}
super.onRefreshing(false);
final LoadingLayout origLoadingView, listViewLoadingView, oppositeListViewLoadingView;
final int selection, scrollToY;
switch (getCurrentMode()) {
case MANUAL_REFRESH_ONLY:
case PULL_FROM_END:
origLoadingView = getFooterLayout();
listViewLoadingView = mFooterLoadingView;
oppositeListViewLoadingView = mHeaderLoadingView;
selection = getRefreshableView().getCount() - 1;
scrollToY = getScrollY() - getFooterSize();
break;
case PULL_FROM_START:
default:
origLoadingView = getHeaderLayout();
listViewLoadingView = mHeaderLoadingView;
oppositeListViewLoadingView = mFooterLoadingView;
selection = 0;
scrollToY = getScrollY() + getHeaderSize();
break;
}
// Hide our original Loading View
origLoadingView.reset();
origLoadingView.hideAllViews();
// Make sure the opposite end is hidden too
oppositeListViewLoadingView.setVisibility(View.GONE);
// Show the ListView Loading View and set it to refresh.
listViewLoadingView.setVisibility(View.VISIBLE);
listViewLoadingView.refreshing();
if (doScroll) {
// We need to disable the automatic visibility changes for now
disableLoadingLayoutVisibilityChanges();
// We scroll slightly so that the ListView's header/footer is at the
// same Y position as our normal header/footer
setHeaderScroll(scrollToY);
// Make sure the ListView is scrolled to show the loading
// header/footer
getRefreshableView().setSelection(selection);
// Smooth scroll as normal
smoothScrollTo(0);
}
}
@Override
protected void onReset() {
/**
* If the extras are not enabled, just call up to super and return.
*/
if (!mListViewExtrasEnabled) {
super.onReset();
return;
}
final LoadingLayout originalLoadingLayout, listViewLoadingLayout;
final int scrollToHeight, selection;
final boolean scrollLvToEdge;
switch (getCurrentMode()) {
case MANUAL_REFRESH_ONLY:
case PULL_FROM_END:
originalLoadingLayout = getFooterLayout();
listViewLoadingLayout = mFooterLoadingView;
selection = getRefreshableView().getCount() - 1;
scrollToHeight = getFooterSize();
scrollLvToEdge = Math.abs(getRefreshableView().getLastVisiblePosition()
- selection) <= 1;
break;
case PULL_FROM_START:
default:
originalLoadingLayout = getHeaderLayout();
listViewLoadingLayout = mHeaderLoadingView;
scrollToHeight = -getHeaderSize();
selection = 0;
scrollLvToEdge = Math.abs(getRefreshableView()
.getFirstVisiblePosition() - selection) <= 1;
break;
}
// If the ListView header loading layout is showing, then we need to
// flip so that the original one is showing instead
if (listViewLoadingLayout.getVisibility() == View.VISIBLE) {
// Set our Original View to Visible
originalLoadingLayout.showInvisibleViews();
// Hide the ListView Header/Footer
listViewLoadingLayout.setVisibility(View.GONE);
/**
* Scroll so the View is at the same Y as the ListView
* header/footer, but only scroll if: we've pulled to refresh, it's
* positioned correctly
*/
if (scrollLvToEdge && getState() != State.MANUAL_REFRESHING) {
getRefreshableView().setSelection(selection);
setHeaderScroll(scrollToHeight);
}
}
// Finally, call up to super
super.onReset();
}
@Override
protected LoadingLayoutProxy createLoadingLayoutProxy(
final boolean includeStart, final boolean includeEnd) {
LoadingLayoutProxy proxy = super.createLoadingLayoutProxy(includeStart,
includeEnd);
if (mListViewExtrasEnabled) {
final Mode mode = getMode();
if (includeStart && mode.showHeaderLoadingLayout()) {
proxy.addLayout(mHeaderLoadingView);
}
if (includeEnd && mode.showFooterLoadingLayout()) {
proxy.addLayout(mFooterLoadingView);
}
}
return proxy;
}
protected SwipeMenuListView createListView(Context context, AttributeSet attrs) {
final SwipeMenuListView lv;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
lv = new InternalListViewSDK9(context, attrs);
} else {
lv = new InternalListView(context, attrs);
}
return lv;
}
@Override
protected SwipeMenuListView createRefreshableView(Context context, AttributeSet attrs) {
SwipeMenuListView lv = createListView(context, attrs);
// Set it to this so it can be used in ListActivity/ListFragment
lv.setId(android.R.id.list);
return lv;
}
@Override
protected void handleStyledAttributes(TypedArray a) {
super.handleStyledAttributes(a);
mListViewExtrasEnabled = a.getBoolean(
R.styleable.PullToRefresh_ptrListViewExtrasEnabled, true);
if (mListViewExtrasEnabled) {
final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT,
Gravity.CENTER_HORIZONTAL);
// Create Loading Views ready for use later
FrameLayout frame = new FrameLayout(getContext());
mHeaderLoadingView = createLoadingLayout(getContext(),
Mode.PULL_FROM_START, a);
mHeaderLoadingView.setVisibility(View.GONE);
frame.addView(mHeaderLoadingView, lp);
getRefreshableView().addHeaderView(frame, null, false);
mLvFooterLoadingFrame = new FrameLayout(getContext());
mFooterLoadingView = createLoadingLayout(getContext(),
Mode.PULL_FROM_END, a);
mFooterLoadingView.setVisibility(View.GONE);
mLvFooterLoadingFrame.addView(mFooterLoadingView, lp);
/**
* If the value for Scrolling While Refreshing hasn't been
* explicitly set via XML, enable Scrolling While Refreshing.
*/
if (!a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
setScrollingWhileRefreshingEnabled(true);
}
}
}
@TargetApi(9)
final class InternalListViewSDK9 extends InternalListView {
public InternalListViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY,
scrollX, scrollY, scrollRangeX, scrollRangeY,
maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(SwipeMenuPullToRefreshListView.this, deltaX,
scrollX, deltaY, scrollY, isTouchEvent);
return returnValue;
}
}
protected class InternalListView extends SwipeMenuListView implements
EmptyViewMethodAccessor {
private boolean mAddedLvFooter = false;
public InternalListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void dispatchDraw(Canvas canvas) {
/**
* This is a bit hacky, but Samsung's ListView has got a bug in it
* when using Header/Footer Views and the list is empty. This masks
* the issue so that it doesn't cause an FC. See Issue #66.
*/
try {
super.dispatchDraw(canvas);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 处理特定区域 拦截 Touch
if (interceptView != null) {
float px = ev.getRawX();
float py = ev.getRawY();
Rect winRect = new Rect();
int[] l = new int[2];
interceptView.getLocationOnScreen(l);
winRect.set(l[0], l[1], l[0] + interceptView.getWidth(), l[1]
+ interceptView.getHeight());
if (px < winRect.right && px > winRect.left
&& py < winRect.bottom && py > winRect.top) {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/**
* This is a bit hacky, but Samsung's ListView has got a bug in it
* when using Header/Footer Views and the list is empty. This masks
* the issue so that it doesn't cause an FC. See Issue #66.
*/
try {
return super.dispatchTouchEvent(ev);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
return false;
}
}
@Override
public void setAdapter(ListAdapter adapter) {
// Add the Footer View at the last possible moment
if (null != mLvFooterLoadingFrame && !mAddedLvFooter) {
addFooterView(mLvFooterLoadingFrame, null, false);
mAddedLvFooter = true;
}
super.setAdapter(adapter);
}
@Override
public void setEmptyView(View emptyView) {
SwipeMenuPullToRefreshListView.this.setEmptyView(emptyView);
}
@Override
public void setEmptyViewInternal(View emptyView) {
super.setEmptyView(emptyView);
}
}
}
```
除了这部分修改外我们还需要处理一下SwipeMenuListView ,把以下代码注释了,这里注释了对于嵌套ScrollView会有问题。
```java
// @Override //为与PullToRefresh一起用
// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// // TODO Auto-generated method stub
// int heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
// MeasureSpec.AT_MOST);
// super.onMeasure(widthMeasureSpec, heightSpec);
// }
```
以上处理后就能轻松实现下拉刷新上拉加载+左滑编辑删除。
###附
如果需要对SwipeMenuListView 特定Item不用左滑,那么可以在getView中的createMenu(menu);代码进行判断处理