安卓listView实现下拉刷新上拉加载滑动仿QQ的删除功能

大家对这些功能都是看的多了,然后对上拉刷新和下拉加载的原理都是非常清楚的,所以实现这功能其实也就是为了让大家能够从众多的同行们来进行比较学习而已,虽然即使是这样,但是面试的时候面试官还是会问你上拉和下拉是怎么实现的,滑动删除功能是怎么实现,其实要实现这些功能又不是唯一的方法,但是基本上思想都是一致的。然后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上下滚动的冲突修复了,但是还是感觉有些小问题,但是绝对是可以直接项目里使用。

源码下载:点击打开链接

醉了,要睡觉了。。。

时间: 2024-08-05 11:12:38

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

最新Android ListView 下拉刷新 上滑加载

开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,之前大家最常用的应该是pull to refresh或它的变种版吧,google官方在最新的android.support.v4包中增加了一个新类SwipeRefreshLayout,地址 这个类的作用就是提供官方的下拉刷新,并且效果相当不错,而上拉加载更多则用我们自定义的listview,也是相当简单. 下拉刷新 简单的介绍下: 首先它是一个viewgroup,但是它只允许有一个子控件,子控件能是任何view,使用的时候,所在

十分钟实现ListView下拉刷新上滑加载更多

说到ListView下拉刷新几乎每个APP都会用到,所以ListView下拉刷新是很重要的,就像ListView优化一样是你必会的东西. ListView实现下拉刷新如果我们开发人员自己编写相对来说比较费事的,当我们使用第三方库之后我们再来开发这个功能就会省事很多.相比与自己实现可以少编写不少代码,Android-PullToRefresh库可以轻松实现ListView的下拉刷新功能. 要使用Android—PullToRefesh库对ListView实现下拉刷新要经过以下几个步骤: 1.下载A

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

在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/details/78781682 ,这里是看了之后,结合自己实际遇到的问题写的. 首先引入包. //下拉框 implementation 'com.android.support:recyclerview-v7:28.0.0-beta01' implementation 'com.scwang.smar

android 安卓 listview 支持下拉刷新 上拉加载更多

[1]重写listView import java.text.SimpleDateFormat; import java.util.Date; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGrou

重写ListView实现下拉刷新上拉加载

安卓本身的ListView没有刷新功能,要想实现这一功能,就得继承ListView并重写它的方法.同时也要实现其OnScrollListener这一接口. 下拉刷新,需要在原本的ListView上部添加一个Head,ListView本身提供了addHeadView()这一方法,但是没有实现刷新的功能,我们可以自定义Head的内容,并加上刷新的功能. 上拉加载更多数据与下拉刷新类似,是在ListView的下部添加一个Footer.具体实现代码如下: import java.text.SimpleD

ListView的下拉刷新+上拉加载(已有demo)

1.XListView因为添加了Header,会导致存储的数据+1,所以赋值时需要position-1.补充:当去掉HeaderView时,position不用-1. 2.提个建议:上拉加载更多,最好在onCreate()中就执行setAdapter,然后不论是空数据.还是有数据,只用更新适配器就行了. 一.XListView 2.用法 导入图中的me.maxwin.view包 提供了两个接口: IXListViewListener:触发下拉刷新,上拉加载更多.实现此接口时,onLoadMore

ListView的下拉刷新上拉加载以及带列的横向滚动

相信有很人做的项目估计都用的到这个.就是ListView的下拉刷新上拉加载还有就是列的横向滚动; PS:横向滚动带表头与固定列(相信蛮多人都有这样的需求吧?就是在ListView上支持很多列,然而设备屏幕宽度有限) PS:这是我个人在网上找的两个示例demo结合而成的一个示例demo,还可以继续拓展,后续有时间就会更新,大家互相学习 ListView下拉刷新上拉加载示例demo原文出处: http://blog.csdn.net/limb99/article/details/18901513 L

带你实现开发者头条APP(五)--RecyclerView下拉刷新上拉加载

转载请注明出处:http://blog.csdn.net/lowprofile_coding/article/details/51321896 一 .前言 最近实在太忙,一个多礼拜没有更新文章了,于是今晚加班加点把demo写出来,现在都12点了才开始写文章. 1.我们的目标 把RecyclerView下拉刷新上拉加载更多加入到我们的开发者头条APP中. 2.效果图 3.实现步骤 找一个带上拉刷新下载加载更多的RecyclerView开源库,我们要站在巨人的肩膀上 下载下来自己先运行下demo,然

Android 下拉刷新上拉加载 多种应用场景 超级大放送(上)

转载请标明原文地址:http://blog.csdn.net/yalinfendou/article/details/47707017 关于Android下拉刷新上拉加载,网上的Demo太多太多了,这里不是介绍怎么去实现下拉刷新上拉加载,而是针对下拉刷新上拉加载常用的一些应用场景就行了一些总结,包含了下拉刷新上拉加载过程中遇到的一些手势冲突问题的解决方法(只能算是抛砖引玉). 去年9月的时候,那时自己正在独立做Android项目.记得刚刚写完那个ListView列表页面(木有下拉刷新,上拉加载)