这节,就一个任务如何把上节自定义的翻页动画控件整进下拉列表中去。

  由于是自定义的下拉列表控件,我们需要自定义能够上啦下滑的listview,这势必会造成这个问题,上拉刷新要响应相应touch事件,拖拽也似乎也要相应触摸事件,这势必会造成了一种事件的冲突了,怎么解决了。我这里用一个变量来区分一下,伪代码如下:

package com.routemap_infomation.utils;import java.util.Date;import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug.IntToString;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;import com.routemap_infomation.myfocus.R;
import com.routemap_infomation.myfocus.LineFocusActivity.RouteItem;
import com.routemap_infomation.utils.GestureJudgeUtils.OnGestureResult;/*** 该类主要是完成 头部部分的功能封装* * 一个可以监听ListView是否滚动到最顶部或最底部的自定义控件* 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br> 如果加以改进,可以实现监听scroll滚动的具体位置等* * @author 进*/public class ScrollOverListView extends ListView implements OnScrollListener {public static boolean canRefleash = true;private static final String TAG = "listview";/**松开更新**/private static final  int RELEASE_TO_REFRESH = 0;/**下拉更新**/private static final  int PULL_TO_REFRESH = 1;/**更新中**/private static final  int REFRESHING = 2;/**无**/private static final  int DONE = 3;/**加载中**/private static final  int LOADING = 4;/**实际的padding的距离与界面上偏移距离的比例**/private static final int RATIO = 3;/**是否要使用下拉刷新功能**/public boolean showRefresh = true;public boolean isDelete;public void setIsDelete(boolean isDelete){this.isDelete = isDelete;}private int mLastY;private int mBottomPosition;private SlideView mFocusedItemView;private LayoutInflater inflater;/**头部刷新的布局**/private LinearLayout headView;/**头部显示下拉刷新等的控件**/private TextView tipsTextview;/**刷新控件**/private TextView lastUpdatedTextView;/**箭头图标**/private ImageView arrowImageView;/**头部滚动条**/private ProgressBar progressBar;/**显示动画**/private RotateAnimation animation;/**头部回退显示动画**/private RotateAnimation reverseAnimation;/** 用于保证startY的值在一个完整的touch事件中只被记录一次**/private boolean isRecored;/**头部高度**/private int headContentHeight;/**开始的Y坐标**/private int startY;/**第一个item**/private int firstItemIndex;/**状态**/private int state;private boolean isBack;private GestureDetector gestureDetector;/** 空的 */private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener() {@Overridepublic boolean onListViewTopAndPullDown(int delta) {return false;}@Overridepublic boolean onListViewBottomAndPullUp(int delta) {return false;}@Overridepublic boolean onMotionDown(MotionEvent ev) {return false;}@Overridepublic boolean onMotionMove(MotionEvent ev, int delta) {return false;}@Overridepublic boolean onMotionUp(MotionEvent ev) {return false;}};public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}public ScrollOverListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public ScrollOverListView(Context context, PullDownView pullDownView) {super(context);isDelete = pullDownView.getIsDelete();init(context);}/**出事化控件**/private void init(Context context) {mBottomPosition = 0;setCacheColorHint(0);inflater = LayoutInflater.from(context);headView = (LinearLayout) inflater.inflate(R.layout.pull_down_head,null);arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);arrowImageView.setMinimumWidth(70);arrowImageView.setMinimumHeight(50);progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView);measureView(headView);headContentHeight = headView.getMeasuredHeight();headView.setPadding(0, -1 * headContentHeight, 0, 0);headView.invalidate();/**列表添加头部**/addHeaderView(headView, null, false);setOnScrollListener(this);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(200);reverseAnimation.setFillAfter(true);state = DONE;gestureDetector=new GestureJudgeUtils(PublicDefine.context,new GestureJudgeUtils.OnGestureResult() {@Overridepublic void onGestureResult(int direction) {// TODO Auto-generated method stubswitch (direction) {case GestureJudgeUtils.GESTURE_LEFT:case GestureJudgeUtils.GESTURE_RIGHT:PublicDefine.isDeleteTag=true;break;case GestureJudgeUtils.GESTURE_DOWN:case GestureJudgeUtils.GESTURE_UP:PublicDefine.isDeleteTag=false;default:break;}}}).Buile();}int currentx=0;int currenty=0;/**触摸事件的处理**/@Overridepublic boolean onTouchEvent(MotionEvent ev) {final int action = ev.getAction();final int y = (int) ev.getRawY();final int deltax=(int)ev.getRawX();if (action==MotionEvent.ACTION_DOWN&&PublicDefine.isDeleteTag) {mFocusedItemView.move();PublicDefine.isDeleteTag=false;return super.onTouchEvent(ev);}if (action==MotionEvent.ACTION_DOWN) {currentx=(int) ev.getRawX();currenty=(int) ev.getRawY();}PublicDefine.isDeleteTag=Horizontal(currentx,currenty,deltax,y);cancelLongPress();if(PublicDefine.isDeleteTag){switch (action) {case MotionEvent.ACTION_DOWN: {int x = (int) ev.getX();int z = (int) ev.getY();int position = pointToPosition(x, z);Log.e(TAG, "postion=" + position);if (position != INVALID_POSITION) {RouteItem data = (RouteItem) getItemAtPosition(position);mFocusedItemView = data.slideView;Log.e(TAG, "FocusedItemView=" + mFocusedItemView);}}default:break;}if (mFocusedItemView != null) {return mFocusedItemView.onRequireTouchEvent(ev);}return super.onTouchEvent(ev);}else{switch (action) {case MotionEvent.ACTION_DOWN:   //按下的时候if (firstItemIndex == 0 && !isRecored) {isRecored = true;startY = (int) ev.getY();Log.v(TAG, "在down时候记录当前位置‘");}// ===========================mLastY = y;final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);if (isHandled) {mLastY = y;return isHandled;}break;case MotionEvent.ACTION_MOVE:    //手指正在移动的时候int tempY = (int) ev.getY();if (showRefresh) {if (!isRecored && firstItemIndex == 0) {Log.v(TAG, "在move时候记录下位置");isRecored = true;startY = tempY;}if (state != REFRESHING && isRecored && state != LOADING) {// 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动// 可以松手去刷新了if (state == RELEASE_TO_REFRESH) {setSelection(0);// 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步if (((tempY - startY) / RATIO < headContentHeight)&& (tempY - startY) > 0) {state = PULL_TO_REFRESH;changeHeaderViewByState();}else if (tempY - startY <= 0) {// 一下子推到顶了state = DONE;changeHeaderViewByState();Log.v(TAG, "由松开刷新状态转变到done状态");}//                        else {// 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步//                            // 不用进行特别的操作,只用更新paddingTop的值就行了//                        }
                        }// 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态if (state == PULL_TO_REFRESH) {setSelection(0);// 下拉到可以进入RELEASE_TO_REFRESH的状态if ((tempY - startY) / RATIO >= headContentHeight) {state = RELEASE_TO_REFRESH;isBack = true;changeHeaderViewByState();Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");}else if (tempY - startY <= 0) {// 上推到顶了state = DONE;changeHeaderViewByState();Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");}}// done状态下if (state == DONE) {if (tempY - startY > 0) {state = PULL_TO_REFRESH;changeHeaderViewByState();}}// 更新headView的sizeif (state == PULL_TO_REFRESH) {headView.setPadding(0, -1 * headContentHeight+ (tempY - startY) / RATIO, 0, 0);}// 更新headView的paddingTopif (state == RELEASE_TO_REFRESH) {headView.setPadding(0, (tempY - startY) / RATIO- headContentHeight, 0, 0);}}}// ==============================================final int childCount = getChildCount();if (childCount == 0){return super.onTouchEvent(ev);}final int itemCount = getAdapter().getCount() - mBottomPosition;final int deltaY = y - mLastY;final int lastBottom = getChildAt(childCount - 1).getBottom();final int end = getHeight() - getPaddingBottom();final int firstVisiblePosition = getFirstVisiblePosition();final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY);if (isHandleMotionMove) {mLastY = y;return true;}/** 到达底部 *  到达底部的事件在另外一个类执行**/if (firstVisiblePosition + childCount >= itemCount&& lastBottom <= end && deltaY < 0) {final boolean isHandleOnListViewBottomAndPullDown;isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY);if (isHandleOnListViewBottomAndPullDown) {mLastY = y;return true;}}break;case MotionEvent.ACTION_UP:    //手指抬起来的时候if (state != REFRESHING && state != LOADING) {//                if (state == DONE) {//                    // 什么都不做//                }if (state == PULL_TO_REFRESH) {state = DONE;changeHeaderViewByState();Log.v(TAG, "由下拉刷新状态,到done状态");}if (state == RELEASE_TO_REFRESH) {state = REFRESHING;changeHeaderViewByState();canRefleash = true;Log.v(TAG, "由松开刷新状态,到done状态");}}isRecored = false;isBack = false;// /======================final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev);if (isHandlerMotionUp) {mLastY = y;return true;}break;default:break;}mLastY = y;return super.onTouchEvent(ev);}
//        }catch(Exception e){
//            Log.i("test", e.getMessage());
//            return true;
//
//        }
    }private boolean Horizontal(int dx, int dy,int dx1,int dy1) {// TODO Auto-generated method stubint deltax=dx-dx1;int deltay=dy-dy1;if (Math.abs(deltax)>=Math.abs(deltay)) {return true;}else {return false;    }}// =============================== public method/*** 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个* * @param index  正数第几个,必须在条目数范围之内*/public void setTopPosition(int index) {if (getAdapter() == null){throw new NullPointerException("You must set adapter before setTopPosition!");}if (index < 0){throw new IllegalArgumentException("Top position must > 0");}}/*** 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个* * @param index  倒数第几个,必须在条目数范围之内*/public void setBottomPosition(int index) {if (getAdapter() == null){throw new NullPointerException("You must set adapter before setBottonPosition!");}if (index < 0){throw new IllegalArgumentException("Bottom position must > 0");}mBottomPosition = index;}/*** 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br>* * @see OnScrollOverListener*/public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener) {mOnScrollOverListener = onScrollOverListener;}/*** 滚动监听接口* * @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)* */public interface OnScrollOverListener {/*** 到达最顶部触发* * @param delta*            手指点击移动产生的偏移量* @return*/boolean onListViewTopAndPullDown(int delta);/*** 到达最底部触发* * @param delta*            手指点击移动产生的偏移量* @return*/boolean onListViewBottomAndPullUp(int delta);/*** 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN}* * @return 返回true表示自己处理* @see View#onTouchEvent(MotionEvent)*/boolean onMotionDown(MotionEvent ev);/*** 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE}* * @return 返回true表示自己处理* @see View#onTouchEvent(MotionEvent)*/boolean onMotionMove(MotionEvent ev, int delta);/*** 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}* * @return 返回true表示自己处理* @see View#onTouchEvent(MotionEvent)*/boolean onMotionUp(MotionEvent ev);}@Overridepublic void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,int arg3) {firstItemIndex = firstVisiableItem;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}// 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及heightprivate 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);}public void onRefreshComplete() {state = DONE;lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());changeHeaderViewByState();}// 当状态改变时候,调用该方法,以更新界面private void changeHeaderViewByState() {switch (state) {case RELEASE_TO_REFRESH:arrowImageView.setVisibility(View.VISIBLE);progressBar.setVisibility(View.GONE);tipsTextview.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.startAnimation(animation);tipsTextview.setText("松开刷新");Log.v(TAG, "当前状态,松开刷新");break;case PULL_TO_REFRESH:progressBar.setVisibility(View.GONE);tipsTextview.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.setVisibility(View.VISIBLE);// 是由RELEASE_To_REFRESH状态转变来的if (isBack) {isBack = false;arrowImageView.clearAnimation();arrowImageView.startAnimation(reverseAnimation);tipsTextview.setText("下拉刷新");} else {tipsTextview.setText("下拉刷新");}Log.v(TAG, "当前状态,下拉刷新");break;case REFRESHING:headView.setPadding(0, 0, 0, 0);progressBar.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.setVisibility(View.GONE);tipsTextview.setText("正在刷新...");lastUpdatedTextView.setVisibility(View.VISIBLE);Log.v(TAG, "当前状态,正在刷新...");break;case DONE:headView.setPadding(0, -1 * headContentHeight, 0, 0);progressBar.setVisibility(View.GONE);arrowImageView.clearAnimation();arrowImageView.setImageResource(R.drawable.pull_down_arrow);tipsTextview.setText("下拉刷新");lastUpdatedTextView.setVisibility(View.VISIBLE);Log.v(TAG, "当前状态,done");break;default:break;}}public void shrinkListItem(int position) {View item = getChildAt(position);if (item != null) {try {((SlideView) item).shrink();} catch (ClassCastException e) {e.printStackTrace();}}}
}

  ①由于这个类继承与listview,因此具有了listview常见属性与方法。

  ②由于他要记录上拉下拉刷新的方式,因此,我们这里需要用定义一些变量来保存,并且要记录他们的状态。  

  ③上拉刷新的状态是需要监听的了,怎么进行监听了,这里依照java中插件机制,依照相应的观察者(observer)模式,需要定义一个接口来监听了。

  ④为了实现动态,这里需要定义不同参数的构造函数了。

  ⑤这里是一个重点,我们定义了一个变量,接上面的论证,我们这里定义了一个变量来确定到底是调用那个触摸方法。

  ⑥一般在android中,init初始化,找到相应控件,数据加载,是一个司空见惯的方法,在这里了,我们还对相应的手势的方法进行了监听,但是后来发现在此情景下,主要是由于touch事件中,无法对手势进行监听。

  ⑦onTouchEvent事件,触摸事件,是这一切事件的重中之重,我这里首先要对其手势方向进行了监听,上文提到无法直接对手势的方向进行监听了,因此我这里粗略进行进行了判断,判断触摸按下与触摸抬起的时候,两点x轴之间的距离与两点y之间的距离孰大孰小。来判断他是竖向滚动,还是横向拖拽。倘若是横向的拖拽,进把触摸事件下放到slideview控件,由她将触摸事件进行处理,否则的话,就由此控件监听上拉下滑的刷新事件。

  ⑧与上节一样,对其滑动以后,也要对其控件的距离,控件位置进行调整,进行计算。

  ⑨这个控件本质是个列表控件,本质是对数据进行处理,因此,我们这里需要一个方法对其数据加以处理的。

  以上的内容就把slideview整合到项目中去了,其最终的效果是这样的:

  

qq消息列表整合上下拉刷新和拖拽删除相关推荐

  1. 浅谈Android列表ListView下拉刷新控件的实现(一)

    ListView下拉刷新的功能到处可见,很多app客户端都存在,比如QQ空间好友动态下拉刷新,网易新闻内容下拉刷新等.相信很多人已经把这个功能运用的很溜,妥妥的吧,接下就来实现一下功能,有个不爽的一点 ...

  2. Android 编程下代码之(QQ消息列表滑动删除)

       这份代码写出来有些时候了,一直没共享,现在把它共享给大家.简单列一下代码中你可以学到的知识点: 自定义控件的实现方式: 事件的拦截分发消费机制: QQ会话列表滑动删除原理: 最后附上源码链接:Q ...

  3. Android开发学习之仿手机QQ消息列表侧滑删除效果

    今天想和大家分享的是手机QQ消息列表侧滑删除效果,这种效果在IOS中被封装为一个列表控件,而手机QQ则是将这个功能移植到了Android上,换言之,这并非是手机QQ的独创.尽管如此,用户体验依然得到了 ...

  4. Android自定义组合控件---教你如何自定义下拉刷新和左滑删除

    绪论 最近项目里面用到了下拉刷新和左滑删除,网上找了找并没有可以用的,有比较好的左滑删除,但是并没有和下拉刷新上拉加载结合到一起,要不就是一些比较水的结合,并不能在项目里面使用,小编一着急自己组合了一 ...

  5. 超简单PictureSelector使用,从相册中选取多张图片并显示再列表中,微信样式,解决相册全白问题,可以拖拽删除并排序

    PictureSelector原项目地址 相册全白是因为在初始化的过程中缺少了一样设置 .loadImageEngine(GlideEngine.createGlideEngine()) 想要微信那样 ...

  6. RecyclerView的基础使用 +点击添加列表数据 +下拉刷新、上拉加载更多

    一.RecyclerView的基础使用. 第一步:添加recyclerview控件. 第二步:创建布局文件(xml) + 单独的类控制布局里面的控件(MyViewHolder). 第三步:创建一个适配 ...

  7. android获取qq消息列表,获取所有qq好友、全部群所有成员部分信息,并保存列表至电子表格文件以备份信息的爬虫...

    20200521更新详细步骤说明 52pojie的markdown支持有些问题,代码注释识别成了标题,懒得调了,大家将就一下吧,实在受不了可以访问我个人网站查看https://cjh0613.gith ...

  8. Android高仿QQ消息列表、侧拉删除菜单按钮效果

    目    录(本篇字数:3000) 介绍 Item布局 自定义存放Item父容器 Bug分析 ·一.解决滑动冲突 二.解决Item点击事件的冲突 三.限制只能有一个menu被打开 博文续篇 ListV ...

  9. autojs模仿qq消息列表侧拉置顶删除菜单

    牙叔教程 简单易懂 效果展示 思路 使用安卓qq可知, 消息列表每次只能有一个侧拉菜单被打开 autojs版本 9.0.4 你将学到以下知识点 RecyclerView的基本使用 拦截rv的触摸事件 ...

最新文章

  1. 【数据挖掘】分类任务简介 ( 分类概念 | 分类和预测 | 分类过程 | 训练集 | 测试集 | 数据预处理 | 有监督学习 )
  2. Redis分布式快速入门
  3. SAP One Order redesign里的新CDS view
  4. Orchard Core Framework:ASP.NET Core 模块化,多租户框架
  5. 怎么修改ipv4服务器,如何修改ipv4 wins 服务器地址
  6. Linux系统编程(四)信号
  7. AJAX 异步加载技术
  8. leetcode129. 求根到叶子节点数字之和(dfs)
  9. Python 函数(三)
  10. 计算机组成原理 唐朔飞 思维导图
  11. AcWing 674.超级2048
  12. python可以制作游戏脚本吗_python制作填词游戏步骤详解
  13. “元宵”大师带你用Python量化交易
  14. VUE vue Expected Object, Function, got String with value “xxx;
  15. 根据中国时间求美国时间
  16. mips-mti-gnu-linux,【歪门邪道】利用WSL搭建MIPS32构建环境
  17. 容器启动失败 ERROR: for log Cannot start service log: OCI runtime create failed: container_linux.go:346
  18. absl教程(五):Synchronization library
  19. 1.Review of Linear Algebra
  20. sanity check

热门文章

  1. java kdj macd_MACD和KDJ相结合-文华财经知识 -程序化交易(CXH99.COM)
  2. 面试官一看就想留下的简历,到底是怎么写的?
  3. ruby+watir-webdriver自动化测试入门
  4. 2020年嵌入式第十一届省赛真题解析
  5. 渣渣白教你使用工具Safe3 SQL注入安全检测工具
  6. MySQL-查询篇(超详细)
  7. 估值77.5亿美金的虚拟线上活动工具 #Hopin 让世界感觉更亲近
  8. 互联网网赚项目入门!
  9. 子沐课堂——Flask小帅锅勾搭Ajax萌妹纸
  10. 度量学习 流形学习_流形学习2