編輯:關於Android編程
大家對這些功能都是看的多了,然後對上拉刷新和下拉加載的原理都是非常清楚的,所以實現這功能其實也就是為了讓大家能夠從眾多的同行們來進行比較學習而已,雖然即使是這樣,但是面試的時候面試官還是會問你上拉和下拉是怎麼實現的,滑動刪除功能是怎麼實現,其實要實現這些功能又不是唯一的方法,但是基本上思想都是一致的。然後gitup上的這些例子是非常的多,然後實現的也是大同小異但是也不能不讓我們去球童存異。作為天朝的程序員即使是一個伸手黨也不必太覺得羞恥,能把別人的東西來改一改或者沿用別人的思想來模仿也是不錯的。然後公司的項目也可能不建議直接去導入XlistView來實現下拉的功能,所以我們可能就需要自己去實現一些這樣的功能了,廢話不多說,稍微就介紹下原理,主要還是代碼為主,即使看到代碼和別人大同小異也不要計較,因為這些功能純粹就是模仿好了,當然參考別人的代碼也不是什麼丟臉的事,關鍵的是對自己有幫助就好了啊。
我們要寫這樣一個功能當然的先從自定義的listView開始了,說起來其實就是一個組合控件,它就由3部分組成:head 、content、footer,所以我們的代碼也就可以分開來實現了額。先我們寫頭部的布局和功能。
xlistview_header.xml
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom" >
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_weight="1"
tools:ignore="UselessParent" >
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" >
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" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="上次更新:"
android:textSize="12dp" />
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/xlistview_header"
android:src="@drawable/zlistview_arrow" />
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/xlistview_header"
android:visibility="invisible" />
很容易發現頭部就是由刷新加載數據時顯示圖片,頭部中間顯示加載的狀態文字,如果是第一次不會提醒上次更新時間,如果不是第一次就會顯示上次的刷新時間,然後手指下拉時箭頭向下松開時箭頭向上,移動時顯示圓形加載進度。好了實現的思路也了解了吧。
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
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp"
tools:ignore="UselessParent" >
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:visibility="invisible"
>
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:visibility="invisible" />
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" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="@android:color/black"
android:textSize="14sp" />
底部的布局就更加的簡單了,正常情況下就一個文字提示你加載更多,當你上拉時就變為顯示進度條和加載狀態。功能實現也不是很復雜。直接代碼:
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
private List
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
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
/**
* 當前打開的所有ZSwipeItem對象
*/
protected Set
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
if (mode == Mode.Multiple) {
return new ArrayList
} 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
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(AdapterViewparent, View view,
int position, long id) {
Toast.makeText(MainActivity.this, "onItemClick=" + position,
Toast.LENGTH_SHORT).show();
}
});
listView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterViewparent, 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
@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
@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上下滾動的沖突修復了,但是還是感覺有些小問題,但是絕對是可以直接項目裡使用。
源碼下載:點擊打開鏈接
醉了,要睡覺了。。。
1 概述這裡我們會詳細講解matrix的各個方法,以及它的用法。matrix叫做矩陣,在前面講解 ColorFilter 的文章中,我們講解了ColorMatrix,他是
相信每個項目都會有用戶反饋建議等功能,這個實現的方法很多,下面是我實現的方法,供大家交流。首先看具體界面,三個字段。名字,郵箱為選填,可以為空,建議不能為空。如有需要可以
說明:這裡是借鑒:曉風殘月前輩的博客,他是將泰然網的跑酷教程,用cocos2d-x 2.X 版本重寫的,目前我正在學習cocos2d-X3.0 於是就用cocos2d-
今天主要修改了幾個bug,然後改了下背景圖片和圖標,添加了變動人數的功能,即:指定參與AA的人數,參與AA計算。還有就是利用getResources().getIdent