您的位置:首页 > 其它

Scroller的用法,VelocityTracker用法,以及滑动冲突的处理

2016-12-14 21:36 435 查看
参考:http://blog.csdn.net/yanghuinipurean/article/details/50419455

/**
* 仿微信刷新
* @author Nipuream
*/
public class WXLayout extends LinearLayout{

private static final String TAG = "WXLayout";
private int mTouchSlop;
private boolean mIsBeingDragged = false;
private float mLastMotionY;
private float  mInitialMotionY;
private float resistance = 0.6f;
private Scroller mScroller;
private ListView mListView;
private boolean isMove = false;
private int duration = 300;
private ScrollRershListener l;
private boolean isRersh = false;

public WXLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init(context);
}

private void init(final Context context){
ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
DecelerateInterpolator interpolator = new DecelerateInterpolator();
mScroller = new Scroller(context,interpolator);
post(new Runnable() {
@Override
public void run() {
// ?主线程的最后才获取mListView?
mListView = (ListView) WXLayout.this.getChildAt(0);
}
});
}

//处理滑动冲突
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
final  int action = ev.getAction();

//手势抬起和取消,不阻碍事件传递
if(action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP){
mIsBeingDragged = false;
return false;
}

//如果mIsBeingDragged为true表示正在拖动,那么除了down事件都不发送给子元素
//down事件应该要发给子元素,因为down触发时,不能判定之后父元素是否会滑动,从而拦截子元素的事件
if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
return true;
}

switch(action){
case MotionEvent.ACTION_DOWN:{
//记录按下的位置,down事件一定会走到此处
mLastMotionY = mInitialMotionY = ev.getY();
mIsBeingDragged = false;
break;
}
case MotionEvent.ACTION_MOVE:{
final float y = ev.getY(), x = ev.getX();
final float diff, absDiff;
diff = y - mLastMotionY;
absDiff = Math.abs(diff);
if(absDiff > mTouchSlop){   //如果滑动的距离足够大
if(diff > 1){           //并且是手指向下滑动
//并且ListView的第一个元素已经可以被看到了
if(mListView.getFirstVisiblePosition()==0){
View view = mListView.getChildAt(0);
Rect rect = new Rect ();
view.getLocalVisibleRect(rect);
//并且第一个元素已经滑到listView的顶部了
if(rect.top == 0){
//那么事件将由listView的父元素接管,来模拟微信的效果,
//mInitialMotionY应该在此处赋值,当作滑动的起始点
mLastMotionY = y;
mIsBeingDragged = true;
}
}
}
}
break;
}
}
return mIsBeingDragged;
}

@Override
public boolean
4000
onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub

//如果碰触到控件的边缘,就不接受这一系列的action了
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
return false;
}

//如果Scroller正在滑动,就不接受这次事件了
if(isMove){
return false;
}

switch(event.getAction()){
case MotionEvent.ACTION_DOWN:{
//因为此Layout元素不会拦截down事件,所以按理说执行不到此处?
mLastMotionY = mInitialMotionY = event.getY();
return true;
}
case MotionEvent.ACTION_MOVE:{
if (mIsBeingDragged) {
if(l!=null && !isRersh){
l.startRersh();
isRersh = true;
}
mLastMotionY = event.getY();
float moveY = mLastMotionY - mInitialMotionY;   //计算layout的滑动距离
if(l != null){
l.Rersh(moveY);
}
if(moveY > 0){  //设置layout的下拉位置
int value = (int) Math.abs(moveY);
scrollTo(0, - (int)(value*resistance));
}
return true;
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:{
//将layout用动画mScroller.startScroll来复原
if(mIsBeingDragged){
mIsBeingDragged = false;
startMoveAnim(getScrollY(), Math.abs(getScrollY()), duration);
if(l!= null && isRersh && (event.getY() - mInitialMotionY) > 0){
l.endRersh(event.getY() - mInitialMotionY);
isRersh = false;
}
return true;
}
break;
}
}
return super.onTouchEvent(event);
}

public void startMoveAnim(int startY, int dy, int duration) {
isMove = true;
mScroller.startScroll(0, startY, 0, dy, duration);
invalidate();//通知UI线程的更新
}

@Override
public void computeScroll() {
//判断是否还在滚动,还在滚动为true
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
//更新界面
postInvalidate();
isMove = true;
} else {
isMove = false;
}
super.computeScroll();
}

public interface ScrollRershListener{
void Rersh(float value);
void startRersh();
void endRersh(float value);
}

public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}

public void setOnScrollRershListener(ScrollRershListener l){
this.l = l;
}
}


/**
* 仿QQ侧滑删除
* @author Nipuream
*
*/
public class SlideListView extends ListView implements OnTouchListener,OnClickListener{

private static final String TAG = "SlideListView";
private Context mContext;
private Scroller mScroller;
/**
* 初始值
*/
private float initalXvalue,mLastXvalue;
private float initalYvalue,mLastYvalue;
/**
* 确认滑动的最小速度
*/
private int MIN_VELOCITY = 800;
/**
* 速度跟踪器
*/
private VelocityTracker velocityTracker;
/**
* 正在被拖动的view
*/
private View dragView;
/**
* 正在被拖动的position
*/
private int touchPos;
/**
* 默认的最小滑动距离
*/
private int mTouchSlop;
/**
* 滑动时间
*/
private int DURATION_TIME = 300;
/**
* 出现删除按钮的Item
*/
private View tempView ;
/**
* 出现删除按钮的position
*/
private int tempPos ;
/**
* 删除按钮
*/
private Button deleteBtn;
/**
* 移除接口
*/
private RemoveItemListener l;
/**
* 是否可以滑动
*/
private boolean isSlide = false;

public SlideListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init(context);
}

private void init(Context context){
mContext = context;
AccelerateInterpolator interpolator = new AccelerateInterpolator();
mScroller = new Scroller(context,interpolator);
velocityTracker = VelocityTracker.obtain();
ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
setOnTouchListener(this);
}

/**
* 捕捉用户到底拖动了哪个view
* 拦截事件
*/

//此方法一定会被调用,所以在此方法内纪录触摸到的Item
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
final int action = ev.getAction();

if (action == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
return false;
}

if(action == MotionEvent.ACTION_DOWN){

mLastXvalue = initalXvalue = ev.getX();
mLastYvalue = initalYvalue = ev.getY();

//正在滑动时,事件继续分发
if(!mScroller.isFinished()){
return super.dispatchTouchEvent(ev);
}

//通过点击位置,查出所点击的Item的索引
touchPos  = pointToPosition((int)mLastXvalue, (int)mLastYvalue);

if(touchPos == AdapterView.INVALID_POSITION){
return super.dispatchTouchEvent(ev);
}

GetTracker(ev);
//取得当前触摸的Item
dragView = getChildAt(touchPos - getFirstVisiblePosition());
isSlide = false;
}else if(action == MotionEvent.ACTION_MOVE){
mLastXvalue = ev.getX();
mLastYvalue = ev.getY();
if(velocityTracker == null){
GetTracker(ev);
}
velocityTracker.computeCurrentVelocity(1000);
//当横向滑动速度足够大,或者横向滑动距离足够大时,标记Item为可以滑动
if(Math.abs(velocityTracker.getXVelocity()) > MIN_VELOCITY
||Math.abs(mLastXvalue - initalXvalue)> mTouchSlop
&& Math.abs(mLastYvalue - initalYvalue)  < mTouchSlop){
isSlide = true;
}
}else if(action == MotionEvent.ACTION_UP){
CloseTracker();
}
return super.dispatchTouchEvent(ev);
}

/**
* 消费事件
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(isSlide){
switch(ev.getAction()){
case MotionEvent.ACTION_MOVE:
{
mLastXvalue = ev.getX();
final float moveX = mLastXvalue - initalXvalue;
//横向滑动大于特定值时,dragView跟随手指的位置向左移动
if(Math.abs(moveX) > dip2px(mContext, 20))
{
if(moveX < 0 && Math.abs(moveX)<dip2px(mContext, 100))
{
if(dragView != null){
dragView.scrollTo(Math.abs((int)moveX), 0);
}
return true;
}
}
}
break;
case MotionEvent.ACTION_UP:
{
mLastXvalue = ev.getX();
float scrollDistance = mLastXvalue - initalXvalue;
//手指抬起时,滑到最左或者划回原位
if(scrollDistance < 0){
if(Math.abs(scrollDistance) < dip2px(mContext, 50)){
//滑动回去
if(dragView != null){
mScroller.startScroll(dragView.getScrollX(), 0, -dragView.getScrollX(), 0, DURATION_TIME);
invalidate();
}
}else if(Math.abs(scrollDistance) > dip2px(mContext, 50) ){
//滑动到底
if(dragView != null){
mScroller.startScroll(dragView.getScrollX(), 0, (dip2px(mContext, 100) - Math.abs(dragView.getScrollX())), 0,DURATION_TIME);
invalidate();
tempView = dragView;
tempPos = touchPos;
setListener();
}
}
}
}
break;
}
}
return super.onTouchEvent(ev);
}

private void GetTracker(MotionEvent ev){
if(velocityTracker == null){
velocityTracker  = VelocityTracker.obtain();
}
velocityTracker.addMovement(ev);
}

private void CloseTracker() {
if(velocityTracker != null){
velocityTracker.recycle();
velocityTracker.clear();
velocityTracker = null;
}
}

private void setListener(){
deleteBtn = (Button) tempView.findViewById(R.id.delete);
deleteBtn.setOnClickListener(this);
}

@Override
public void computeScroll() {
// TODO Auto-generated method stub
if(mScroller.computeScrollOffset()){
dragView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
super.computeScroll();
}

public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}

@Override
//返回true将使onTouchEvent无法触发,当某个Item左滑后显示出delete按钮,
//则点击整个layout时,Item复位,且onTouchEvent不能被触发
public boolean onTouch(View v, MotionEvent event) {
if(tempView != null){
dragView = tempView ;
mScroller.startScroll(dragView.getScrollX(), 0, - dragView.getScrollX(), 0,DURATION_TIME);
invalidate();
deleteBtn.setOnClickListener(null);
deleteBtn = null;
tempView = null;
return true;
}
return false;
}

@Override
//点击delete按钮的回调
public void onClick(View v) {
// TODO Auto-generated method stub
if(l != null){
l.remove(tempPos);
dragView = tempView;
dragView.scrollTo(0, 0);
tempView = null;
invalidate();
}
}

public interface RemoveItemListener{
void remove(int pos);
}

public void setOnRemoveItemListener(RemoveItemListener l){
this.l = l;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: