您的位置:首页 > 移动开发 > Android开发

Android 下拉/上拉刷新/左滑删除控件(2)

2018-03-06 15:36 309 查看
代码复制即可用

这个控件的下拉刷新的实现是自定义的

package com.cx.wdiget.listView;

import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.cx.wdiget.R;
import com.mcxtzhang.swipemenulib.SwipeMenuLayout;

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

/**
* 自定义listView下拉和上拉刷新
* <p/>
* listView中item左滑删除采用这个第三方{@link SwipeMenuLayout}控件
*/
public class CListView extends ListView implements AbsListView.OnScrollListener {

private LayoutInflater inflate;
private View footerRootView;
private View headerRootView;
private ProgressBar pb_footerProgressBar;
private TextView tv_footerHint;
private TextView tv_headRefreshTips;
private TextView tv_headRefreshLastUpdateTime;
private TextView tv_headRefreshSuccess;
private ImageView iv_headRefreshArrow;
private ImageView iv_headRefreshProgress;
private RelativeLayout rl_headRefreshContentLayout;

private IUpPullRefreshListener iUpPullRefreshListener;
private IDownPullRefreshListener iDownPullRefreshListener;

/**
* headView箭头动画
*/
private RotateAnimation mArrowAnim;
/**
* headView箭头反转动画
*/
private RotateAnimation mArrowReverseAnim;

/**
* 上拉加载更多状态码
* <p/>
* UP_LOADING 加载中
* <p/>
* UP_LOADING_DONE 加载完成
* <p/>
* UP_LOADING_NO_MORE 加载完成没有更多新数据,数据加载成功没有下一页数据时显示
* <p/>
* UP_LOADING_ERROR 加载完成异常(无网络、获取数据失败等)
*/
private int upLoadingState = 0;
private final int UP_LOADING = 1;
private final int UP_LOADING_DONE = 2;
private final int UP_LOADING_NO_MORE = 3;
private final int UP_LOADING_ERROR = 4;
/**
* DOWN_RELEASE_LOADING 下拉松开刷新状态
* <p/>
* DOWN_PULL_LOADING    下拉刷新状态
* <p/>
* DOWN_LOADING  下拉刷新中
* <p/>
* DOWN_LOADING_DONE 下刷新完成
*/
private int downLoadingState = 5;
private final static int DOWN_RELEASE_LOADING = 6;
private final static int DOWN_PULL_LOADING = 7;
private final static int DOWN_LOADING = 8;
private final static int DOWN_LOADING_DONE = 9;

/**headerView高度**/
private int mHeadViewHeight;

/**listView 可见的第一个item下标**/
private int mFirstVisibleItem = -1;

/**下拉刷新时 记录第一次downY的位置**/
private int mStartY = -1;
/**
* 下拉刷新实际距离和界面便宜距离的比例,造成一种拉伸感
**/
private final static int downPullRatio = 3;

/**
* 当前item是否超过手机一屏幕,超过才把footerView预加载出来
**/
private boolean moreThanOneScreen = false;

/**
* 当前item是否是滑动到屏幕最后一条
**/
private boolean scrollLastItem = false;

/**
* 使用了{@link SwipeMenuLayout}控件后,是否有item滑动开,false关闭 true 打开
**/
private boolean swipeMenuLayoutOpen = false;

/**
* 当上拉加载,没有更多数据时,记录加载时间,做一个几分钟内再次执行上拉不加载数据的限制
**/
private long upNoMoreDataStartTime = 0;

public CListView(Context context) {
this(context, null);

}

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

/**
* 初始化操作
*
* @param context context
*/
private void initView(Context context) {
inflate = LayoutInflater.from(context);
this.setOnScrollListener(this);
initHeaderView();
initPullImageAnimation(0);
}

/**
* listView 初始化FooterView
*/
private void initFooterView() {
footerRootView = inflate.inflate(R.layout.item_up_pull_refresh_footer, null);
pb_footerProgressBar = (ProgressBar) footerRootView.findViewById(R.id.pb_footerProgressBar);
tv_footerHint = (TextView) footerRootView.findViewById(R.id.tv_footerHint);
footerRootView.setVisibility(View.GONE);
footerRootView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
upPullLoadingData(true);
}
});
addFooterView(footerRootView);
}

/**
* 添加下拉刷新的HeadView
*/
private void initHeaderView() {

headerRootView = inflate.inflate(R.layout.listview_refresh_header, null);
iv_headRefreshArrow = (ImageView) headerRootView.findViewById(R.id.iv_headRefreshArrow);
iv_headRefreshArrow.setMinimumWidth(60);
iv_headRefreshArrow.setMinimumHeight(60);
iv_headRefreshProgress = (ImageView) headerRootView.findViewById(R.id.iv_headRefreshProgress);
tv_headRefreshTips = (TextView) headerRootView.findViewById(R.id.tv_headRefreshTips);
tv_headRefreshLastUpdateTime = (TextView) headerRootView.findViewById(R.id.tv_headRefreshLastUpdateTime);
tv_headRefreshSuccess= (TextView) headerRootView.findViewById(R.id.tv_headRefreshSuccess);
rl_headRefreshContentLayout= (RelativeLayout) headerRootView.findViewById(R.id.rl_headRefreshContentLayout);

measureView(headerRootView);
mHeadViewHeight = headerRootView.getMeasuredHeight();
headerRootView.setPadding(0, -1 * mHeadViewHeight, 0, 0);
headerRootView.invalidate();
addHeaderView(headerRootView, null, false);
downLoadingState = DOWN_LOADING_DONE;
}

/**
* IM聊天 加载历史数据使用
*/
public void setChat() {
RelativeLayout rl_chat = (RelativeLayout) headerRootView.findViewById(R.id.rl_chatProgress);
rl_chat.setVisibility(View.VISIBLE);
headerRootView.setBackgroundColor(getResources().getColor(R.color.color_ffffff));
}

/**
* 测量HeadView宽高
*/
private void measureView(View pChild) {
ViewGroup.LayoutParams p = pChild.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int lpHeight = p.height;

int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
pChild.measure(childWidthSpec, childHeightSpec);
}

/**
* on touch event
*
* @param event event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

swipeMenuLayoutOpen = (SwipeMenuLayout.getViewCache() != null);
//当左滑打开时不能执行任何非当前item的事件
if (swipeMenuLayoutOpen) {
this.requestDisallowInterceptTouchEvent(true);
SwipeMenuLayout.getViewCache().smoothClose();
}

getDowPullStartY((int) event.getY());

break;
case MotionEvent.ACTION_MOVE:

this.requestDisallowInterceptTouchEvent(swipeMenuLayoutOpen);

downPullMoveEvent(event);

break;
case MotionEvent.ACTION_UP:
swipeMenuLayoutOpen = false;
downPullUpEvent();
break;
case MotionEvent.ACTION_CANCEL:
swipeMenuLayoutOpen = false;
break;
default:
break;

}

return swipeMenuLayoutOpen || super.onTouchEvent(event);

}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

if (scrollState == SCROLL_STATE_IDLE && scrollLastItem) {
upPullLoadingData(false);
}

}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mFirstVisibleItem = firstVisibleItem;
moreThanOneScreen = (totalItemCount > visibleItemCount);
scrollLastItem = (firstVisibleItem + visibleItemCount == totalItemCount);

//满足条件先把footerRootView显示出来,滑动到底部时不会造成footerRootView显示的延迟
//iUpPullRefreshListener表示用户允许执行上拉加载
//moreThanOneScreen当列表数据超过一屏幕时才展示
if (iUpPullRefreshListener != null && footerRootView != null) {

footerRootView.setVisibility(moreThanOneScreen ? View.VISIBLE : View.GONE);
}
}

/**
* 记录下拉刷新时第一次点击到屏幕的Y坐标
* <p/>
* 只记录一次mStartY的值
* <p/>
* up的时候还原mStartY=-1;
* <p/>
* 这个方法会在down下去的时候执行一次(如果刚好item处于第0个则记录y轴值,move的时候就不再执行)
* <p/>
* 如果down下去item不是处于第0个,我们move滑动到第一个时也要执行此方法
*
* @param y y
*/
private void getDowPullStartY(int y) {

if (swipeMenuLayoutOpen || iDownPullRefreshListener == null) {
return;
}

//当item处于第一个的时候我们就记录mStartY的坐标
if (mFirstVisibleItem == 0 && mStartY == -1) {
mStartY = y;
}

}

/**
* 下拉刷新move事件代码逻辑
*/
private void downPullMoveEvent(MotionEvent event) {

int tempY = (int) event.getY();
getDowPullStartY(tempY);

if (downLoadingState != DOWN_LOADING && mStartY != -1) {

// 以松手去刷新了
if (downLoadingState == DOWN_RELEASE_LOADING) {

// 保证在设置padding的过程中,当前的位置一直是在head,
// 否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
setSelection(0);

// 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
if (((tempY - mStartY) / downPullRatio < mHeadViewHeight) && (tempY - mStartY) > 0) {
downLoadingState = DOWN_PULL_LOADING;
changeHeaderViewByState();
}
// 一下子推到顶了
else if (tempY - mStartY <= 0) {
downLoadingState = DOWN_LOADING_DONE;
changeHeaderViewByState();
} else {
headerRootView.setPadding(0, (tempY - mStartY) / downPullRatio - mHeadViewHeight, 0, 0);
}

}

// 还没有到达显示松开刷新的时候
if (downLoadingState == DOWN_PULL_LOADING) {

setSelection(0);

// 下拉到进入松手刷新状态
if ((tempY - mStartY) / downPullRatio >= mHeadViewHeight) {
downLoadingState = DOWN_RELEASE_LOADING;
changeHeaderViewByState();
} else if (tempY - mStartY <= 0) {
downLoadingState = DOWN_LOADING_DONE;
changeHeaderViewByState();
} else {
headerRootView.setPadding(0, (tempY - mStartY) / downPullRatio - mHeadViewHeight, 0, 0);
}

}
//刷新完成的状态
if (downLoadingState == DOWN_LOADING_DONE) {
if (tempY - mStartY > 0) {
downLoadingState = DOWN_PULL_LOADING;
changeHeaderViewByState();
}
}
}

}

/**
* 上拉刷新执行up事件的代码逻辑
*/
private void downPullUpEvent() {

if (downLoadingState != DOWN_LOADING) {

if (downLoadingState == DOWN_PULL_LOADING) {
downLoadingState = DOWN_LOADING_DONE;
changeHeaderViewByState();
}
if (downLoadingState == DOWN_RELEASE_LOADING) {
downLoadingState = DOWN_LOADING;
changeHeaderViewByState();
}
}
mStartY = -1;
}

private void changeHeaderViewByState() {

if (headerRootView == null || iUpPullRefreshListener == null) {
return;
}

switch (downLoadingState) {
case DOWN_LOADING:

headerRootView.setPadding(0, 0, 0, 0);
headRefreshProgressAnimation(true);
iv_headRefreshArrow.clearAnimation();
tv_headRefreshTips.setText("正在刷新");

iUpPullRefreshListener.upPullRefresh();
downPullLoadingComplete();

break;
case DOWN_LOADING_DONE:

headerRootView.setPadding(0, -1 * mHeadViewHeight, 0, 0);
headRefreshProgressAnimation(false);
iv_headRefreshArrow.clearAnimation();
iv_headRefreshArrow.setImageResource(R.mipmap.xia);
tv_headRefreshTips.setText("下拉刷新");

break;
case DOWN_RELEASE_LOADING:

headRefreshProgressAnimation(false);
iv_headRefreshArrow.clearAnimation();
iv_headRefreshArrow.startAnimation(mArrowAnim);
tv_headRefreshTips.setText("松开刷新");

break;
case DOWN_PULL_LOADING:

headRefreshProgressAnimation(false);
tv_headRefreshTips.setText("下拉刷新");
iv_headRefreshArrow.clearAnimation();
iv_headRefreshArrow.startAnimation(mArrowReverseAnim);

break;
default:
break;
}

}

/**
* 上拉刷新状态的改变,改变样式
*/
private void changeFooterViewByState() {

if (iUpPullRefreshListener == null || footerRootView == null) {
return;
}
switch (upLoadingState) {

case UP_LOADING_NO_MORE:
pb_footerProgressBar.setVisibility(View.GONE);
tv_footerHint.setText("没有更多了");
if (upNoMoreDataStartTime <= 0) {
upNoMoreDataStartTime = System.currentTimeMillis();
}
break;
case UP_LOADING_ERROR:
pb_footerProgressBar.setVisibility(View.GONE);
tv_footerHint.setText("点击加载更多");
upNoMoreDataStartTime = 0;
break;
case UP_LOADING:
pb_footerProgressBar.setVisibility(View.VISIBLE);
tv_footerHint.setText("加载中...");
upNoMoreDataStartTime = 0;
break;
default:
break;
}
}

/**
* 执行上拉加载数据
*/
private void upPullLoadingData(boolean clickFooterView) {

if (iUpPullRefreshListener != null && upLoadingState != UP_LOADING && moreThanOneScreen) {

if (!AndroidUtils.isNetworkAvailable()) {
AndroidUtils.toast("网络不给力,请检查后重试");
upLoadingState = UP_LOADING_ERROR;
changeFooterViewByState();
return;
}

if (upLoadingState == UP_LOADING_NO_MORE && !againLoadingNoMoreData()) {
return;
}

if (upLoadingState == UP_LOADING_ERROR && !clickFooterView) {
return;
}
upLoadingState = UP_LOADING;
changeFooterViewByState();
iUpPullRefreshListener.upPullRefresh();
}
}

/**
* 实例化下拉刷新的箭头的动画效果
*
* @param pAnimDuration 动画运行时长
* @date 2013-11-20 上午11:53:22
* @change JohnWatson
* @version 1.0
*/
private void initPullImageAnimation(final int pAnimDuration) {

int _Duration;

if (pAnimDuration > 0) {
_Duration = pAnimDuration;
} else {
_Duration = 250;
}

Interpolator _Interpolator = new LinearInterpolator();

mArrowAnim = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mArrowAnim.setInterpolator(_Interpolator);
mArrowAnim.setDuration(_Duration);
mArrowAnim.setFillAfter(true);

mArrowReverseAnim = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mArrowReverseAnim.setInterpolator(_Interpolator);
mArrowReverseAnim.setDuration(_Duration);
mArrowReverseAnim.setFillAfter(true);
}

/**
* 超过五分钟才能再次执行上拉加载
* <p/>
* 当上拉加载状态为UP_LOADING_NO_MORE时,再次执行上拉加载时有时间限制
* <p/>
* 超过限制时间后才能再次执行上拉操作
*
* @return true 可以加载 false不可以加载
*/
private boolean againLoadingNoMoreData() {

return (System.currentTimeMillis() - upNoMoreDataStartTime > (5 * 60 * 1000));
}

/**
* 数据加载完成后调用
*
* @param loadSuccess 加载数据是否成功
* @param nextPage    是否还有下一页数据
*/
public void loadDataOver(boolean loadSuccess, boolean nextPage) {

Log.v("tag", "loadDataOver");

if (loadSuccess) {

if (!nextPage) {

upLoadingState = UP_LOADING_NO_MORE;
changeFooterViewByState();

} else {
upLoadingState = UP_LOADING_DONE;
}

} else {

upLoadingState = UP_LOADING_ERROR;
changeFooterViewByState();
}
}

Handler mHandler = new Handler();

/**
* 下拉刷新完成
*/
public void downPullLoadingComplete() {
new Thread() {
public void run() {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
rl_headRefreshContentLayout.setVisibility(View.GONE);
tv_headRefreshSuccess.setText(AndroidUtils.isNetworkAvailable()?"刷新成功":"网络不给力请检查后重试!");
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {

rl_headRefreshContentLayout.setVisibility(View.VISIBLE);
tv_headRefreshLastUpdateTime.setText("上次更新:" + new SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.CHINA).format(new Date()));
downLoadingState = DOWN_LOADING_DONE;
changeHeaderViewByState();
}
});
}

}.start();

}

public void headRefreshProgressAnimation(boolean isStart) {

iv_headRefreshProgress.setVisibility(isStart ? VISIBLE : GONE);
iv_headRefreshArrow.setVisibility(isStart ? GONE : VISIBLE);

AnimationDrawable animationDrawable = (AnimationDrawable) iv_headRefreshProgress.getBackground();
if (isStart) {
animationDrawable.start();
} else {
animationDrawable.stop();
}

}

public void setUpPullRefreshListener(IUpPullRefreshListener iUpPullRefreshListener) {
if (iUpPullRefreshListener != null) {
this.iUpPullRefreshListener = iUpPullRefreshListener;
initFooterView();
}
}

public void setDownPullRefreshListener(IDownPullRefreshListener iDownPullRefreshListener) {
this.iDownPullRefreshListener = iDownPullRefreshListener;
}

public interface IUpPullRefreshListener {

void upPullRefresh();
}

public interface IDownPullRefreshListener {

void downPullRefresh();
}

}


<?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:background="@color/color_f5f7f7"
android:orientation="horizontal">

<RelativeLayout
android:id="@+id/rl_chatProgress"
android:layout_width="match_parent"
android:layout_height="50dp"
android:visibility="gone">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal">

<ProgressBar
android:id="@+id/pb_chatProgressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="@dimen/dimen_15dp"
android:layout_height="@dimen/dimen_15dp"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/up_pull_footer_progressbar"
android:visibility="visible" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="5dp"
android:text="加载中..." />
</LinearLayout>

</RelativeLayout>

<TextView
android:visibility="gone"
android:id="@+id/tv_headRefreshSuccess"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="刷新成功!"
android:textColor="#747b81"
android:textSize="12sp" />

<RelativeLayout
android:id="@+id/rl_headRefreshContentLayout"
android:layout_width="match_parent"
android:layout_height="50dp">

<!-- 箭头图像、进度条 -->

<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="18dp"
android:layout_toLeftOf="@+id/ll_">

<ImageView
android:id="@+id/iv_headRefreshArrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:contentDescription="@string/app_name"
android:src="@mipmap/xia" />

<ImageView
android:id="@+id/iv_headRefreshProgress"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:background="@drawable/animation_loading_circle"
android:visibility="gone" />
</FrameLayout>

<LinearLayout
android:id="@+id/ll_"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:orientation="vertical">

<TextView
android:id="@+id/tv_headRefreshTips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="松开刷新"
android:textColor="#747b81"
android:textSize="12sp" />

<TextView
android:id="@+id/tv_headRefreshLastUpdateTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="上次更新:"
android:textColor="#747b81"
android:textSize="9sp" />
</LinearLayout>
</RelativeLayout>

</LinearLayout>


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/color_ffffff"
android:gravity="center"
android:orientation="horizontal"
android:padding="@dimen/dimen_15dp">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">

<ProgressBar
android:id="@+id/pb_footerProgressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="@dimen/dimen_15dp"
android:layout_height="@dimen/dimen_15dp"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/up_pull_footer_progressbar"
android:visibility="visible" />

<TextView
android:id="@+id/tv_footerHint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_10dp"
android:gravity="center"
android:text="@string/p2refresh_head_load_more"
android:textColor="#00c0c7"
android:textSize="@dimen/dimen_14sp" />
</LinearLayout>

</LinearLayout>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐