大家对这些功能都是看的多了,然后对上拉刷新和下拉加载的原理都是非常清楚的,所以实现这功能其实也就是为了让大家能够从众多的同行们来进行比较学习而已,虽然即使是这样,但是面试的时候面试官还是会问你上拉和下拉是怎么实现的,滑动删除功能是怎么实现,其实要实现这些功能又不是唯一的方法,但是基本上思想都是一致的。然后gitup上的这些例子是非常的多,然后实现的也是大同小异但是也不能不让我们去球童存异。作为天朝的程序员即使是一个伸手党也不必太觉得羞耻,能把别人的东西来改一改或者沿用别人的思想来模仿也是不错的。然后公司的项目也可能不建议直接去导入XlistView来实现下拉的功能,所以我们可能就需要自己去实现一些这样的功能了,废话不多说,稍微就介绍下原理,主要还是代码为主,即使看到代码和别人大同小异也不要计较,因为这些功能纯粹就是模仿好了,当然参考别人的代码也不是什么丢脸的事,关键的是对自己有帮助就好了啊。

我们要写这样一个功能当然的先从自定义的listView开始了,说起来其实就是一个组合控件,它就由3部分组成:head 、content、footer,所以我们的代码也就可以分开来实现了额。先我们写头部的布局和功能。

xlistview_header.xml

<?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:gravity="bottom" >
    <RelativeLayout
        android:id="@+id/xlistview_header_content"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_weight="1"
        tools:ignore="UselessParent" >
        <LinearLayout
            android:id="@+id/xlistview_header"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_centerInParent="true"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="15dp"
            android:orientation="vertical" >

<TextView
                android:id="@+id/xlistview_header_hint_textview"
                android:layout_width="180dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="10dp"
                android:text="正在加载"
                android:textColor="@android:color/black"
                android:textSize="18sp" />
            <!-- 最近更新 -->
            <TextView
                android:id="@+id/head_lastUpdatedTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:text="上次更新:"
                android:textSize="12dp" />
        </LinearLayout>

<ImageView
            android:id="@+id/xlistview_header_arrow"
            android:layout_width="30dp"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@id/xlistview_header"
            android:src="@drawable/zlistview_arrow" />
        <ProgressBar
            android:id="@+id/xlistview_header_progressbar"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@id/xlistview_header"
            android:visibility="invisible" />
    </RelativeLayout>
</LinearLayout>

很容易发现头部就是由刷新加载数据时显示图片,头部中间显示加载的状态文字,如果是第一次不会提醒上次更新时间,如果不是第一次就会显示上次的刷新时间,然后手指下拉时箭头向下松开时箭头向上,移动时显示圆形加载进度。好了实现的思路也了解了吧。

ZListViewHeader.java(头部布局)

package com.zy.zlistview.widget;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.socks.zlistview.R;
public class ZListViewHeader extends LinearLayout {
       //手势动作变化,状态的三种描述
private static final String HINT_NORMAL = "下拉刷新";
private static final String HINT_READY = "松开刷新数据";
private static final String HINT_LOADING = "正在加载...";
// 正常状态
public final static int STATE_NORMAL = 0;
// 准备刷新状态,也就是箭头方向发生改变之后的状态
public final static int STATE_READY = 1;
// 刷新状态,箭头变成了progressBar
public final static int STATE_REFRESHING = 2;
// 布局容器,也就是根布局
private LinearLayout container;
// 箭头图片
private ImageView mArrowImageView;
// 刷新状态显示
private ProgressBar mProgressBar;
// 说明文本
private TextView mHintTextView;
// 记录当前的状态
private int mState;
// 用于改变箭头的方向的动画
private Animation mRotateUpAnim;
private Animation mRotateDownAnim;
// 动画持续时间
private final int ROTATE_ANIM_DURATION = 180;

//x显示上次刷新的时间

private TextView head_lastUpdatedTextView;

protected TextView getHead_lastUpdatedTextView() {
return head_lastUpdatedTextView;
}
public void setHead_lastUpdatedTextView(TextView head_lastUpdatedTextView) {
this.head_lastUpdatedTextView = head_lastUpdatedTextView;
}

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

private void initView(Context context) {
mState = STATE_NORMAL;
// 初始情况下,设置下拉刷新view高度为0
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0);
container = (LinearLayout) LayoutInflater.from(context).inflate(
R.layout.xlistview_header, null);
addView(container, lp);
// 初始化控件
mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);
head_lastUpdatedTextView = (TextView) findViewById(R.id.head_lastUpdatedTextView);
head_lastUpdatedTextView.setVisibility(View.INVISIBLE);
mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);
mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar);
// 初始化动画
mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
mRotateUpAnim.setFillAfter(true);
mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
mRotateDownAnim.setFillAfter(true);
}

// 设置header的状态
public void setState(int state) {
if (state == mState)
return;
// 显示进度
if (state == STATE_REFRESHING) {//刷新时
mArrowImageView.clearAnimation();//箭头的动画消失,并且替换为显示精度条
mArrowImageView.setVisibility(View.INVISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
} else {
// 显示箭头
mArrowImageView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.INVISIBLE);
}

switch (state) {
case STATE_NORMAL:
if (mState == STATE_READY) {
mArrowImageView.startAnimation(mRotateDownAnim);
}
if (mState == STATE_REFRESHING) {
mArrowImageView.clearAnimation();
SharedPreferences sp = getContext().getSharedPreferences(
"isResh", 0);
if (sp.getBoolean("isresh", true) == true) {
head_lastUpdatedTextView.setVisibility(View.VISIBLE);
head_lastUpdatedTextView.setText("上次更新:"
+ sp.getString("date", ""));
}
}
mHintTextView.setText(HINT_NORMAL);
break;
case STATE_READY:
if (mState != STATE_READY) {
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mRotateUpAnim);
mHintTextView.setText(HINT_READY);//显示提示文字
}
break;
case STATE_REFRESHING:
mHintTextView.setText(HINT_LOADING);
break;
}
mState = state;
}

public void setVisiableHeight(int height) {//设置头部可视高度
if (height < 0)
height = 0;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) container
.getLayoutParams();
lp.height = height;
container.setLayoutParams(lp);
}

public int getVisiableHeight() {
return container.getHeight();
}

public void show() {
container.setVisibility(View.VISIBLE);
}

public void hide() {
container.setVisibility(View.INVISIBLE);
}
}

接下来看底部:xlistview_footer.xml

<?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="fill_parent"
    android:layout_height="wrap_content" >

<RelativeLayout
        android:id="@+id/xlistview_footer_content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        tools:ignore="UselessParent" >
        <LinearLayout 
            android:id="@+id/footer_load"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:visibility="invisible"
            >
            <ProgressBar
            android:id="@+id/xlistview_footer_progressbar"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_gravity="center"
            android:visibility="invisible" />
              <TextView
                  android:id="@+id/xlistview_footer_loding_textview"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_gravity="center"
                  android:paddingLeft="8dp"
                  android:textColor="@android:color/black"
                  android:textSize="14sp"
                  android:visibility="invisible" />
        </LinearLayout>
        <TextView
            android:id="@+id/xlistview_footer_hint_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textColor="@android:color/black"
            android:textSize="14sp" />
    </RelativeLayout>

</LinearLayout>

底部的布局就更加的简单了,正常情况下就一个文字提示你加载更多,当你上拉时就变为显示进度条和加载状态。功能实现也不是很复杂。直接代码:

ZListViewFooter.java

package com.zy.zlistview.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.socks.zlistview.R;
public class ZListViewFooter extends LinearLayout {
public final static String HINT_READ = "松开载入更多";
public final static String HINT_NORMAL = "查看更多";

// 正常状态
public final static int STATE_NORMAL = 0;
// 准备状态
public final static int STATE_READY = 1;
// 加载状态
public final static int STATE_LOADING = 2;
private View mContentView;
private View mProgressBar;
private TextView mHintView;
    private LinearLayout footer_load;
    private TextView loding_textview;
public ZListViewFooter(Context context) {
super(context);
initView(context);
}
public ZListViewFooter(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(Context context) {

LinearLayout moreView = (LinearLayout) LayoutInflater.from(context)
.inflate(R.layout.xlistview_footer, null);
addView(moreView);
moreView.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mContentView = moreView.findViewById(R.id.xlistview_footer_content);
footer_load= (LinearLayout) moreView.findViewById(R.id.footer_load);
  loding_textview= (TextView) moreView.findViewById(R.id.xlistview_footer_loding_textview);
mProgressBar = moreView.findViewById(R.id.xlistview_footer_progressbar);
mHintView = (TextView) moreView
.findViewById(R.id.xlistview_footer_hint_textview);
}

/**
* 设置当前的状态

* @param state
*/
public void setState(int state) {

//mProgressBar.setVisibility(View.INVISIBLE);
footer_load.setVisibility(View.INVISIBLE);
mHintView.setVisibility(View.INVISIBLE);

switch (state) {
case STATE_READY://准备状态
mHintView.setVisibility(View.VISIBLE);
mHintView.setText(HINT_READ);
break;
case STATE_NORMAL://正常状态
mHintView.setVisibility(View.VISIBLE);
mHintView.setText(HINT_NORMAL);
break;

case STATE_LOADING:
footer_load.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
loding_textview.setVisibility(View.VISIBLE);
loding_textview.setText("正在加载中....");
break;

}

}

public void setBottomMargin(int height) {
if (height > 0) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
.getLayoutParams();
lp.bottomMargin = height;
mContentView.setLayoutParams(lp);
}
}

public int getBottomMargin() {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
.getLayoutParams();
return lp.bottomMargin;
}

public void hide() {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
.getLayoutParams();
lp.height = 0;
mContentView.setLayoutParams(lp);
}

public void show() {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView
.getLayoutParams();
lp.height = LayoutParams.WRAP_CONTENT;
mContentView.setLayoutParams(lp);
}
}

跟实现头部的代码差不多,主要就是一些状态改变然后布局做相应的改变。

接下来是中间item的布局,然后是模仿qq的滑动删除的功能,所以我们还是参考了gitup上的开源项目SwipeXXX,你可以自己去看下,然后参考了里面的很多东西,不多说了直接代码

SwipeListener.java

package com.zy.zlistview.listener;

import com.zy.zlistview.view.ZSwipeItem;

public interface SwipeListener {

public void onStartOpen(ZSwipeItem layout);

public void onOpen(ZSwipeItem layout);

public void onStartClose(ZSwipeItem layout);

public void onClose(ZSwipeItem layout);

public void onUpdate(ZSwipeItem layout, int leftOffset, int topOffset);

public void onHandRelease(ZSwipeItem layout, float xvel, float yvel);

}

然后我们设了几个枚举类方便我们对item的控制

public enum DragEdge {
Left, Right, Top, Bottom;
};

public enum Mode {
Single, Multiple;
};

public enum ShowMode {
LayDown, PullOut;
}

我们先来参考QQ条目的布局实现:

package com.zy.zlistview.view;

import java.util.ArrayList;
import java.util.List;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.ListAdapter;

import com.socks.zlistview.R;
import com.zy.zlistview.adapter.BaseSwipeAdapter.OnSwipeLayoutListener;
import com.zy.zlistview.enums.DragEdge;
import com.zy.zlistview.enums.ShowMode;
import com.zy.zlistview.listener.SwipeListener;
@SuppressLint("ClickableViewAccessibility")
public class ZSwipeItem extends FrameLayout {

protected static final String TAG = "ZSwipeItem";
private ViewDragHelper mDragHelper;
private int mDragDistance = 0;
private DragEdge mDragEdge;
private ShowMode mShowMode;
private float mHorizontalSwipeOffset;
private float mVerticalSwipeOffset;
private boolean mSwipeEnabled = true;

private List<OnSwipeLayoutListener> mOnLayoutListeners;
private List<SwipeListener> swipeListeners = new ArrayList<SwipeListener>();

public ZSwipeItem(Context context) {
this(context, null);
}

public ZSwipeItem(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public ZSwipeItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

mDragHelper = ViewDragHelper.create(this, mDragHelperCallback);

TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ZSwipeItem);
// 默认是右边缘检测
int ordinal = a.getInt(R.styleable.ZSwipeItem_drag_edge,
DragEdge.Right.ordinal());
mDragEdge = DragEdge.values()[ordinal];
// 默认模式是拉出
ordinal = a.getInt(R.styleable.ZSwipeItem_show_mode,
ShowMode.PullOut.ordinal());
mShowMode = ShowMode.values()[ordinal];

mHorizontalSwipeOffset = a.getDimension(
R.styleable.ZSwipeItem_horizontalSwipeOffset, 0);
mVerticalSwipeOffset = a.getDimension(
R.styleable.ZSwipeItem_verticalSwipeOffset, 0);

a.recycle();
}

/**
* 进行拖拽的主要类
*/
private ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() {

/**
* 计算被横向拖动view的left
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == getSurfaceView()) {
switch (mDragEdge) {
case Top:
case Bottom:
return getPaddingLeft();
case Left:
if (left < getPaddingLeft())
return getPaddingLeft();
if (left > getPaddingLeft() + mDragDistance)
return getPaddingLeft() + mDragDistance;
break;
case Right:
if (left > getPaddingLeft())
return getPaddingLeft();
if (left < getPaddingLeft() - mDragDistance)
return getPaddingLeft() - mDragDistance;
break;
}
} else if (child == getBottomView()) {

switch (mDragEdge) {
case Top:
case Bottom:
return getPaddingLeft();
case Left:
if (mShowMode == ShowMode.PullOut) {
if (left > getPaddingLeft())
return getPaddingLeft();
}
break;
case Right:
if (mShowMode == ShowMode.PullOut) {
if (left < getMeasuredWidth() - mDragDistance) {
return getMeasuredWidth() - mDragDistance;
}
}
break;
}
}
return left;
}

/**
* 计算被纵向拖动的view的top
*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if (child == getSurfaceView()) {
switch (mDragEdge) {
case Left:
case Right:
return getPaddingTop();
case Top:
if (top < getPaddingTop())
return getPaddingTop();
if (top > getPaddingTop() + mDragDistance)
return getPaddingTop() + mDragDistance;
break;
case Bottom:
if (top < getPaddingTop() - mDragDistance) {
return getPaddingTop() - mDragDistance;
}
if (top > getPaddingTop()) {
return getPaddingTop();
}
}
} else {
switch (mDragEdge) {
case Left:
case Right:
return getPaddingTop();
case Top:
if (mShowMode == ShowMode.PullOut) {
if (top > getPaddingTop())
return getPaddingTop();
} else {
if (getSurfaceView().getTop() + dy < getPaddingTop())
return getPaddingTop();
if (getSurfaceView().getTop() + dy > getPaddingTop()
+ mDragDistance)
return getPaddingTop() + mDragDistance;
}
break;
case Bottom:
if (mShowMode == ShowMode.PullOut) {
if (top < getMeasuredHeight() - mDragDistance)
return getMeasuredHeight() - mDragDistance;
} else {
if (getSurfaceView().getTop() + dy >= getPaddingTop())
return getPaddingTop();
if (getSurfaceView().getTop() + dy <= getPaddingTop()
- mDragDistance)
return getPaddingTop() - mDragDistance;
}
}
}
return top;
}

/**
* 确定要进行拖动的view
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == getSurfaceView() || child == getBottomView();
}

/**
* 确定横向拖动边界
*/
@Override
public int getViewHorizontalDragRange(View child) {
return mDragDistance;
}

/**
* 确定纵向拖动边界
*/
@Override
public int getViewVerticalDragRange(View child) {
return mDragDistance;
}

/**
* 当子控件被释放的时候调用,可以获取加速度的数据,来判断用户意图
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);

for (SwipeListener l : swipeListeners) {
l.onHandRelease(ZSwipeItem.this, xvel, yvel);
}

if (releasedChild == getSurfaceView()) {
processSurfaceRelease(xvel, yvel);
} else if (releasedChild == getBottomView()) {
if (getShowMode() == ShowMode.PullOut) {
processBottomPullOutRelease(xvel, yvel);
} else if (getShowMode() == ShowMode.LayDown) {
processBottomLayDownMode(xvel, yvel);
}
}

invalidate();
}

/**
* 当view的位置发生变化的时候调用,可以设置view的位置跟随手指移动
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {

int evLeft = getSurfaceView().getLeft();
int evTop = getSurfaceView().getTop();

if (changedView == getSurfaceView()) {

if (mShowMode == ShowMode.PullOut) {
if (mDragEdge == DragEdge.Left
|| mDragEdge == DragEdge.Right) {
getBottomView().offsetLeftAndRight(dx);
} else {
getBottomView().offsetTopAndBottom(dy);
}
}

} else if (changedView == getBottomView()) {

if (mShowMode == ShowMode.PullOut) {
getSurfaceView().offsetLeftAndRight(dx);
getSurfaceView().offsetTopAndBottom(dy);
} else {
Rect rect = computeBottomLayDown(mDragEdge);
getBottomView().layout(rect.left, rect.top, rect.right,
rect.bottom);

int newLeft = getSurfaceView().getLeft() + dx;
int newTop = getSurfaceView().getTop() + dy;

if (mDragEdge == DragEdge.Left
&& newLeft < getPaddingLeft())
newLeft = getPaddingLeft();
else if (mDragEdge == DragEdge.Right
&& newLeft > getPaddingLeft())
newLeft = getPaddingLeft();
else if (mDragEdge == DragEdge.Top
&& newTop < getPaddingTop())
newTop = getPaddingTop();
else if (mDragEdge == DragEdge.Bottom
&& newTop > getPaddingTop())
newTop = getPaddingTop();

getSurfaceView().layout(newLeft, newTop,
newLeft + getMeasuredWidth(),
newTop + getMeasuredHeight());
}
}

// 及时派发滑动事件
dispatchSwipeEvent(evLeft, evTop, dx, dy);

invalidate();
}
};

/**
* swipe事件分发器

* @param surfaceLeft
* @param surfaceTop
* @param dx
* @param dy
*/
protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop, int dx,
int dy) {
DragEdge edge = getDragEdge();
boolean open = true;
if (edge == DragEdge.Left) {
if (dx < 0)
open = false;
} else if (edge == DragEdge.Right) {
if (dx > 0)
open = false;
} else if (edge == DragEdge.Top) {
if (dy < 0)
open = false;
} else if (edge == DragEdge.Bottom) {
if (dy > 0)
open = false;
}

dispatchSwipeEvent(surfaceLeft, surfaceTop, open);
}

private int mEventCounter = 0;

/**
* swipe事件分发器

* @param surfaceLeft
* @param surfaceTop
* @param open
*/
protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop,
boolean open) {

safeBottomView();
Status status = getOpenStatus();

if (!swipeListeners.isEmpty()) {

Log.d(TAG, "swipeListeners=" + swipeListeners.size());

mEventCounter++;

if (mEventCounter == 1) {
if (open) {
swipeListeners.get(0).onStartOpen(ZSwipeItem.this);
swipeListeners.get(swipeListeners.size() - 1).onStartOpen(
ZSwipeItem.this);
} else {
swipeListeners.get(0).onStartClose(ZSwipeItem.this);
swipeListeners.get(swipeListeners.size() - 1).onStartClose(
ZSwipeItem.this);
}
}

for (SwipeListener l : swipeListeners) {
l.onUpdate(ZSwipeItem.this, surfaceLeft - getPaddingLeft(),
surfaceTop - getPaddingTop());
}

if (status == Status.Close) {
swipeListeners.get(0).onClose(ZSwipeItem.this);
swipeListeners.get(swipeListeners.size() - 1).onClose(
ZSwipeItem.this);
mEventCounter = 0;
} else if (status == Status.Open) {
getBottomView().setEnabled(true);
swipeListeners.get(0).onOpen(ZSwipeItem.this);
swipeListeners.get(swipeListeners.size() - 1).onOpen(
ZSwipeItem.this);
mEventCounter = 0;
}
}
}

/**
* 防止底布局获取到任何的触摸事件,特别是在LayDown模式
*/
private void safeBottomView() {
Status status = getOpenStatus();
ViewGroup bottom = getBottomView();

if (status == Status.Close) {
if (bottom.getVisibility() != INVISIBLE)
bottom.setVisibility(INVISIBLE);
} else {
if (bottom.getVisibility() != VISIBLE)
bottom.setVisibility(VISIBLE);
}
}

@Override
public void computeScroll() {
super.computeScroll();
// 让滚动一直进行下去
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}

/**
* 强制布局中必须嵌套两个ViewGroup布局,在新item出现的时候就会调用
*/
@SuppressLint("WrongCall")
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
if (childCount != 2) {
throw new IllegalStateException("You need 2  views in SwipeLayout");
}
if (!(getChildAt(0) instanceof ViewGroup)
|| !(getChildAt(1) instanceof ViewGroup)) {
throw new IllegalArgumentException(
"The 2 children in SwipeLayout must be an instance of ViewGroup");
}

if (mShowMode == ShowMode.PullOut) {
layoutPullOut();
} else if (mShowMode == ShowMode.LayDown) {
layoutLayDown();
}

safeBottomView();

if (mOnLayoutListeners != null)
for (int i = 0; i < mOnLayoutListeners.size(); i++) {
mOnLayoutListeners.get(i).onLayout(this);
}

}

private void layoutPullOut() {
Rect rect = computeSurfaceLayoutArea(false);
getSurfaceView().layout(rect.left, rect.top, rect.right, rect.bottom);
rect = computeBottomLayoutAreaViaSurface(ShowMode.PullOut, rect);
getBottomView().layout(rect.left, rect.top, rect.right, rect.bottom);
bringChildToFront(getSurfaceView());
}

private void layoutLayDown() {
Rect rect = computeSurfaceLayoutArea(false);
getSurfaceView().layout(rect.left, rect.top, rect.right, rect.bottom);
rect = computeBottomLayoutAreaViaSurface(ShowMode.LayDown, rect);
getBottomView().layout(rect.left, rect.top, rect.right, rect.bottom);
bringChildToFront(getSurfaceView());
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 初始化移动距离
if (mDragEdge == DragEdge.Left || mDragEdge == DragEdge.Right)
mDragDistance = getBottomView().getMeasuredWidth()
- dp2px(mHorizontalSwipeOffset);
else {
mDragDistance = getBottomView().getMeasuredHeight()
- dp2px(mVerticalSwipeOffset);
}
}

private boolean mTouchConsumedByChild = false;

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

if (!isEnabled() || !isEnabledInAdapterView()) {
return true;
}

if (!isSwipeEnabled()) {
return false;
}

int action = ev.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
Status status = getOpenStatus();
if (status == Status.Close) {
mTouchConsumedByChild = childNeedHandleTouchEvent(
getSurfaceView(), ev) != null;
} else if (status == Status.Open) {
mTouchConsumedByChild = childNeedHandleTouchEvent(
getBottomView(), ev) != null;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTouchConsumedByChild = false;
}

if (mTouchConsumedByChild)
return false;
return mDragHelper.shouldInterceptTouchEvent(ev);
}

/**
* ViewGroup的子View是否需要处理这个事件

* @param v
* @param event
* @return
*/
private View childNeedHandleTouchEvent(ViewGroup v, MotionEvent event) {
if (v == null)
return null;
if (v.onTouchEvent(event))
return v;

int childCount = v.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View child = v.getChildAt(i);
if (child instanceof ViewGroup) {
View grandChild = childNeedHandleTouchEvent((ViewGroup) child,
event);
if (grandChild != null)
return grandChild;
} else {
if (childNeedHandleTouchEvent(v.getChildAt(i), event))
return v.getChildAt(i);
}
}
return null;
}

/**
* 判断View是否要去处理触摸事件

* @param v
* @param event
* @return
*/
private boolean childNeedHandleTouchEvent(View v, MotionEvent event) {
if (v == null)
return false;

int[] loc = new int[2];
v.getLocationOnScreen(loc);
int left = loc[0];
int top = loc[1];

if (event.getRawX() > left && event.getRawX() < left + v.getWidth()
&& event.getRawY() > top
&& event.getRawY() < top + v.getHeight()) {
return v.onTouchEvent(event);
}

return false;
}

private float sX = -1, sY = -1;

@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabledInAdapterView() || !isEnabled())
return true;

if (!isSwipeEnabled())
return super.onTouchEvent(event);

int action = event.getActionMasked();
ViewParent parent = getParent();

gestureDetector.onTouchEvent(event);

Status status = getOpenStatus();
ViewGroup touching = null;
if (status == Status.Close) {
touching = getSurfaceView();
} else if (status == Status.Open) {
touching = getBottomView();
}

switch (action) {
case MotionEvent.ACTION_DOWN:
mDragHelper.processTouchEvent(event);
parent.requestDisallowInterceptTouchEvent(true);

sX = event.getRawX();
sY = event.getRawY();

if (touching != null)
touching.setPressed(true);

return true;
case MotionEvent.ACTION_MOVE: {
if (sX == -1 || sY == -1) {
// Trick:
// When in nested mode, we need to send a constructed
// ACTION_DOWN MotionEvent to mDragHelper, to help
// it initialize itself.
event.setAction(MotionEvent.ACTION_DOWN);
mDragHelper.processTouchEvent(event);
parent.requestDisallowInterceptTouchEvent(true);
sX = event.getRawX();
sY = event.getRawY();
return true;
}

float distanceX = event.getRawX() - sX;
float distanceY = event.getRawY() - sY;
float angle = Math.abs(distanceY / distanceX);
angle = (float) Math.toDegrees(Math.atan(angle));

boolean doNothing = false;
// 根据触摸角度,判断是否执行用户操作
if (mDragEdge == DragEdge.Right) {
boolean suitable = (status == Status.Open && distanceX > 0)
|| (status == Status.Close && distanceX < 0);
suitable = suitable || (status == Status.Middle);

if (angle > 30 || !suitable) {
doNothing = true;
}
}

if (mDragEdge == DragEdge.Left) {
boolean suitable = (status == Status.Open && distanceX < 0)
|| (status == Status.Close && distanceX > 0);
suitable = suitable || status == Status.Middle;

if (angle > 30 || !suitable) {
doNothing = true;
}
}

if (mDragEdge == DragEdge.Top) {
boolean suitable = (status == Status.Open && distanceY < 0)
|| (status == Status.Close && distanceY > 0);
suitable = suitable || status == Status.Middle;

if (angle < 60 || !suitable) {
doNothing = true;
}
}

if (mDragEdge == DragEdge.Bottom) {
boolean suitable = (status == Status.Open && distanceY > 0)
|| (status == Status.Close && distanceY < 0);
suitable = suitable || status == Status.Middle;

if (angle < 60 || !suitable) {
doNothing = true;
}
}

if (doNothing) {
// 拦截触摸事件
parent.requestDisallowInterceptTouchEvent(false);
return false;
} else {
if (touching != null) {
touching.setPressed(false);
}
parent.requestDisallowInterceptTouchEvent(true);
mDragHelper.processTouchEvent(event);
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
sX = -1;
sY = -1;
if (touching != null) {
touching.setPressed(false);
}
}
default:
parent.requestDisallowInterceptTouchEvent(true);
mDragHelper.processTouchEvent(event);
}

return true;
}

/**
* if working in {@link android.widget.AdapterView}, we should response
* {@link android.widget.Adapter} isEnable(int position).

* @return true when item is enabled, else disabled.
*/
private boolean isEnabledInAdapterView() {
@SuppressWarnings("rawtypes")
AdapterView adapterView = getAdapterView();
boolean enable = true;
if (adapterView != null) {
Adapter adapter = adapterView.getAdapter();
if (adapter != null) {
int p = adapterView.getPositionForView(ZSwipeItem.this);
if (adapter instanceof BaseAdapter) {
enable = ((BaseAdapter) adapter).isEnabled(p);
} else if (adapter instanceof ListAdapter) {
enable = ((ListAdapter) adapter).isEnabled(p);
}
}
}
return enable;
}

public void setSwipeEnabled(boolean enabled) {
mSwipeEnabled = enabled;
}

public boolean isSwipeEnabled() {
return mSwipeEnabled;
}

@SuppressWarnings("rawtypes")
private AdapterView getAdapterView() {
ViewParent t = getParent();
while (t != null) {
if (t instanceof AdapterView) {
return (AdapterView) t;
}
t = t.getParent();
}
return null;
}

private void performAdapterViewItemClick(MotionEvent e) {
ViewParent t = getParent();

Log.d(TAG, "performAdapterViewItemClick()");

while (t != null) {
if (t instanceof AdapterView) {
@SuppressWarnings("rawtypes")
AdapterView view = (AdapterView) t;
int p = view.getPositionForView(ZSwipeItem.this);
if (p != AdapterView.INVALID_POSITION
&& view.performItemClick(
view.getChildAt(p
- view.getFirstVisiblePosition()), p,
view.getAdapter().getItemId(p)))
return;
} else {
if (t instanceof View && ((View) t).performClick())
return;
}

t = t.getParent();
}
}

private GestureDetector gestureDetector = new GestureDetector(getContext(),
new SwipeDetector());

/**
* 手势监听器,通过调用performItemClick、performItemLongClick,来解决item的点击问题,


*/
private class SwipeDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}

@Override
public boolean onSingleTapUp(MotionEvent e) {
// 当用户单击之后,手指抬起的时候调用,如果没有双击监听器,就直接调用
performAdapterViewItemClick(e);
return true;
}

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// 这个方法只有在确认用户不会发生双击事件的时候调用
return false;
}

@Override
public void onLongPress(MotionEvent e) {
// 长按事件
performLongClick();
}

@Override
public boolean onDoubleTap(MotionEvent e) {
return false;
}
}

public void setDragEdge(DragEdge dragEdge) {
mDragEdge = dragEdge;
requestLayout();
}

public void setShowMode(ShowMode mode) {
mShowMode = mode;
requestLayout();
}

public DragEdge getDragEdge() {
return mDragEdge;
}

public int getDragDistance() {
return mDragDistance;
}

public ShowMode getShowMode() {
return mShowMode;
}

public ViewGroup getSurfaceView() {
return (ViewGroup) getChildAt(1);
}

public ViewGroup getBottomView() {
return (ViewGroup) getChildAt(0);
}

public enum Status {
Middle, Open, Close
}

/**
* 获取当前的开启状态

* @return
*/
public Status getOpenStatus() {
int surfaceLeft = getSurfaceView().getLeft();
int surfaceTop = getSurfaceView().getTop();
if (surfaceLeft == getPaddingLeft() && surfaceTop == getPaddingTop())
return Status.Close;

if (surfaceLeft == (getPaddingLeft() - mDragDistance)
|| surfaceLeft == (getPaddingLeft() + mDragDistance)
|| surfaceTop == (getPaddingTop() - mDragDistance)
|| surfaceTop == (getPaddingTop() + mDragDistance))
return Status.Open;

return Status.Middle;
}

/**
* 执行前布局的释放过程

* @param xvel
* @param yvel
*/
private void processSurfaceRelease(float xvel, float yvel) {
if (xvel == 0 && getOpenStatus() == Status.Middle)
close();

if (mDragEdge == DragEdge.Left || mDragEdge == DragEdge.Right) {
if (xvel > 0) {
if (mDragEdge == DragEdge.Left)
open();
else
close();
}
if (xvel < 0) {
if (mDragEdge == DragEdge.Left)
close();
else
open();
}
} else {
if (yvel > 0) {
if (mDragEdge == DragEdge.Top)
open();
else
close();
}
if (yvel < 0) {
if (mDragEdge == DragEdge.Top)
close();
else
open();
}
}
}

/**
* 执行PullOut模式下,底布局的释放过程

* @param xvel
* @param yvel
*/
private void processBottomPullOutRelease(float xvel, float yvel) {

if (xvel == 0 && getOpenStatus() == Status.Middle)
close();

if (mDragEdge == DragEdge.Left || mDragEdge == DragEdge.Right) {
if (xvel > 0) {
if (mDragEdge == DragEdge.Left)
open();
else
close();
}
if (xvel < 0) {
if (mDragEdge == DragEdge.Left)
close();
else
open();
}
} else {
if (yvel > 0) {
if (mDragEdge == DragEdge.Top)
open();
else
close();
}

if (yvel < 0) {
if (mDragEdge == DragEdge.Top)
close();
else
open();
}
}
}

/**
* 执行LayDown模式下,底布局的释放过程

* @param xvel
* @param yvel
*/
private void processBottomLayDownMode(float xvel, float yvel) {

if (xvel == 0 && getOpenStatus() == Status.Middle)
close();

int l = getPaddingLeft(), t = getPaddingTop();

if (xvel < 0 && mDragEdge == DragEdge.Right)
l -= mDragDistance;
if (xvel > 0 && mDragEdge == DragEdge.Left)
l += mDragDistance;

if (yvel > 0 && mDragEdge == DragEdge.Top)
t += mDragDistance;
if (yvel < 0 && mDragEdge == DragEdge.Bottom)
t -= mDragDistance;

mDragHelper.smoothSlideViewTo(getSurfaceView(), l, t);
invalidate();
}

public void open() {
open(true, true);
}

public void open(boolean smooth) {
open(smooth, true);
}

public void open(boolean smooth, boolean notify) {
ViewGroup surface = getSurfaceView(), bottom = getBottomView();
int dx, dy;
Rect rect = computeSurfaceLayoutArea(true);
if (smooth) {
mDragHelper
.smoothSlideViewTo(getSurfaceView(), rect.left, rect.top);
} else {
dx = rect.left - surface.getLeft();
dy = rect.top - surface.getTop();
surface.layout(rect.left, rect.top, rect.right, rect.bottom);
if (getShowMode() == ShowMode.PullOut) {
Rect bRect = computeBottomLayoutAreaViaSurface(
ShowMode.PullOut, rect);
bottom.layout(bRect.left, bRect.top, bRect.right, bRect.bottom);
}
if (notify) {
dispatchSwipeEvent(rect.left, rect.top, dx, dy);
} else {
safeBottomView();
}
}
invalidate();
}

public void close() {
close(true, true);
}

public void close(boolean smooth) {
close(smooth, true);
}

public void close(boolean smooth, boolean notify) {
ViewGroup surface = getSurfaceView();
int dx, dy;
if (smooth)
mDragHelper.smoothSlideViewTo(getSurfaceView(), getPaddingLeft(),
getPaddingTop());
else {
Rect rect = computeSurfaceLayoutArea(false);
dx = rect.left - surface.getLeft();
dy = rect.top - surface.getTop();
surface.layout(rect.left, rect.top, rect.right, rect.bottom);
if (notify) {
dispatchSwipeEvent(rect.left, rect.top, dx, dy);
} else {
safeBottomView();
}
}
invalidate();
}

public void toggle() {
toggle(true);
}

public void toggle(boolean smooth) {
if (getOpenStatus() == Status.Open)
close(smooth);
else if (getOpenStatus() == Status.Close)
open(smooth);
}

private Rect computeSurfaceLayoutArea(boolean open) {
int l = getPaddingLeft(), t = getPaddingTop();
if (open) {
if (mDragEdge == DragEdge.Left)
l = getPaddingLeft() + mDragDistance;
else if (mDragEdge == DragEdge.Right)
l = getPaddingLeft() - mDragDistance;
else if (mDragEdge == DragEdge.Top)
t = getPaddingTop() + mDragDistance;
else
t = getPaddingTop() - mDragDistance;
}
return new Rect(l, t, l + getMeasuredWidth(), t + getMeasuredHeight());
}

private Rect computeBottomLayoutAreaViaSurface(ShowMode mode,
Rect surfaceArea) {
Rect rect = surfaceArea;

int bl = rect.left, bt = rect.top, br = rect.right, bb = rect.bottom;
if (mode == ShowMode.PullOut) {
if (mDragEdge == DragEdge.Left)
bl = rect.left - mDragDistance;
else if (mDragEdge == DragEdge.Right)
bl = rect.right;
else if (mDragEdge == DragEdge.Top)
bt = rect.top - mDragDistance;
else
bt = rect.bottom;

if (mDragEdge == DragEdge.Left || mDragEdge == DragEdge.Right) {
bb = rect.bottom;
br = bl + getBottomView().getMeasuredWidth();
} else {
bb = bt + getBottomView().getMeasuredHeight();
br = rect.right;
}
} else if (mode == ShowMode.LayDown) {
if (mDragEdge == DragEdge.Left)
br = bl + mDragDistance;
else if (mDragEdge == DragEdge.Right)
bl = br - mDragDistance;
else if (mDragEdge == DragEdge.Top)
bb = bt + mDragDistance;
else
bt = bb - mDragDistance;

}
return new Rect(bl, bt, br, bb);

}

private Rect computeBottomLayDown(DragEdge dragEdge) {
int bl = getPaddingLeft(), bt = getPaddingTop();
int br, bb;
if (dragEdge == DragEdge.Right) {
bl = getMeasuredWidth() - mDragDistance;
} else if (dragEdge == DragEdge.Bottom) {
bt = getMeasuredHeight() - mDragDistance;
}
if (dragEdge == DragEdge.Left || dragEdge == DragEdge.Right) {
br = bl + mDragDistance;
bb = bt + getMeasuredHeight();
} else {
br = bl + getMeasuredWidth();
bb = bt + mDragDistance;
}
return new Rect(bl, bt, br, bb);
}

public void addSwipeListener(SwipeListener l) {

if (swipeListeners.size() == 2) {
swipeListeners.remove(1);
}

swipeListeners.add(l);
}

public void removeSwipeListener(SwipeListener l) {
swipeListeners.remove(l);
}

public void addOnLayoutListener(OnSwipeLayoutListener l) {
if (mOnLayoutListeners == null)
mOnLayoutListeners = new ArrayList<OnSwipeLayoutListener>();
mOnLayoutListeners.add(l);
}

public void removeOnLayoutListener(OnSwipeLayoutListener l) {
if (mOnLayoutListeners != null)
mOnLayoutListeners.remove(l);
}

private int dp2px(float dp) {
return (int) (dp
* getContext().getResources().getDisplayMetrics().density + 0.5f);
}

}

通过swipeItem.addSwipeListener()可以给滑动item添加各种事件监听,推荐使用SimpleSwipeListener的匿名类,这样就可以只重写自己关心的事件,onOpen和onClose是打开关闭的时候调用,onStartXX则是在动作一开始就调用,因此,如果需要改变后面布局的状态,请在onStartXX的时候调用,onHandRelease()则是在用户手指离开屏幕的时候调用,参数layout是事件发生的ZSwipeItem对象、xvel和yvel则是手势放开瞬间,x和y方向的加速度。onUpdate()在滑动的时候一直会调用,leftOffset和topOffset是距离左上角坐标的距离。

接下来我们就来写我们最终的组合控件:XYListView

package com.zy.zlistview.view;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;

import com.socks.zlistview.R;

public class ZListView extends ListView {

private final static int SCROLLBACK_HEADER = 0;
private final static int SCROLLBACK_FOOTER = 1;
// 滑动时长
private final static int SCROLL_DURATION = 400;
// 加载更多的距离
private final static int PULL_LOAD_MORE_DELTA = 100;
// 滑动比例
private final static float OFFSET_RADIO = 2f;
// 记录按下点的y坐标
private float lastY;
// 用来回滚
private Scroller scroller;
private IXListViewListener mListViewListener;
private ZListViewHeader headerView;
private RelativeLayout headerViewContent;
// header的高度
private int headerHeight;
// 是否能够刷新
private boolean enableRefresh = true;
// 是否正在刷新
private boolean isRefreashing = false;
// footer
private ZListViewFooter footerView;
// 是否可以加载更多
private boolean enableLoadMore;
// 是否正在加载
private boolean isLoadingMore;
// 是否footer准备状态
private boolean isFooterAdd = false;
// item的总数
private int totalItemCount;
// 记录是从header还是footer返回
private int mScrollBack;
SharedPreferences sp=null;
public ZListView(Context context) {
super(context);
initView(context);
}

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

public ZListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}

private void initView(Context context) {
sp=getContext().getSharedPreferences("isResh", 0);
scroller = new Scroller(context, new DecelerateInterpolator());
headerView = new ZListViewHeader(context);
footerView = new ZListViewFooter(context);

headerViewContent = (RelativeLayout) headerView
.findViewById(R.id.xlistview_header_content);
headerView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
headerHeight = headerViewContent.getHeight();
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
addHeaderView(headerView);

}

@Override
public void setAdapter(ListAdapter adapter) {
// 确保footer最后添加并且只添加一次
if (isFooterAdd == false) {
isFooterAdd = true;
addFooterView(footerView);
}
super.setAdapter(adapter);

}

@SuppressLint({ "SimpleDateFormat", "ClickableViewAccessibility" }) @Override
public boolean onTouchEvent(MotionEvent ev) {

totalItemCount = getAdapter().getCount();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录按下的坐标
lastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
// 计算移动距离
float deltaY = ev.getRawY() - lastY;
lastY = ev.getRawY();
// 是第一项并且标题已经显示或者是在下拉
if (getFirstVisiblePosition() == 0
&& (headerView.getVisiableHeight() > 0 || deltaY > 0)) {
updateHeaderHeight(deltaY / OFFSET_RADIO);
} else if (getLastVisiblePosition() == totalItemCount - 1
&& (footerView.getBottomMargin() > 0 || deltaY < 0)) {
updateFooterHeight(-deltaY / OFFSET_RADIO);
}
break;

case MotionEvent.ACTION_UP:

if (getFirstVisiblePosition() == 0) {
if (enableRefresh
&& headerView.getVisiableHeight() > headerHeight) {
isRefreashing = true;

Editor editor=sp.edit();
editor.putBoolean("isresh", true);
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  HH时mm分ss秒");
String date = format.format(new Date());
editor.putString("date", date);
editor.commit();
headerView.setState(ZListViewHeader.STATE_REFRESHING);
if (mListViewListener != null) {
mListViewListener.onRefresh();
}
}
resetHeaderHeight();
} else if (getLastVisiblePosition() == totalItemCount - 1) {
if (enableLoadMore
&& footerView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
startLoadMore();
}
resetFooterHeight();
}
break;
}
return super.onTouchEvent(ev);
}

@Override
public void computeScroll() {

// 松手之后调用
if (scroller.computeScrollOffset()) {

if (mScrollBack == SCROLLBACK_HEADER) {
headerView.setVisiableHeight(scroller.getCurrY());
} else {
footerView.setBottomMargin(scroller.getCurrY());
}
postInvalidate();
}
super.computeScroll();

}

public void setPullRefreshEnable(boolean enable) {
enableRefresh = enable;

if (!enableRefresh) {
headerView.hide();
} else {
headerView.show();
}
}

public void setPullLoadEnable(boolean enable) {
enableLoadMore = enable;
if (!enableLoadMore) {
footerView.hide();
footerView.setOnClickListener(null);
} else {
isLoadingMore = false;
footerView.show();
footerView.setState(ZListViewFooter.STATE_NORMAL);
footerView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startLoadMore();
}
});
}
}
public void stopRefresh() {
if (isRefreashing == true) {
isRefreashing = false;
resetHeaderHeight();
}
}

public void stopLoadMore() {
if (isLoadingMore == true) {
isLoadingMore = false;
footerView.setState(ZListViewFooter.STATE_NORMAL);
}
}

private void updateHeaderHeight(float delta) {
headerView.setVisiableHeight((int) delta
+ headerView.getVisiableHeight());
// 未处于刷新状态,更新箭头
if (enableRefresh && !isRefreashing) {
if (headerView.getVisiableHeight() > headerHeight) {
headerView.setState(ZListViewHeader.STATE_READY);
} else {
headerView.setState(ZListViewHeader.STATE_NORMAL);
}
}

}

private void resetHeaderHeight() {
// 当前的可见高度
int height = headerView.getVisiableHeight();
// 如果正在刷新并且高度没有完全展示
if ((isRefreashing && height <= headerHeight) || (height == 0)) {
return;
}
// 默认会回滚到header的位置
int finalHeight = 0;
// 如果是正在刷新状态,则回滚到header的高度
if (isRefreashing && height > headerHeight) {
finalHeight = headerHeight;
}
mScrollBack = SCROLLBACK_HEADER;
// 回滚到指定位置
scroller.startScroll(0, height, 0, finalHeight - height,
SCROLL_DURATION);
// 触发computeScroll
invalidate();
}

private void updateFooterHeight(float delta) {
int height = footerView.getBottomMargin() + (int) delta;
if (enableLoadMore && !isLoadingMore) {
if (height > PULL_LOAD_MORE_DELTA) {
footerView.setState(ZListViewFooter.STATE_READY);
} else {
footerView.setState(ZListViewFooter.STATE_NORMAL);
}
}
footerView.setBottomMargin(height);

}

private void resetFooterHeight() {
int bottomMargin = footerView.getBottomMargin();
if (bottomMargin > 0) {
mScrollBack = SCROLLBACK_FOOTER;
scroller.startScroll(0, bottomMargin, 0, -bottomMargin,
SCROLL_DURATION);
invalidate();
}
}

private void startLoadMore() {
isLoadingMore = true;
footerView.setState(ZListViewFooter.STATE_LOADING);
if (mListViewListener != null) {
mListViewListener.onLoadMore();
}
}

public void setXListViewListener(IXListViewListener l) {
mListViewListener = l;
}

public interface IXListViewListener {

public void onRefresh();

public void onLoadMore();
}
}

基本我们的控件就写完了,接下来就是适配器的书写了,其实和我们平常写的没有什么两样,然后我们就来实现它

package com.zy.zlistview.adapter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

import com.zy.zlistview.enums.Mode;
import com.zy.zlistview.listener.SwipeListener;
import com.zy.zlistview.view.ZSwipeItem;
public abstract class BaseSwipeAdapter extends BaseAdapter {

public static final String TAG = "BaseSwipeAdapter";

public final int INVALID_POSITION = -1;
/**
* 显示模式,默认单开
*/
private Mode mode = Mode.Single;
/**
* 当前打开的item的position
*/
protected int openPosition = INVALID_POSITION;
/**
* 当前打开的所有item的position
*/
protected Set<Integer> openPositions = new HashSet<Integer>();
/**
* 当前打开的所有ZSwipeItem对象
*/
protected Set<ZSwipeItem> mShownLayouts = new HashSet<ZSwipeItem>();

public abstract int getSwipeLayoutResourceId(int position);

public abstract View generateView(int position, ViewGroup parent);

public abstract void fillValues(int position, View convertView);

@Override
public final View getView(int position, View convertView, ViewGroup parent) {

if (convertView == null) {
convertView = generateView(position, parent);
initialize(convertView, position);
} else {
updateConvertView(convertView, position);
}
fillValues(position, convertView);
return convertView;

}

/**
* 初始化item布局调用

* @param target
* @param position
*/
public void initialize(View target, int position) {

int resId = getSwipeLayoutResourceId(position);

OnLayoutListener onLayoutListener = new OnLayoutListener(position);
ZSwipeItem swipeLayout = (ZSwipeItem) target.findViewById(resId);
if (swipeLayout == null)
throw new IllegalStateException(
"can not find SwipeLayout in target view");

SwipeMemory swipeMemory = new SwipeMemory(position);
// 添加滑动监听器
swipeLayout.addSwipeListener(swipeMemory);
// 添加布局监听器
swipeLayout.addOnLayoutListener(onLayoutListener);
swipeLayout.setTag(resId, new ValueBox(position, swipeMemory,
onLayoutListener));
mShownLayouts.add(swipeLayout);

}

/**
* 复用item布局的时候调用

* @param target
* @param position
*/
public void updateConvertView(View target, int position) {

int resId = getSwipeLayoutResourceId(position);

ZSwipeItem swipeLayout = (ZSwipeItem) target.findViewById(resId);
if (swipeLayout == null)
throw new IllegalStateException(
"can not find SwipeLayout in target view");

ValueBox valueBox = (ValueBox) swipeLayout.getTag(resId);
valueBox.swipeMemory.setPosition(position);
valueBox.onLayoutListener.setPosition(position);
valueBox.position = position;

Log.d(TAG, "updateConvertView=" + position);

}

private void closeAllExcept(ZSwipeItem layout) {

for (ZSwipeItem s : mShownLayouts) {
if (s != layout)
s.close();
}
}

/**
* 获取打开的所有的item的position信息

* @return
*/
public List<Integer> getOpenItems() {

if (mode == Mode.Multiple) {
return new ArrayList<Integer>(openPositions);
} else {
return Arrays.asList(openPosition);
}
}

/**
* position位置的item是否打开

* @param position
* @return
*/
public boolean isOpen(int position) {
if (mode == Mode.Multiple) {
return openPositions.contains(position);
} else {
return openPosition == position;
}
}

public Mode getMode() {
return mode;
}

public void setMode(Mode mode) {
this.mode = mode;
openPositions.clear();
mShownLayouts.clear();
openPosition = INVALID_POSITION;
}

class ValueBox {
OnLayoutListener onLayoutListener;
SwipeMemory swipeMemory;
int position;

ValueBox(int position, SwipeMemory swipeMemory,
OnLayoutListener onLayoutListener) {
this.swipeMemory = swipeMemory;
this.onLayoutListener = onLayoutListener;
this.position = position;
}
}
public interface OnSwipeLayoutListener {
public void onLayout(ZSwipeItem v);
}
class OnLayoutListener implements OnSwipeLayoutListener {

private int position;

OnLayoutListener(int position) {
this.position = position;
}

public void setPosition(int position) {
this.position = position;
}

@Override
public void onLayout(ZSwipeItem v) {

if (isOpen(position)) {
v.open(false, false);
} else {
v.close(false, false);
}
}
}
class SwipeMemory implements SwipeListener {

private int position;

SwipeMemory(int position) {
this.position = position;
}

@Override
public void onClose(ZSwipeItem layout) {

if (mode == Mode.Multiple) {
openPositions.remove(position);
} else {
openPosition = INVALID_POSITION;
}
}

@Override
public void onStartOpen(ZSwipeItem layout) {

if (mode == Mode.Single) {
closeAllExcept(layout);
}
}

@Override
public void onOpen(ZSwipeItem layout) {

if (mode == Mode.Multiple)
openPositions.add(position);
else {
closeAllExcept(layout);
openPosition = position;
}
}

public void setPosition(int position) {
this.position = position;
}

@Override
public void onStartClose(ZSwipeItem layout) {
// TODO Auto-generated method stub

}

@Override
public void onUpdate(ZSwipeItem layout, int leftOffset, int topOffset) {
// TODO Auto-generated method stub

}

@Override
public void onHandRelease(ZSwipeItem layout, float xvel, float yvel) {
// TODO Auto-generated method stub

}
}
}

最后就是初始化数据以及上拉和下拉数据的加载了,直接看代码不多说:

package com.zy.zlistview;
import java.util.LinkedList;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Toast;
import com.socks.zlistview.R;
import com.zy.zlistview.view.ZListView;
import com.zy.zlistview.view.ZListView.IXListViewListener;
public class MainActivity extends Activity {
protected static final String TAG = "MainActivity";
private ZListView listView;
private Handler handler = new Handler();
private LinkedList<String> lists = new LinkedList<String>();
private ListViewAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ZListView) findViewById(R.id.listview);
initData();
listView.setXListViewListener(new IXListViewListener() {
@Override
public void onRefresh() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
PullRefreshTask rTask = new PullRefreshTask();
rTask.execute(1000);

}
}, 1000);
}

@Override
public void onLoadMore() {

handler.postDelayed(new Runnable() {
@Override
public void run() {
UpRefreshTask rTask = new UpRefreshTask();
rTask.execute(1000);
}
}, 1000);
}
});

listView.setPullLoadEnable(true);
listView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {

Toast.makeText(MainActivity.this, "onItemClick=" + position,
Toast.LENGTH_SHORT).show();

}

});

listView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(MainActivity.this,
"onItemLongClick=" + position, Toast.LENGTH_SHORT)
.show();
return true;
}
});
mAdapter = new ListViewAdapter(this, lists);
listView.setAdapter(mAdapter);
}
private void initData() {
for (int i = 0; i < 20; i++) {
lists.add("我是测试数据" + i);
}
}

// AsyncTask异步任务加载数据
class PullRefreshTask extends AsyncTask<Integer, Integer, String> {
@Override
protected String doInBackground(Integer... params) {
try {
Thread.sleep(params[0]);
} catch (Exception e) {
e.printStackTrace();
}
// 在data最前添加数据
for (int j = 0; j < 5; j++) {
lists.addFirst("下拉刷新加载的数据" + j);
}

return null;
}

@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
mAdapter.notifyDataSetChanged();//数据更新
listView.stopRefresh();//头部状态重置
}
}

// AsyncTask异步任务加载数据
class UpRefreshTask extends AsyncTask<Integer, Integer, String> {
@Override
protected String doInBackground(Integer... params) {
try {
Thread.sleep(params[0]);
} catch (Exception e) {
e.printStackTrace();
}
// 在data最前添加数据
for (int j = 0; j < 5; j++) {
lists.addLast("上拉加载的数据" + j);
}
return null;
}

@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
mAdapter.notifyDataSetChanged();//数据更新
listView.stopLoadMore();//底部状态重置
}
}
}

效果图请看:效果还是蛮不错的

里面的难点就是SwipeLayout里面奋力出来的,那里面条目是不只有删除功能还有打开功能或其他,所以就是条目的滑动是最难理解,因此,当我们的item被滑动的时候,就会不断的调用这个onLayout方法,我们判断当前打开的position,然后恢复现场即可。然后项目里不再是我们常见的scrollListener,看代码才知道ViewTreeObserver里面实现了OnScrollChangedListener,smooth代表是否是平滑移动的,如果是的话,就调用了ViewDragHelper.smoothSlideViewTo()。其实在ViewDragHelper里面有一个Scroller,这个方法就是通过Scroller类来实现的,但是只这样写还不行,我们还需要重写computeScroll(),然后用下面的代码,让滚动一直持续下去,否则View是不会滚动起来的。如果不是smooth的话,就直接layout(),把View的位置定位过去了。这里面的item的实现参考了大神凯子哥(http://blog.csdn.net/zhaokaiqiang1992/article/details)的项目,然后里面的一些bug进行了修复,但是发现还是有bug的,虽然左右滑动条目然后listView上下滚动的冲突修复了,但是还是感觉有些小问题,但是绝对是可以直接项目里使用。

源码下载:点击打开链接

醉了,要睡觉了。。

安卓listView实现下拉刷新上拉加载滑动仿QQ的删除功能的更多相关文章

  1. Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView

    在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/de ...

  2. SwipeRefreshLayout实现下拉刷新上滑加载

    1. 效果图 2.RefreshLayout.java package myapplication.com.myapplication; import android.content.Context; ...

  3. 移动端下拉刷新上拉加载-mescroll.js插件

    最近无意间看到有这么一个上拉刷新下拉加载的插件 -- mescroll.js,个人感觉挺好用的,官网地址是:http://www.mescroll.com 然后我就看了一下文档,简单的写了一个小dem ...

  4. ListView实现Item上下拖动交换位置 并且实现下拉刷新 上拉加载更多

    ListView实现Item上下拖动交换位置  并且实现下拉刷新  上拉加载更多 package com.example.ListViewDragItem; import android.app.Ac ...

  5. ListView下拉刷新上拉加载更多实现

    这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...

  6. listview下拉刷新上拉加载扩展(三)-仿最新版美团外卖

    本篇是基于上篇listview下拉刷新上拉加载扩展(二)-仿美团外卖改造而来,主要调整了headview的布局,并加了两个背景动画,看似高大上,其实很简单: as源码地址:http://downloa ...

  7. listview下拉刷新上拉加载扩展(二)-仿美团外卖

    经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...

  8. MaterialRefreshLayout+ListView 下拉刷新 上拉加载

    效果图是这样的,有入侵式的,非入侵式的,带波浪效果的......就那几个属性,都给出来了,自己去试就行. 下拉刷新 上拉加载 关于下拉刷新-上拉加载的效果,有许许多多的实现方式,百度了一下竟然有几十种 ...

  9. react-native-page-listview使用方法(自定义FlatList/ListView下拉刷新,上拉加载更多,方便的实现分页)

    react-native-page-listview 对ListView/FlatList的封装,可以很方便的分页加载网络数据,还支持自定义下拉刷新View和上拉加载更多的View.兼容高版本Flat ...

随机推荐

  1. 向windows添加环境变量

    以NASM为例,软件安装完毕后,启动Windows操作系统的命令窗口,在安装目录(比如C:\Program Files\NASM)下运行nasm是ok的,但是在其他任意目录下运行nasm就会报错. 这 ...

  2. 一个小栗子聊聊JAVA泛型基础

    背景 周五本该是愉快的,可是今天花了一个早上查问题,为什么要花一个早上?我把原因总结为两点: 日志信息严重丢失,茫茫代码毫无头绪. 对泛型的认识不够,导致代码出现了BUG. 第一个原因可以通过以后编码 ...

  3. Git 常用命令 思维导图(转)

    Git 是一个很强大的分布式版本控制系统.它不但适用于管理大型开源软件的源代码,管理私人的文档和源代码也有很多优势. 本来想着只把最有用.最常用的 Git 命令记下来,但是总觉得这个也挺有用.那个也用 ...

  4. POSTGRESQL 创建表结构、修改字段、导入导出数据库(支持CSV)

    这两个月经常使用postgresql,总结一些经常使用的语句: --创建表 CREATE TABLE customers ( customerid SERIAL primary key , compa ...

  5. angularjs中templateUrl的路径问题

    angularjs中templateUrl的路径问题 templateUrl的相对路径是现对于app主页面的,因为template使用js来加载的.

  6. Java Netty (1)

    Netty是由JBOSS提供的一个java开源框架,本质上也是NIO,是对NIO的封装,比NIO更加高级,功能更加强大.可以说发展的路线是IO->NIO->Netty. ServerBoo ...

  7. Python学习笔记_05:使用Flask+MySQL实现用户登陆注册以及增删查改操作

    前言:本文代码参考自两篇英文博客,具体来源点击文末代码链接中文档说明. (PS:代码运行Python版本为2.7.14) 运行效果: 首页: 注册页面: 登陆界面: 管理员登陆后界面: 添加.删除.修 ...

  8. Linux文件系统操作

    1:查看磁盘使用情况 df [-选项] 目录名 2:查看目录下文件大小 du 目录名称 3:实体链接 创建inode产生一个新文件名,链接到一个已有的文件. 限制:不能垮文件系统进行实体链接:不能链接 ...

  9. Maven常用指令

    Maven是通过pom.xml配置好一系列操作,然后通过命令行来进行控制操作的.格式为 mvn 命令 常用命令主要有: mvn 命令 -v:查看maven版本 compile:编译项目 test:测试 ...

  10. java Map常用方法封装

      java Map常用方法封装 CreationTime--2018年7月16日15点59分 Author:Marydon 1.准备工作 import java.util.HashMap; impo ...