- 浏览: 12885275 次
- 性别:
- 来自: 大连
文章分类
最新评论
-
sanrenxing_1:
GoEasy 实时推送支持IE6-IE11及大多数主流浏览器的 ...
WindowsPhone消息推送服务 -
张砚辉:
两侧照片绕Y轴旋转后有锯齿,请问锯齿解决方案,很长时间没解决
自定义带倒影和偏转的超炫Gallery -
knight_black_bob:
能不能把你自己的博客整理下分类下,写了这么多 ,都不知道怎么查 ...
Android_View,ViewGroup,Window之间的关系 -
jeasonyoung:
你这个代码实现在iOS8下应该是滑不动的
UISlider 滑块控件—IOS开发 -
wx_hello:
如果能写个可运行的java程序,不胜感激。。。
rs232串口通信原理
android ListView的上部下拉刷新下部点击加载更多具体实现及拓展
这次就不上图了,例子太多太多了,想必大家都见过.这个功能的实现,简直是开发者必备的.
我也不过多介绍了,网上详细介绍的博客太多太多了,若想深入了解,请参考网上其他博文.
在这里,我只是按照自己的理解,模拟实现了一个,顺便代码贡献出来.
我对之详细标明的注释,想必如果不懂的同学们,看注释也应该明白,前提是,你要耐心看,因为代码有点多,但是我整理过了,还算清晰.
详细代码:
package com.jj.drag; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; /*** * 自定义拖拉ListView * * @author zhangjia * */ public class DragListView extends ListView implements OnScrollListener, OnClickListener { // 拖拉ListView枚举所有状态 private enum DListViewState { LV_NORMAL, // 普通状态 LV_PULL_REFRESH, // 下拉状态(为超过mHeadViewHeight) LV_RELEASE_REFRESH, // 松开可刷新状态(超过mHeadViewHeight) LV_LOADING;// 加载状态 } // 点击加载更多枚举所有状态 private enum DListViewLoadingMore { LV_NORMAL, // 普通状态 LV_LOADING, // 加载状态 LV_OVER; // 结束状态 } private View mHeadView;// 头部headView private TextView mRefreshTextview; // 刷新msg(mHeadView) private TextView mLastUpdateTextView;// 更新事件(mHeadView) private ImageView mArrowImageView;// 下拉图标(mHeadView) private ProgressBar mHeadProgressBar;// 刷新进度体(mHeadView) private int mHeadViewWidth; // headView的宽(mHeadView) private int mHeadViewHeight;// headView的高(mHeadView) private View mFootView;// 尾部mFootView private View mLoadMoreView;// mFootView 的view(mFootView) private TextView mLoadMoreTextView;// 加载更多.(mFootView) private View mLoadingView;// 加载中...View(mFootView) private Animation animation, reverseAnimation;// 旋转动画,旋转动画之后旋转动画. private int mFirstItemIndex = -1;// 当前视图能看到的第一个项的索引 // 用于保证startY的值在一个完整的touch事件中只被记录一次 private boolean mIsRecord = false; private int mStartY, mMoveY;// 按下是的y坐标,move时的y坐标 private DListViewState mlistViewState = DListViewState.LV_NORMAL;// 拖拉状态.(自定义枚举) private DListViewLoadingMore loadingMoreState = DListViewLoadingMore.LV_NORMAL;// 加载更多默认状态. private final static int RATIO = 2;// 手势下拉距离比. private boolean mBack = false;// headView是否返回. private OnRefreshLoadingMoreListener onRefreshLoadingMoreListener;// 下拉刷新接口(自定义) private boolean isScroller = true;// 是否屏蔽ListView滑动。 public DragListView(Context context) { super(context, null); initDragListView(context); } public DragListView(Context context, AttributeSet attrs) { super(context, attrs); initDragListView(context); } // 注入下拉刷新接口 public void setOnRefreshListener( OnRefreshLoadingMoreListener onRefreshLoadingMoreListener) { this.onRefreshLoadingMoreListener = onRefreshLoadingMoreListener; } /*** * 初始化ListView */ public void initDragListView(Context context) { String time = "1994.12.05";// 更新时间 initHeadView(context, time);// 初始化该head. initLoadMoreView(context);// 初始化footer setOnScrollListener(this);// ListView滚动监听 } /*** * 初始话头部HeadView * * @param context * 上下文 * @param time * 上次更新时间 */ public void initHeadView(Context context, String time) { mHeadView = LayoutInflater.from(context).inflate(R.layout.head, null); mArrowImageView = (ImageView) mHeadView .findViewById(R.id.head_arrowImageView); mArrowImageView.setMinimumWidth(60); mHeadProgressBar = (ProgressBar) mHeadView .findViewById(R.id.head_progressBar); mRefreshTextview = (TextView) mHeadView .findViewById(R.id.head_tipsTextView); mLastUpdateTextView = (TextView) mHeadView .findViewById(R.id.head_lastUpdatedTextView); // 显示更新事件 mLastUpdateTextView.setText("最近更新:" + time); measureView(mHeadView); // 获取宽和高 mHeadViewWidth = mHeadView.getMeasuredWidth(); mHeadViewHeight = mHeadView.getMeasuredHeight(); addHeaderView(mHeadView, null, false);// 将初始好的ListView add进拖拽ListView // 在这里我们要将此headView设置到顶部不显示位置. mHeadView.setPadding(0, -1 * mHeadViewHeight, 0, 0); initAnimation();// 初始化动画 } /*** * 初始化底部加载更多控件 */ private void initLoadMoreView(Context context) { mFootView = LayoutInflater.from(context).inflate(R.layout.footer, null); mLoadMoreView = mFootView.findViewById(R.id.load_more_view); mLoadMoreTextView = (TextView) mFootView .findViewById(R.id.load_more_tv); mLoadingView = (LinearLayout) mFootView .findViewById(R.id.loading_layout); mLoadMoreView.setOnClickListener(this); addFooterView(mFootView); } /*** * 初始化动画 */ private void initAnimation() { // 旋转动画 animation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); animation.setInterpolator(new LinearInterpolator());// 匀速 animation.setDuration(250); animation.setFillAfter(true);// 停留在最后状态. // 反向旋转动画 reverseAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); reverseAnimation.setInterpolator(new LinearInterpolator()); reverseAnimation.setDuration(250); reverseAnimation.setFillAfter(true); } /*** * 作用:测量 headView的宽和高. * * @param child */ private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 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); } child.measure(childWidthSpec, childHeightSpec); } /*** * touch 事件监听 */ @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { // 按下 case MotionEvent.ACTION_DOWN: doActionDown(ev); break; // 移动 case MotionEvent.ACTION_MOVE: doActionMove(ev); break; // 抬起 case MotionEvent.ACTION_UP: doActionUp(ev); break; default: break; } /*** * 如果是ListView本身的拉动,那么返回true,这样ListView不可以拖动. * 如果不是ListView的拉动,那么调用父类方法,这样就可以上拉执行. */ if (isScroller) { return super.onTouchEvent(ev); } else { return true; } } /*** * 摁下操作 * * 作用:获取摁下是的y坐标 * * @param event */ void doActionDown(MotionEvent event) { if (mIsRecord == false && mFirstItemIndex == 0) { mStartY = (int) event.getY(); mIsRecord = true; } } /*** * 拖拽移动操作 * * @param event */ void doActionMove(MotionEvent event) { mMoveY = (int) event.getY();// 获取实时滑动y坐标 // 检测是否是一次touch事件. if (mIsRecord == false && mFirstItemIndex == 0) { mStartY = (int) event.getY(); mIsRecord = true; } /*** * 如果touch关闭或者正处于Loading状态的话 return. */ if (mIsRecord == false || mlistViewState == DListViewState.LV_LOADING) { return; } // 向下啦headview移动距离为y移动的一半.(比较友好) int offset = (mMoveY - mStartY) / RATIO; switch (mlistViewState) { // 普通状态 case LV_NORMAL: { // 如果<0,则意味着上滑动. if (offset > 0) { // 设置headView的padding属性. mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0); switchViewState(DListViewState.LV_PULL_REFRESH);// 下拉状态 } } break; // 下拉状态 case LV_PULL_REFRESH: { setSelection(0);// 时时保持在顶部. // 设置headView的padding属性. mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0); if (offset < 0) { /*** * 要明白为什么isScroller = false; */ isScroller = false; switchViewState(DListViewState.LV_NORMAL);// 普通状态 Log.e("jj", "isScroller=" + isScroller); } else if (offset > mHeadViewHeight) {// 如果下拉的offset超过headView的高度则要执行刷新. switchViewState(DListViewState.LV_RELEASE_REFRESH);// 更新为可刷新的下拉状态. } } break; // 可刷新状态 case LV_RELEASE_REFRESH: { setSelection(0);时时保持在顶部 // 设置headView的padding属性. mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0); // 下拉offset>0,但是没有超过headView的高度.那么要goback 原装. if (offset >= 0 && offset <= mHeadViewHeight) { mBack = true; switchViewState(DListViewState.LV_PULL_REFRESH); } else if (offset < 0) { switchViewState(DListViewState.LV_NORMAL); } else { } } break; default: return; } ; } /*** * 手势抬起操作 * * @param event */ public void doActionUp(MotionEvent event) { mIsRecord = false;// 此时的touch事件完毕,要关闭。 isScroller = true;// ListView可以Scrooler滑动. mBack = false; // 如果下拉状态处于loading状态. if (mlistViewState == DListViewState.LV_LOADING) { return; } // 处理相应状态. switch (mlistViewState) { // 普通状态 case LV_NORMAL: break; // 下拉状态 case LV_PULL_REFRESH: mHeadView.setPadding(0, -1 * mHeadViewHeight, 0, 0); switchViewState(mlistViewState.LV_NORMAL); break; // 刷新状态 case LV_RELEASE_REFRESH: mHeadView.setPadding(0, 0, 0, 0); switchViewState(mlistViewState.LV_LOADING); onRefresh();// 下拉刷新 break; } } // 切换headview视图 private void switchViewState(DListViewState state) { switch (state) { // 普通状态 case LV_NORMAL: { mArrowImageView.clearAnimation();// 清除动画 mArrowImageView.setImageResource(R.drawable.arrow); } break; // 下拉状态 case LV_PULL_REFRESH: { mHeadProgressBar.setVisibility(View.GONE);// 隐藏进度条 mArrowImageView.setVisibility(View.VISIBLE);// 下拉图标 mRefreshTextview.setText("下拉可以刷新"); mArrowImageView.clearAnimation();// 清除动画 // 是有可刷新状态(LV_RELEASE_REFRESH)转为这个状态才执行,其实就是你下拉后在上拉会执行. if (mBack) { mBack = false; mArrowImageView.clearAnimation();// 清除动画 mArrowImageView.startAnimation(reverseAnimation);// 启动反转动画 } } break; // 松开刷新状态 case LV_RELEASE_REFRESH: { mHeadProgressBar.setVisibility(View.GONE);// 隐藏进度条 mArrowImageView.setVisibility(View.VISIBLE);// 显示下拉图标 mRefreshTextview.setText("松开获取更多"); mArrowImageView.clearAnimation();// 清除动画 mArrowImageView.startAnimation(animation);// 启动动画 } break; // 加载状态 case LV_LOADING: { Log.e("!!!!!!!!!!!", "convert to IListViewState.LVS_LOADING"); mHeadProgressBar.setVisibility(View.VISIBLE); mArrowImageView.clearAnimation(); mArrowImageView.setVisibility(View.GONE); mRefreshTextview.setText("载入中..."); } break; default: return; } // 切记不要忘记时时更新状态。 mlistViewState = state; } /*** * 下拉刷新 */ private void onRefresh() { if (onRefreshLoadingMoreListener != null) { onRefreshLoadingMoreListener.onRefresh(); } } /*** * 下拉刷新完毕 */ public void onRefreshComplete() { mHeadView.setPadding(0, -1 * mHeadViewHeight, 0, 0);// 回归. switchViewState(mlistViewState.LV_NORMAL);// } /*** * 点击加载更多 * * @param flag * 数据是否已全部加载完毕 */ public void onLoadMoreComplete(boolean flag) { if (flag) { updateLoadMoreViewState(DListViewLoadingMore.LV_OVER); } else { updateLoadMoreViewState(DListViewLoadingMore.LV_NORMAL); } } // 更新Footview视图 private void updateLoadMoreViewState(DListViewLoadingMore state) { switch (state) { // 普通状态 case LV_NORMAL: mLoadingView.setVisibility(View.GONE); mLoadMoreTextView.setVisibility(View.VISIBLE); mLoadMoreTextView.setText("查看更多"); break; // 加载中状态 case LV_LOADING: mLoadingView.setVisibility(View.VISIBLE); mLoadMoreTextView.setVisibility(View.GONE); break; // 加载完毕状态 case LV_OVER: mLoadingView.setVisibility(View.GONE); mLoadMoreTextView.setVisibility(View.VISIBLE); mLoadMoreTextView.setText("加载完毕"); break; default: break; } loadingMoreState = state; } /*** * ListView 滑动监听 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstItemIndex = firstVisibleItem; } /*** * 底部点击事件 */ @Override public void onClick(View v) { // 防止重复点击 if (onRefreshLoadingMoreListener != null && loadingMoreState == DListViewLoadingMore.LV_NORMAL) { updateLoadMoreViewState(DListViewLoadingMore.LV_LOADING); onRefreshLoadingMoreListener.onLoadMore();// 对外提供方法加载更多. } } /*** * 自定义接口 */ public interface OnRefreshLoadingMoreListener { /*** * // 下拉刷新执行 */ void onRefresh(); /*** * 点击加载更多 */ void onLoadMore(); } }
上面就是全部代码,其实重要的是明白理解,这样我们还可以进行拓展.
具体应用:(只需要这样引用即可.)
<com.jj.drag.DragListView android:id="@+id/dlv_main" android:layout_width="fill_parent" android:layout_height="fill_parent" android:cacheColorHint="#00000000" />
在Activity中的调用,相比大家都清楚,开个异步或线程进行加载数据,这里我简单说一下异步使用,线程同理.
代码如下:
/*** * 执行类 异步 * * @author zhangjia * */ class MyAsyncTask extends AsyncTask<Void, Void, Void> { private Context context; private int index;// 用于判断是下拉刷新还是点击加载更多 public MyAsyncTask(Context context, int index) { this.context = context; this.index = index; } @Override protected Void doInBackground(Void... params) { try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } return null; } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); if (index == DRAG_INDEX) dlv_main.onRefreshComplete(); else if (index == LOADMORE_INDEX) dlv_main.onLoadMoreComplete(false); } }先声明一点,这个只是个示例,所以这部分代码写的不够友好,也请见谅.
就说道这里,最后展示一下效果:
至于如果显示,如何adapter.notifyDataSetChanged();那就要大家开发时候自己调理了.
最后说明一点:网上有好多介绍下拉刷新的例子,但是他们没有对滑动进行处理,比如,我下拉的时候现在不想刷新了,这时我又向上滑动,正常的处理,应该滑动到FirstItemIndex=1就是顶部,滑动就结束了.(意思就是要下拉和listview正常滑动要分开)可是网上一些案例都没有对之处理,用起来不友好,大家可以看看成功案例,那些新浪,腾讯,百度等.
解决方法:(这是onTouch方法中的一部分.)
/*** * 如果是ListView本身的拉动,那么返回true,这样ListView不可以拖动. * 如果不是ListView的拉动,那么调用父类方法,这样就可以上拉执行. */ if (isScroller) { return super.onTouchEvent(ev); } else { return true; }要问Why的话,那么你就要去详细看Touch种种事件,记住,这里用到的不是分发与拦截,分发拦截流程如下:
Activity 的dispatchTouchEvent开始分发给子的View,如果该View是ViewGroup的话,那么执行其dispatchTouchEvent进行分发,在执行相应的onInterceptTouchEvent拦截.如果要想实现上诉说的那种效果,那么在自定义ListView中对拦截分发方法是无效的,只有在ListView的上一层进行处理,比我我们在外层自定义一个布局,等等,实现起来总之麻烦一个字,其实我们也可以考虑考虑onTouchEvent事件的实现,
ListView.java
@Override public boolean onTouchEvent(MotionEvent ev) { if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { // Don't handle edge touches immediately -- they may actually belong to one of our // descendants. return false; } return super.onTouchEvent(ev); }
继续点击查看父类,这里就不显示了,自己可以查看源码,其实就是我们ListView滑动的具体实现,而此时我们只是想临时屏蔽掉此滑动,那么我们只需要不调用父类的onTouchEvent不就OK的,是的,确实如此,而何时进行屏蔽,大家就仔细看上面源码实现吧,解释的也很清楚,这样大家都明白了吧。注:有疑问请留言!之前这个例子android 自定义ScrollView实现反弹效果(以及解决和ListView之间的冲突)没有解决这个问题,因为处境不同.(不过正在完善,相信也会完美的实现这些效果,因为原理上是行的通的。)
知识拓展:
首先我们还是看一些案例:
效果就是可以上下拖拽.而用在最多的地方就是ListView,而普通的布局拖拽直接自定义布局就OK了,详情请参考上面连接那篇文章.
实现起来也不是很麻烦,就是对上面那个自定义类稍作修改,把底部也做成动态拖拽效果就OK了.
这里不详细讲解,因为注释相当明确,如有疑问,请指出.
代码如下:
package com.jj.drag; import android.content.Context; import android.os.AsyncTask; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; /*** * 自定义拖拉ListView * * @author zhangjia * */ public class DragListView extends ListView implements OnScrollListener, OnClickListener { // 下拉ListView枚举所有状态 private enum DListViewState { LV_NORMAL, // 普通状态 LV_PULL_REFRESH, // 下拉状态(为超过mHeadViewHeight) } // 点击加载更多枚举所有状态 private enum DListViewLoadingMore { LV_NORMAL, // 普通状态 LV_PULL_REFRESH, // 上拉状态(为超过mHeadViewHeight) } private View mHeadView, mFootView;// 头部headView private int mHeadViewWidth; // headView的宽(mHeadView) private int mHeadViewHeight;// headView的高(mHeadView) private int mFirstItemIndex = -1;// 当前视图能看到的第一个项的索引 private int mLastItemIndex = -1;// 当前视图中是否是最后一项. // 用于保证startY的值在一个完整的touch事件中只被记录一次 private boolean mIsRecord = false;// 针对下拉 private boolean mIsRecord_B = false;// 针对上拉 private int mStartY, mMoveY;// 按下是的y坐标,move时的y坐标 private DListViewState mlistViewState = DListViewState.LV_NORMAL;// 拖拉状态.(自定义枚举) private DListViewLoadingMore loadingMoreState = DListViewLoadingMore.LV_NORMAL;// 加载更多默认状态. private final static int RATIO = 2;// 手势下拉距离比. private boolean isScroller = true;// 是否屏蔽ListView滑动。 private MyAsynTask myAsynTask;// 任务 private final static int DRAG_UP = 1, DRAG_DOWN = 2; public DragListView(Context context) { super(context, null); initDragListView(context); } public DragListView(Context context, AttributeSet attrs) { super(context, attrs); initDragListView(context); } /*** * 初始化ListView */ public void initDragListView(Context context) { initHeadView(context);// 初始化该head. initFooterView(context);// 初始化footer setOnScrollListener(this);// ListView滚动监听 } /*** * 初始话头部HeadView * * @param context * 上下文 * @param time * 上次更新时间 */ public void initHeadView(Context context) { mHeadView = LayoutInflater.from(context).inflate(R.layout.head, null); measureView(mHeadView); // 获取宽和高 mHeadViewWidth = mHeadView.getMeasuredWidth(); mHeadViewHeight = mHeadView.getMeasuredHeight(); addHeaderView(mHeadView, null, false);// 将初始好的ListView add进拖拽ListView // 在这里我们要将此headView设置到顶部不显示位置. mHeadView.setPadding(0, -1 * mHeadViewHeight, 0, 0); } /*** * 初始化底部加载更多控件 */ private void initFooterView(Context context) { mFootView = LayoutInflater.from(context).inflate(R.layout.head, null); addFooterView(mFootView, null, false);// 将初始好的ListView add进拖拽ListView // 在这里我们要将此FooterView设置到底部不显示位置. mFootView.setPadding(0, -1 * mHeadViewHeight, 0, 0); } /*** * 作用:测量 headView的宽和高. * * @param child */ private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 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); } child.measure(childWidthSpec, childHeightSpec); } /*** * touch 事件监听 */ @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { // 按下 case MotionEvent.ACTION_DOWN: doActionDown_B(ev); doActionDown(ev); break; // 移动 case MotionEvent.ACTION_MOVE: doActionMove_B(ev); doActionMove(ev); break; // 抬起 case MotionEvent.ACTION_UP: doActionUp_B(ev); doActionUp(ev); break; default: break; } /*** * 如果是ListView本身的拉动,那么返回true,这样ListView不可以拖动. * 如果不是ListView的拉动,那么调用父类方法,这样就可以上拉执行. */ if (isScroller) { return super.onTouchEvent(ev); } else { return true; } } /*** * 摁下操作 * * 作用:获取摁下是的y坐标 * * @param event */ void doActionDown(MotionEvent event) { // 如果是第一项且是一次touch if (mIsRecord == false && mFirstItemIndex == 0) { mStartY = (int) event.getY(); mIsRecord = true; } } /*** * 摁下操作 底部 * * 作用:获取摁下是的y坐标 */ void doActionDown_B(MotionEvent event) { // 如果是第一项且是一次touch if (mIsRecord_B == false && mLastItemIndex == getCount()) { mStartY = (int) event.getY(); mIsRecord_B = true; } } /*** * 拖拽移动操作 * * @param event */ void doActionMove(MotionEvent event) { // 判断是否是第一项,若不是直接返回 mMoveY = (int) event.getY();// 获取实时滑动y坐标 // 检测是否是一次touch事件. if (mIsRecord == false && mFirstItemIndex == 0) { mStartY = (int) event.getY(); mIsRecord = true; } // 直接返回说明不是第一项 if (mIsRecord == false) return; // 向下啦headview移动距离为y移动的一半.(比较友好) int offset = (mMoveY - mStartY) / RATIO; switch (mlistViewState) { // 普通状态 case LV_NORMAL: { // 说明下拉 if (offset > 0) { // 设置headView的padding属性. mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0); mlistViewState = DListViewState.LV_PULL_REFRESH;// 下拉状态 } } break; // 下拉状态 case LV_PULL_REFRESH: { setSelection(0);// 时时保持在顶部. // 设置headView的padding属性. mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0); if (offset < 0) { /*** * 要明白为什么isScroller = false; */ isScroller = false; mlistViewState = mlistViewState.LV_NORMAL; } } break; default: return; } } void doActionMove_B(MotionEvent event) { mMoveY = (int) event.getY();// 获取实时滑动y坐标 // 检测是否是一次touch事件.(若mFirstItemIndex为0则要初始化mStartY) if (mIsRecord_B == false && mLastItemIndex == getCount()) { mStartY = (int) event.getY(); mIsRecord_B = true; } // 直接返回说明不是最后一项 if (mIsRecord_B == false) return; // 向下啦headview移动距离为y移动的一半.(比较友好) int offset = (mMoveY - mStartY) / RATIO; switch (loadingMoreState) { // 普通状态 case LV_NORMAL: { // 说明上拉 if (offset < 0) { int distance = Math.abs(offset); // 设置headView的padding属性. mFootView.setPadding(0, distance - mHeadViewHeight, 0, 0); loadingMoreState = loadingMoreState.LV_PULL_REFRESH;// 下拉状态 } } break; // 上拉状态 case LV_PULL_REFRESH: { setSelection(getCount() - 1);// 时时保持最底部 // 设置headView的padding属性. int distance = Math.abs(offset); mFootView.setPadding(0, distance - mHeadViewHeight, 0, 0); // 说明下滑 if (offset > 0) { /*** * 要明白为什么isScroller = false; */ isScroller = false; loadingMoreState = loadingMoreState.LV_NORMAL; } } break; default: return; } } /*** * 手势抬起操作 * * @param event */ public void doActionUp(MotionEvent event) { mIsRecord = false;// 此时的touch事件完毕,要关闭。 mIsRecord_B = false; // 此时的touch事件完毕,要关闭。 isScroller = true;// ListView可以Scrooler滑动. mlistViewState = mlistViewState.LV_NORMAL;// 状态也回归最初状态 // 执行相应动画. myAsynTask = new MyAsynTask(); myAsynTask.execute(DRAG_UP); } private void doActionUp_B(MotionEvent event) { mIsRecord = false;// 此时的touch事件完毕,要关闭。 isScroller = true;// ListView可以Scrooler滑动. loadingMoreState = loadingMoreState.LV_NORMAL;// 状态也回归最初状态 // 执行相应动画. myAsynTask = new MyAsynTask(); myAsynTask.execute(DRAG_DOWN); } /*** * ListView 滑动监听 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstItemIndex = firstVisibleItem; mLastItemIndex = firstVisibleItem + visibleItemCount; } @Override public void onClick(View v) { } /*** * 用于产生动画 * * @author zhangjia * */ private class MyAsynTask extends AsyncTask<Integer, Integer, Void> { private final static int STEP = 30;// 步伐 private final static int TIME = 5;// 休眠时间 private int distance;// 距离(该距离指的是:mHeadView的PaddingTop+mHeadView的高度,及默认位置状态.) private int number;// 循环执行次数. private int disPadding;// 时时padding距离. private int DRAG; @Override protected Void doInBackground(Integer... params) { try { this.DRAG = params[0]; if (params[0] == DRAG_UP) { // 获取距离. distance = mHeadView.getPaddingTop() + Math.abs(mHeadViewHeight); } else { // 获取距离. distance = mFootView.getPaddingTop() + Math.abs(mHeadViewHeight); } // 获取循环次数. if (distance % STEP == 0) { number = distance / STEP; } else { number = distance / STEP + 1; } // 进行循环. for (int i = 0; i < number; i++) { Thread.sleep(TIME); publishProgress(STEP); } } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); switch (DRAG) { case DRAG_UP: disPadding = Math.max(mHeadView.getPaddingTop() - STEP, -1 * mHeadViewHeight); mHeadView.setPadding(0, disPadding, 0, 0);// 回归. break; case DRAG_DOWN: disPadding = Math.max(mFootView.getPaddingTop() - STEP, -1 * mHeadViewHeight); mFootView.setPadding(0, disPadding, 0, 0);// 回归. break; default: break; } } } }运行效果:
默认效果 下拉拖拽效果(会自动回缩) 上拉拖拽效果(会自动回缩)
前面那章实现起来有点小BUG,正在处理,不过这个实现起来没有发现什么BUG,要说BUG的话,那么就是优化,因为我觉得上面效果是实现了,可是性能觉得有点差,比如说“我每次UP的时候要执行任务,那么就要创建任务对象,你想想看,每次执行都要创建,那么要创建多少对象,虽说JAVA虚拟机会自动回收,但是总觉得不是很完善,嗯,临时就如此了,自己在研究研究看.
至于微信,陌陌等大多数应用都是(数据少的话,就上下都可以拖拽,只是一个人性效果,而数据多的话,上部用于加载过时数据.下部只是个形式.),效果实现起来也不难,只是进行了些判断,效果嘛,其实上面自定义ListView整理下就OK了.
上面我详细给出了两个自定义源码的实现,大家可以直接引用.
就说到这里,如有疑问请留言。
另外,如果对您有帮助的话,记得赞一个哦.
在此:Thanks for you !
相关推荐
自定义ListView实现下拉刷新+加载更多功能Demo
自定义listview下拉刷新上拉加载更多以及与google官方的下拉刷新结合使用
android 实现listview动态下拉刷新,动态加载数据 可以避免一次数据加载过多
这次就不上图了,例子太多太多了,想必大家都见过...具体应用:(只需要这样引用即可.)在Activity中的调用,相比大家都清楚,开个异步或线程进行加载数据,这里我简单说一下异步使用,线程同理.代码如下:先声明一点,
Android Listview 单独的下拉刷新和 下拉刷新以及加载更多结合.
NULL 博文链接:https://geningaixin.iteye.com/blog/2034379
ListView下拉刷新 ListView下拉刷新ListView下拉刷新ListView下拉刷新
android Listview下拉刷新 上拉(滑动分页)加载更多 高仿新浪微博下拉刷新,同时实现了滑动加载下一页
android自定义listview控件 可实现下拉刷新、下拉加载更多的功能,代码比较简单,内有详细注释和实现方法,方便初学者学习
android listview demo 下拉加载更多 已调试通过。
Android下拉刷新+上拉加载+滑动删除的ListView,简单易用,快速集成到应用。
Android Listview下拉刷新上拉加载源码
Android ListView下拉刷新 Demo.rar
里面有三个例子 是分开的 listView 的下拉刷新上拉加载,现在大多数的应用都用的这一款
Android 自定义ListView,分别实现下拉刷新的ListView及上拉加载的ListView 详情可以参考我的博客 http://blog.csdn.net/sunshanai/article/details/51622917
http://blog.csdn.net/qq_32673327/article/details/52012327 这是关于Demo实现的博客
ListView下拉刷新点击加载更多
自定义listview下拉刷新与上拉加载更多方法,其中MyPulltoRefresh只包含下拉加载更多,MyPulltoRefresh22种都包含,选择需要的引入xml文件哦
自定义 ListView 实现 下拉刷新 加载更多