緒論
最近項(xiàng)目里面用到了下拉刷新和左滑刪除癞松,網(wǎng)上找了找并沒(méi)有可以用的浆竭,有比較好的左滑刪除鬼贱,但是并沒(méi)有和下拉刷新上拉加載結(jié)合到一起,要不就是一些比較水的結(jié)合始赎,并不能在項(xiàng)目里面使用和橙,小編一著急自己組合了一個(gè)仔燕,做完了和QQ的對(duì)比了一下,并沒(méi)有太大區(qū)別魔招,今天分享給大家晰搀,其實(shí)并不難,但是不知道為什么網(wǎng)上沒(méi)有比較好的Demo仆百,當(dāng)你的項(xiàng)目真的很急的時(shí)候厕隧,又沒(méi)有比較好的Demo,那么"那條友誼的小船兒真是說(shuō)翻就翻啊",好了俄周,下面先來(lái)具體看一下實(shí)現(xiàn)后的效果吧:
代碼已經(jīng)上傳到Github上了,小伙伴們記得star和follow哦
https://github.com/Hankkin/SwipeRefreshDemo
還不錯(cuò)吧髓迎?比QQ多了個(gè)上拉加載峦朗,好了看看怎么實(shí)現(xiàn)的吧,小編在之前的游客評(píng)論中了解到排龄,代碼注釋很重要波势,所以盡量把注釋寫(xiě)的很清楚:
實(shí)現(xiàn)思路
由于時(shí)間有限,左滑菜單是在網(wǎng)上找的Demo
- 自定義下拉刷新頭橄维、尾
- 手勢(shì)判斷尺铣,根據(jù)滑動(dòng)距離顯示頭部下拉布局
- 判斷是否滑動(dòng)到底部顯示尾部上拉布局
- 創(chuàng)建左滑菜單,根據(jù)手勢(shì)滑動(dòng)事件彈出菜單
詳細(xì)的看一下實(shí)現(xiàn)過(guò)程
1.首先我們先自定義下拉頭布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="bottom" >
<RelativeLayout
android:id="@+id/xlistview_header_content"
android:layout_width="fill_parent"
android:background="#00000000"
android:layout_height="60dp" >
<LinearLayout
android:id="@+id/xlistview_header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="@+id/xlistview_header_hint_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000"
android:textSize="16sp"
android:text="@string/xlistview_header_hint_normal" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000"
android:text="@string/xlistview_header_last_time"
android:textSize="14sp" />
<TextView
android:id="@+id/xlistview_header_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/xlistview_header_arrow"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_alignLeft="@id/xlistview_header_text"
android:layout_centerVertical="true"
android:layout_marginLeft="-50dp"
android:src="@drawable/xlistview_arrow" />
<ProgressBar
android:id="@+id/xlistview_header_progressbar"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_alignLeft="@id/xlistview_header_text"
android:layout_centerVertical="true"
android:layout_marginLeft="-55dp"
android:visibility="gone"/>
</RelativeLayout>
</LinearLayout>
2.接下來(lái)我們自定義HeaderView争舞,代碼很詳細(xì)了凛忿,不多介紹了
package com.hankkin.library;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
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;
public class RefreshListHeader extends LinearLayout {
//根布局
private LinearLayout mContainer;
//下拉箭頭圖片
private ImageView mArrowImageView;
//下拉進(jìn)度條
private ProgressBar mProgressBar;
//下拉文本
private TextView mHintTextView;
//狀態(tài)值 0-正常 1-準(zhǔn)備刷新 2-正在刷新
private int mState = STATE_NORMAL;
//抬起動(dòng)畫(huà)
private Animation mRotateUpAnim;
//下拉動(dòng)畫(huà)
private Animation mRotateDownAnim;
//下拉動(dòng)畫(huà)時(shí)間
private final int ROTATE_ANIM_DURATION = 180;
public final static int STATE_NORMAL = 0;
public final static int STATE_READY = 1;
public final static int STATE_REFRESHING = 2;
public RefreshListHeader(Context context) {
super(context);
initView(context);
}
/**
* @param context
* @param attrs
*/
public RefreshListHeader(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
/**
* 初始化組件
* @param context
*/
private void initView(Context context) {
LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, 0);
mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.xlistview_header, null);
addView(mContainer, lp);
setGravity(Gravity.BOTTOM);
mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);
mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);
mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar);
//設(shè)置抬起動(dòng)畫(huà)
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);
//設(shè)置下拉動(dòng)畫(huà)
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);
}
//設(shè)置狀態(tài)
public void setState(int state) {
if (state == mState)
return;
//正在刷新時(shí)-顯示進(jìn)度條,隱藏下拉箭頭
if (state == STATE_REFRESHING) {
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(View.INVISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
} else {
//顯示下拉箭頭竞川,隱藏進(jìn)度條
mArrowImageView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.INVISIBLE);
}
switch (state) {
case STATE_NORMAL:
if (mState == STATE_READY) {//準(zhǔn)備刷新時(shí)開(kāi)啟動(dòng)畫(huà)
mArrowImageView.startAnimation(mRotateDownAnim);
}
if (mState == STATE_REFRESHING) {//正在刷新時(shí)開(kāi)啟動(dòng)畫(huà)
mArrowImageView.clearAnimation();
}
mHintTextView.setText(R.string.xlistview_header_hint_normal);
break;
case STATE_READY:
if (mState != STATE_READY) {
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mRotateUpAnim);
mHintTextView.setText(R.string.xlistview_header_hint_ready);
}
break;
case STATE_REFRESHING:
mHintTextView.setText(R.string.xlistview_header_hint_loading);
break;
default:
}
mState = state;
}
/**
* 設(shè)置頭部高度
* @param height
*/
public void setVisiableHeight(int height) {
if (height < 0)
height = 0;
LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();
lp.height = height;
mContainer.setLayoutParams(lp);
}
public int getVisiableHeight() {
return mContainer.getHeight();
}
}
3.HeaderView定義結(jié)束后店溢,我們需要自定義一個(gè)既支持下拉刷新又支持左滑刪除的ListView,看看我是怎么做的:(左滑菜單是引用網(wǎng)上的Demo委乌,代碼寫(xiě)的也比較易懂床牧,這里不詳細(xì)給大家介紹了)
然后我們?cè)谒幕A(chǔ)上添加下拉上拉事件:(重點(diǎn)看一下onTouchEvent事件)
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) { //獲取上次y軸坐標(biāo)
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: //手勢(shì)按下事件、獲取坐標(biāo)遭贸、設(shè)置上次下拉時(shí)間
firstTouchY = ev.getRawY();
mLastY = ev.getRawY();
setRefreshTime(RefreshTime.getRefreshTime(getContext()));
int oldPos = mTouchPosition;
mDownX = ev.getX();
mDownY = ev.getY();
mTouchState = TOUCH_STATE_NONE;
mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
//彈出左滑菜單
if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {
mTouchState = TOUCH_STATE_X;
mTouchView.onSwipe(ev);//左滑菜單手勢(shì)監(jiān)聽(tīng)事件戈咳,根據(jù)滑動(dòng)距離彈出菜單
return true;
}
//獲取item view,此方法是因?yàn)間etChildAt()傳入index值導(dǎo)致listview不可見(jiàn)的item會(huì)報(bào)空指針
// 防止listview不可見(jiàn)的item獲取到的為空壕吹,使用下面方法
View view = getChildAt(mTouchPosition - getFirstVisiblePosition());
if (mTouchView != null && mTouchView.isOpen()) {//如果滑動(dòng)的item不為空并且已經(jīng)開(kāi)啟著蛙,則關(guān)閉該菜單
mTouchView.smoothCloseMenu();
mTouchView = null;
return super.onTouchEvent(ev);
}
if (mTouchView != null) {//否則打開(kāi)左滑菜單
mTouchView.onSwipe(ev);
}
if (view instanceof SwipeMenuLayout) {
mTouchView = (SwipeMenuLayout) view;
}
break;
case MotionEvent.ACTION_MOVE://手勢(shì)滑動(dòng)事件
final float deltaY = ev.getRawY() - mLastY;
float dy = Math.abs((ev.getY() - mDownY));
float dx = Math.abs((ev.getX() - mDownX));
mLastY = ev.getRawY();
//判斷左滑菜單是否未激活、或者x軸偏移平方小于y軸偏移平方3倍的時(shí)候
if ((mTouchView == null || !mTouchView.isActive()) && Math.pow(dx, 2) / Math.pow(dy, 2) <= 3) {
//判斷第一個(gè)可見(jiàn)位置并且頭部布局可見(jiàn)高度大于0時(shí)或者y軸偏移量>0
if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
// 重新更新頭部高度
updateHeaderHeight(deltaY / OFFSET_RADIO);
invokeOnScrolling();
}
}
if (mTouchState == TOUCH_STATE_X) {//如果x軸偏移彈出左滑菜單
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
getSelector().setState(new int[] { 0 });
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
} else if (mTouchState == TOUCH_STATE_NONE) {
if (Math.abs(dy) > MAX_Y) { //如果y軸偏移量>指定y軸偏移量算利,設(shè)置y軸偏移狀態(tài)
mTouchState = TOUCH_STATE_Y;
} else if (dx > MAX_X) {//如果x軸偏移量>指定x軸偏移量册踩,設(shè)置x軸偏移狀態(tài),開(kāi)始彈出左滑菜單
mTouchState = TOUCH_STATE_X;
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeStart(mTouchPosition);
}
}
}
break;
case MotionEvent.ACTION_UP://手勢(shì)抬起事件
mLastY = -1; // reset
if (getFirstVisiblePosition() == 0) {
// 設(shè)置下拉刷新?tīng)顟B(tài)值效拭,開(kāi)啟下拉刷新?tīng)顟B(tài)
if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
mPullRefreshing = true;
mHeaderView.setState(RefreshListHeader.STATE_REFRESHING);
if (onRefreshListener != null) {
tag=REFRESH;
onRefreshListener.onRefresh();
}
}
resetHeaderHeight();
}
lastTouchY = ev.getRawY();//獲取上次y軸偏移量
if (canLoadMore()) {//判斷是否滿(mǎn)足上拉
loadData();
}
if (mTouchState == TOUCH_STATE_X) {//如果為x軸偏移狀態(tài)暂吉,開(kāi)啟左滑
if (mTouchView != null) {
mTouchView.onSwipe(ev);
if (!mTouchView.isOpen()) {
mTouchPosition = -1;
mTouchView = null;
}
}
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeEnd(mTouchPosition);
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
break;
}
return super.onTouchEvent(ev);
}
當(dāng)然這部分也是滑動(dòng)沖突解決的主要部分胖秒,看一下完整的代碼,應(yīng)該能夠看的懂了慕的,小編幾乎每一句都加上了注釋?zhuān)绻锌床幻靼椎牡胤娇梢粤粞匝指危【帟?huì)及時(shí)回復(fù)的:
package com.hankkin.library;
import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;
import java.util.Date;
public class RefreshSwipeMenuListView extends ListView implements OnScrollListener {
private static final int TOUCH_STATE_NONE = 0;
private static final int TOUCH_STATE_X = 1; //x軸觸摸狀態(tài)值
private static final int TOUCH_STATE_Y = 2; //y軸觸摸狀態(tài)值
public static final int BOTH=2;//上拉和下拉
public static final int HEADER=0;//下拉
public static final int FOOTER=1;//上拉
public static String tag;//ListView的動(dòng)作
public static final String REFRESH="refresh";
public static final String LOAD="load";
private int MAX_Y = 5; //Y軸最大偏移量
private int MAX_X = 3; //X軸最大偏移量
private float mDownX; //觸摸x
private float mDownY; //觸摸y
private int mTouchState; //觸摸狀態(tài)
private int mTouchPosition; //觸摸位置
private SwipeMenuLayout mTouchView; //滑動(dòng)彈出布局
private OnSwipeListener mOnSwipeListener; //彈出監(jiān)聽(tīng)器
private float firstTouchY; //第一次觸摸y坐標(biāo)
private float lastTouchY; //最后一次觸摸y坐標(biāo)
//創(chuàng)建左滑菜單接口
private SwipeMenuCreator mMenuCreator;
//菜單點(diǎn)擊事件
private OnMenuItemClickListener mOnMenuItemClickListener;
//關(guān)閉菜單動(dòng)畫(huà)修飾Interpolator
private Interpolator mCloseInterpolator;
//開(kāi)啟菜單動(dòng)畫(huà)修飾Interpolator
private Interpolator mOpenInterpolator;
private float mLastY = -1;
private Scroller mScroller;
private OnScrollListener mScrollListener; // 滑動(dòng)監(jiān)聽(tīng)
// 下拉上拉監(jiān)聽(tīng)器
private OnRefreshListener onRefreshListener;
//下拉頭
private RefreshListHeader mHeaderView;
//頭部視圖內(nèi)容,用來(lái)計(jì)算頭部高度肮街,不下拉時(shí)隱藏
private RelativeLayout mHeaderViewContent;
//下拉時(shí)間文本控件
private TextView mHeaderTimeView;
private int mHeaderViewHeight; // 頭部高度
private boolean mEnablePullRefresh = true;//能否下拉刷新
private boolean mPullRefreshing = false; // 是否正在刷新
//上拉尾部視圖
private LinearLayout mFooterView;
private boolean mEnablePullLoad;//是否可以上拉加載
private boolean mPullLoading; //是否正在上拉
private boolean mIsFooterReady = false;
private int mTotalItemCount;
private int mScrollBack;
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 = 50;
private final static float OFFSET_RADIO = 1.8f;
private boolean isFooterVisible=false;
public RefreshSwipeMenuListView(Context context) {
super(context);
init(context);
}
public RefreshSwipeMenuListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public RefreshSwipeMenuListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* 初始化組件
* @param context
*/
private void init(Context context) {
mScroller = new Scroller(context, new DecelerateInterpolator());
super.setOnScrollListener(this);
// 初始化頭部視圖
mHeaderView = new RefreshListHeader(context);
mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);
mHeaderTimeView = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);
addHeaderView(mHeaderView);
// 初始化尾部視圖
mFooterView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.xlistview_footer, null, false);
// 初始化頭部高度
mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mHeaderViewHeight = mHeaderViewContent.getHeight();
//向 ViewTreeObserver 注冊(cè)方法风题,以獲取控件尺寸
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
MAX_X = dp2px(MAX_X);
MAX_Y = dp2px(MAX_Y);
mTouchState = TOUCH_STATE_NONE;
}
/**
* 添加適配器
* @param adapter
*/
@Override
public void setAdapter(ListAdapter adapter) {
if (mIsFooterReady == false) { //添加尾部隱藏
mIsFooterReady = true;
addFooterView(mFooterView);
mFooterView.setVisibility(GONE);
}
super.setAdapter(new SwipeMenuAdapter(getContext(), adapter) {
@Override
public void createMenu(SwipeMenu menu) {//創(chuàng)建左滑菜單
if (mMenuCreator != null) {
mMenuCreator.create(menu);
}
}
@Override
public void onItemClick(SwipeMenuView view, SwipeMenu menu, int index) {
if (mOnMenuItemClickListener != null) {//左滑菜單點(diǎn)擊事件
mOnMenuItemClickListener.onMenuItemClick(view.getPosition(), menu, index);
}
if (mTouchView != null) {
mTouchView.smoothCloseMenu();
}
}
});
}
public void setCloseInterpolator(Interpolator interpolator) {
mCloseInterpolator = interpolator;
}
public void setOpenInterpolator(Interpolator interpolator) {
mOpenInterpolator = interpolator;
}
public Interpolator getOpenInterpolator() {
return mOpenInterpolator;
}
public Interpolator getCloseInterpolator() {
return mCloseInterpolator;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) { //獲取上次y軸坐標(biāo)
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: //手勢(shì)按下事件、獲取坐標(biāo)嫉父、設(shè)置上次下拉時(shí)間
firstTouchY = ev.getRawY();
mLastY = ev.getRawY();
setRefreshTime(RefreshTime.getRefreshTime(getContext()));
int oldPos = mTouchPosition;
mDownX = ev.getX();
mDownY = ev.getY();
mTouchState = TOUCH_STATE_NONE;
mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
//彈出左滑菜單
if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {
mTouchState = TOUCH_STATE_X;
mTouchView.onSwipe(ev);//左滑菜單手勢(shì)監(jiān)聽(tīng)事件沛硅,根據(jù)滑動(dòng)距離彈出菜單
return true;
}
//獲取item view,此方法是因?yàn)間etChildAt()傳入index值導(dǎo)致listview不可見(jiàn)的item會(huì)報(bào)空指針
// 防止listview不可見(jiàn)的item獲取到的為空绕辖,使用下面方法
View view = getChildAt(mTouchPosition - getFirstVisiblePosition());
if (mTouchView != null && mTouchView.isOpen()) {//如果滑動(dòng)的item不為空并且已經(jīng)開(kāi)啟摇肌,則關(guān)閉該菜單
mTouchView.smoothCloseMenu();
mTouchView = null;
return super.onTouchEvent(ev);
}
if (mTouchView != null) {//否則打開(kāi)左滑菜單
mTouchView.onSwipe(ev);
}
if (view instanceof SwipeMenuLayout) {
mTouchView = (SwipeMenuLayout) view;
}
break;
case MotionEvent.ACTION_MOVE://手勢(shì)滑動(dòng)事件
final float deltaY = ev.getRawY() - mLastY;
float dy = Math.abs((ev.getY() - mDownY));
float dx = Math.abs((ev.getX() - mDownX));
mLastY = ev.getRawY();
//判斷左滑菜單是否未激活、或者x軸偏移平方小于y軸偏移平方3倍的時(shí)候
if ((mTouchView == null || !mTouchView.isActive()) && Math.pow(dx, 2) / Math.pow(dy, 2) <= 3) {
//判斷第一個(gè)可見(jiàn)位置并且頭部布局可見(jiàn)高度大于0時(shí)或者y軸偏移量>0
if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
// 重新更新頭部高度
updateHeaderHeight(deltaY / OFFSET_RADIO);
invokeOnScrolling();
}
}
if (mTouchState == TOUCH_STATE_X) {//如果x軸偏移彈出左滑菜單
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
getSelector().setState(new int[] { 0 });
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
} else if (mTouchState == TOUCH_STATE_NONE) {
if (Math.abs(dy) > MAX_Y) { //如果y軸偏移量>指定y軸偏移量仪际,設(shè)置y軸偏移狀態(tài)
mTouchState = TOUCH_STATE_Y;
} else if (dx > MAX_X) {//如果x軸偏移量>指定x軸偏移量围小,設(shè)置x軸偏移狀態(tài),開(kāi)始彈出左滑菜單
mTouchState = TOUCH_STATE_X;
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeStart(mTouchPosition);
}
}
}
break;
case MotionEvent.ACTION_UP://手勢(shì)抬起事件
mLastY = -1; // reset
if (getFirstVisiblePosition() == 0) {
// 設(shè)置下拉刷新?tīng)顟B(tài)值树碱,開(kāi)啟下拉刷新?tīng)顟B(tài)
if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
mPullRefreshing = true;
mHeaderView.setState(RefreshListHeader.STATE_REFRESHING);
if (onRefreshListener != null) {
tag=REFRESH;
onRefreshListener.onRefresh();
}
}
resetHeaderHeight();
}
lastTouchY = ev.getRawY();//獲取上次y軸偏移量
if (canLoadMore()) {//判斷是否滿(mǎn)足上拉
loadData();
}
if (mTouchState == TOUCH_STATE_X) {//如果為x軸偏移狀態(tài)肯适,開(kāi)啟左滑
if (mTouchView != null) {
mTouchView.onSwipe(ev);
if (!mTouchView.isOpen()) {
mTouchPosition = -1;
mTouchView = null;
}
}
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeEnd(mTouchPosition);
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
break;
}
return super.onTouchEvent(ev);
}
class ResetHeaderHeightTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
mPullRefreshing = false;
mHeaderView.setState(RefreshListHeader.STATE_NORMAL);
resetHeaderHeight();
}
}
public void smoothOpenMenu(int position) {
if (position >= getFirstVisiblePosition() && position <= getLastVisiblePosition()) {
View view = getChildAt(position - getFirstVisiblePosition());
if (view instanceof SwipeMenuLayout) {
mTouchPosition = position;
if (mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
}
mTouchView = (SwipeMenuLayout) view;
mTouchView.smoothOpenMenu();
}
}
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
getContext().getResources().getDisplayMetrics());
}
public void setMenuCreator(SwipeMenuCreator menuCreator) {
this.mMenuCreator = menuCreator;
}
public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
this.mOnMenuItemClickListener = onMenuItemClickListener;
}
public void setOnSwipeListener(OnSwipeListener onSwipeListener) {
this.mOnSwipeListener = onSwipeListener;
}
public static interface OnMenuItemClickListener {
void onMenuItemClick(int position, SwipeMenu menu, int index);
}
public static interface OnSwipeListener {
void onSwipeStart(int position);
void onSwipeEnd(int position);
}
/**
* 設(shè)置刷新可用
* @param enable
*/
private void setPullRefreshEnable(boolean enable) {
mEnablePullRefresh = enable;
if (!mEnablePullRefresh) { // disable, hide the content
mHeaderViewContent.setVisibility(View.INVISIBLE);
} else {
mHeaderViewContent.setVisibility(View.VISIBLE);
}
}
/**
* enable or disable pull up load more feature.
* 設(shè)置加載可用
* @param enable
*/
private void setPullLoadEnable(boolean enable) {
mEnablePullLoad = enable;
if (!mEnablePullLoad) {
mFooterView.setVisibility(GONE);
mFooterView.setOnClickListener(null);
} else {
mPullLoading = false;
mFooterView.setVisibility(VISIBLE);
// both "pull up" and "click" will invoke load more.
mFooterView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startLoadMore();
}
});
}
}
/**
* stop refresh, reset header view.
* 停止刷新,重置頭部控件
*/
private void stopRefresh() {
if (mPullRefreshing == true) {
mPullRefreshing = false;
resetHeaderHeight();
}
}
/**
* stop load more, reset footer view.
* 停止加載更多,重置尾部控件
*/
private void stopLoadMore() {
if (mPullLoading == true) {
mPullLoading = false;
mFooterView.setVisibility(GONE);
}
}
/**
* set last refresh time
*
* @param time
*/
public void setRefreshTime(String time) {
mHeaderTimeView.setText(time);
}
private void invokeOnScrolling() {
if (mScrollListener instanceof OnXScrollListener) {
OnXScrollListener l = (OnXScrollListener) mScrollListener;
l.onXScrolling(this);
}
}
/**
* 更新頭部高度,設(shè)置狀態(tài)值
* @param delta
*/
private void updateHeaderHeight(float delta) {
mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight());
if (mEnablePullRefresh && !mPullRefreshing) {
if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
mHeaderView.setState(RefreshListHeader.STATE_READY);
} else {
mHeaderView.setState(RefreshListHeader.STATE_NORMAL);
}
}
setSelection(0); // scroll to top each time
}
/**
* 重置頭部視圖高度
*/
private void resetHeaderHeight() {
int height = mHeaderView.getVisiableHeight();
if (height == 0) // 不可見(jiàn)
return;
// 如果正在刷新并且頭部高度沒(méi)有完全顯示不做操作
if (mPullRefreshing && height <= mHeaderViewHeight) {
return;
}
int finalHeight = 0; // 默認(rèn)
//如果正在刷新并且滑動(dòng)高度大于頭部高度
if (mPullRefreshing && height > mHeaderViewHeight) {
finalHeight = mHeaderViewHeight;
}
mScrollBack = SCROLLBACK_HEADER;
mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION);
// 觸發(fā)computescroll
invalidate();
}
/**
* 開(kāi)啟上啦
*/
private void startLoadMore() {
mPullLoading = true;
mFooterView.setVisibility(VISIBLE);
if (onRefreshListener != null) {
tag=LOAD;
onRefreshListener.onLoadMore();
}
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
if (mScrollBack == SCROLLBACK_HEADER) {
mHeaderView.setVisiableHeight(mScroller.getCurrY());
}
postInvalidate();
invokeOnScrolling();
}
super.computeScroll();
}
@Override
public void setOnScrollListener(OnScrollListener l) {
mScrollListener = l;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mScrollListener != null) {
mScrollListener.onScrollStateChanged(view, scrollState);
}
}
/**
* 滑動(dòng)監(jiān)聽(tīng)
* @param view
* @param firstVisibleItem
* @param visibleItemCount
* @param totalItemCount
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mTotalItemCount = totalItemCount;
if (mScrollListener != null) {
mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount>=totalItemCount){
isFooterVisible=true;
}else{
isFooterVisible=false;
}
}
public void setOnRefreshListener(OnRefreshListener l) {
onRefreshListener = l;
}
/**
* you can listen ListView.OnScrollListener or this one. it will invoke
* onXScrolling when header/footer scroll back.
*/
public interface OnXScrollListener extends OnScrollListener {
public void onXScrolling(View view);
}
/**
* implements this interface to get refresh/load more event.
*/
public interface OnRefreshListener {
public void onRefresh();
public void onLoadMore();
}
/**
* 上拉加載和下拉刷新請(qǐng)求完畢
*/
public void complete(){
stopLoadMore();
stopRefresh();
if(REFRESH.equals(tag)){
RefreshTime.setRefreshTime(getContext(),new Date());
}
}
/**
* 設(shè)置ListView的模式,上拉和下拉
* @param mode
*/
public void setListViewMode(int mode){
if(mode==BOTH){
setPullRefreshEnable(true);
setPullLoadEnable(true);
}else if(mode==FOOTER){
setPullLoadEnable(true);
}else if(mode==HEADER){
setPullRefreshEnable(true);
}
}
/**
* 判斷是否可以上蠟加載
* @return
*/
private boolean canLoadMore() {
return isBottom() && !mPullLoading && isPullingUp();
}
/**
* 判斷是否到達(dá)底部
* @return
*/
private boolean isBottom() {
if (getCount() > 0) {
if (getLastVisiblePosition() == getAdapter().getCount() - 1 &&
getChildAt(getChildCount() - 1).getBottom() <= getHeight()) {
return true;
}
}
return false;
}
private boolean isPullingUp() {
return (firstTouchY - lastTouchY) >= 200;
}
private void loadData() {
if (onRefreshListener != null) {
setLoading(true);
}
}
/**
* 設(shè)置是否上拉
* @param loading
*/
public void setLoading(boolean loading) {
if (this == null) return;
mPullLoading = loading;
if (loading) {
mFooterView.setVisibility(VISIBLE);
setSelection(getAdapter().getCount() - 1);
onRefreshListener.onLoadMore();
} else {
mFooterView.setVisibility(GONE);
firstTouchY = 0;
lastTouchY = 0;
}
}
}
定義完了之后我們是這樣用的:
rsmLv.setAdapter(adapter);
rsmLv.setListViewMode(RefreshSwipeMenuListView.HEADER);
rsmLv.setOnRefreshListener(this);
SwipeMenuCreator creator = new SwipeMenuCreator() {
@Override
public void create(SwipeMenu menu) {
// 創(chuàng)建滑動(dòng)選項(xiàng)
SwipeMenuItem rejectItem = new SwipeMenuItem(
getApplicationContext());
// 設(shè)置選項(xiàng)背景
rejectItem.setBackground(new ColorDrawable(getResources().getColor(R.color.top)));
// 設(shè)置選項(xiàng)寬度
rejectItem.setWidth(dp2px(80,getApplicationContext()));
// 設(shè)置選項(xiàng)標(biāo)題
rejectItem.setTitle("置頂");
// 設(shè)置選項(xiàng)標(biāo)題
rejectItem.setTitleSize(16);
// 設(shè)置選項(xiàng)標(biāo)題顏色
rejectItem.setTitleColor(Color.WHITE);
// 添加選項(xiàng)
menu.addMenuItem(rejectItem);
// 創(chuàng)建刪除選項(xiàng)
SwipeMenuItem argeeItem = new SwipeMenuItem(getApplicationContext());
argeeItem.setBackground(new ColorDrawable(getResources().getColor(R.color.del)));
argeeItem.setWidth(dp2px(80, getApplicationContext()));
argeeItem.setTitle("刪除");
argeeItem.setTitleSize(16);
argeeItem.setTitleColor(Color.WHITE);
menu.addMenuItem(argeeItem);
}
};
rsmLv.setMenuCreator(creator);
rsmLv.setOnMenuItemClickListener(new RefreshSwipeMenuListView.OnMenuItemClickListener() {
@Override
public void onMenuItemClick(int position, SwipeMenu menu, int index) {
switch (index) {
case 0: //第一個(gè)選項(xiàng)
Toast.makeText(MainActivity.this,"您點(diǎn)擊的是置頂",Toast.LENGTH_SHORT).show();
break;
case 1: //第二個(gè)選項(xiàng)
del(position,rsmLv.getChildAt(position+1-rsmLv.getFirstVisiblePosition()));
break;
}
}
});
為了和QQ有著一樣的體驗(yàn)效果成榜,小編加了一個(gè)刪除item的動(dòng)畫(huà)
看起來(lái)就不會(huì)太生硬了框舔,用戶(hù)體驗(yàn)也比較好:
/**
* 刪除item動(dòng)畫(huà)
* @param index
* @param v
*/
private void del(final int index, View v){
final Animation animation = (Animation) AnimationUtils.loadAnimation(v.getContext(), R.anim.list_anim);
animation.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {
data.remove(index);
adapter.notifyDataSetChanged();
animation.cancel();
}
});
v.startAnimation(animation);
}
好了到這里了,我們的組合控件-上拉下拉+左滑刪除的組合ListView就定義好了伦连,分享出來(lái)小伙伴們以后遇到可以直接拿來(lái)用雨饺,"到時(shí)候小船兒也不會(huì)再翻了",不合理的地方希望大家提出來(lái)惑淳,共同交流额港,進(jìn)步。
代碼已經(jīng)上傳到Github上了歧焦,小伙伴們記得star和follow哦
https://github.com/Hankkin/SwipeRefreshDemo