类似QQ2013联系人模块listview上下滚动与左右滑动item间模块切换

本文介绍了一种自定义的滑动布局实现方案,通过Scroller组件实现流畅的左右滑动切换效果,并结合自定义ListView实现了复杂的触摸事件处理,确保了在进行左右滑动时不会干扰到ListView的上下滚动。
摘要由CSDN通过智能技术生成
去年就一直纠结这个问题,也一直没想出解决办法,今年转行不做 安卓了,今天把程序翻出来看看,无意间想到解决办法就试了一下,发现还不错,立马上来分享。
    简单的listview使用我就不多说了,先说模块的左右滑动,废话不多说,先上代码。
package com.huicui.music.custom;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

import com.huicui.music.custominterface.OnViewChangeListener;
import com.huicui.music.util.BaseTools;
/**
 *@author zero (975804495@qq.com)
 *@date 2012-10-19
 */
public class MyScrollLayout extends ViewGroup{
    private static final String TAG = "ScrollLayout";
    private VelocityTracker mVelocityTracker;
    private static final int SNAP_VELOCITY = 600;
    private Scroller  mScroller;
    private int mCurScreen;
private int mDefaultScreen = 0;
private float mLastMotionX;
    
 //   private int mTouchSlop;
    
//    private static final int TOUCH_STATE_REST = 0;
//    private static final int TOUCH_STATE_SCROLLING = 1;
//    private int mTouchState = TOUCH_STATE_REST;
    
    private OnViewChangeListener mOnViewChangeListener;
public MyScrollLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
init(context);
}
public MyScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init(context);
}
public MyScrollLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
init(context);
}
private void init(Context context)
{
mCurScreen = mDefaultScreen;    
//   mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();    
   mScroller = new Scroller(context); 
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//if (changed) {    
           int childLeft = 0;
           final int childCount = getChildCount();
           for (int i=0; i<childCount; i++) {    
               final View childView = getChildAt(i);    
               if (childView.getVisibility() != View.GONE) {    
                   final int childWidth = childView.getMeasuredWidth();    
                   childView.layout(childLeft, 0,childLeft+childWidth, childView.getMeasuredHeight());//left top right bottom
                   childLeft += childWidth;
               }
           }
   //    }
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
   final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
        scrollTo(mCurScreen * width, 0);
}
/**Calculation the point*/
public void snapToDestination() {
   final int screenWidth = getWidth();
   final int destScreen = (getScrollX()+ screenWidth/2)/screenWidth;
   snapToScreen(destScreen);
}
public void snapToScreen(int whichScreen) {
// get the valid layout page
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1));//key the right point
//Log.e("MyScrollLayout","print the getScroll" + getScrollX());
if (getScrollX() != (whichScreen*getWidth())) {
BaseTools.Log(TAG, "getScrollX:" + getScrollX(), 3);
final int delta = whichScreen*getWidth()-getScrollX();
mScroller.startScroll(getScrollX(), 0,delta, 0, Math.abs(delta)*2);//the start point ,the distance ,the duration time
mCurScreen = whichScreen;
invalidate();//repaint
// Redraw the layout
if (mOnViewChangeListener != null)
{
mOnViewChangeListener.OnViewChange(mCurScreen);
}
}
}

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

@Override
public boolean onTouchEvent(MotionEvent event) {
BaseTools.Log(TAG, "OnTouch--------", 1);
       final int action = event.getAction();
       final float x = event.getX();
       final float y = event.getY();

       switch (action) {
       case MotionEvent.ACTION_DOWN:
       
        Log.i("", "onTouchEvent  ACTION_DOWN");
       
        if (mVelocityTracker == null) {
           mVelocityTracker = VelocityTracker.obtain();
           mVelocityTracker.addMovement(event);
   }
       
           if (!mScroller.isFinished()){
               mScroller.abortAnimation();
           }
           
           mLastMotionX = x;
           break;
           
       case MotionEvent.ACTION_MOVE:
        Log.i("", "onTouchEvent  ACTION_MOVE");
        int deltaX = (int)(mLastMotionX - x);
       
        if (IsCanMove(deltaX))
        {
        if (mVelocityTracker != null)
        {
              mVelocityTracker.addMovement(event);
        }
        mLastMotionX = x;
        scrollBy(deltaX, 0);
        }
         
        break;
               
       case MotionEvent.ACTION_UP:
       
        int velocityX = 0;
           if (mVelocityTracker != null)
           {
            mVelocityTracker.addMovement(event);
            mVelocityTracker.computeCurrentVelocity(1000);
            velocityX = (int) mVelocityTracker.getXVelocity();
           }
           
           if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
               // Fling enough to move left       
               //Log.e(TAG, "snap left:" + velocityX);
               snapToScreen(mCurScreen - 1);
           } else if (velocityX < -SNAP_VELOCITY       
                   && mCurScreen < getChildCount() - 1) {       
               // Fling enough to move right
               //Log.e(TAG, "snap right:" + velocityX);
               snapToScreen(mCurScreen + 1);
           } else {
            //Log.e(TAG, "do the action according to the getScrollX()");
               snapToDestination();
           }
           if (mVelocityTracker != null) {
               mVelocityTracker.recycle();
               mVelocityTracker = null;
           }
     //      mTouchState = TOUCH_STATE_REST;
           break;
       }
       
       return true;
}
/**judge Boundary*/
public boolean IsCanMove(int deltaX)
{
if(getScrollX() <= 0 && deltaX < 0 )
{
return false;
}
if(getScrollX() >=  (getChildCount() - 1) * getWidth() && deltaX > 0)
{
return false;
}
return true;
}
/**set listener*/
public void SetOnViewChangeListener(OnViewChangeListener listener)
{
mOnViewChangeListener = listener;
}

}
    这个类说白了就是利用Scroller实现滚动,基本没什么技术含量,注意snapToScreen()方法,这个是用来翻页调用的,比如触发了翻到第二页事件,只要穿进参数1就好了,Activity调用时直接在xml里面定义,例如:
    <com.huicui.music.custom.MyScrollLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/topiclayout"
        android:layout_above="@+id/control"
        android:id="@+id/mainscroll"
        >
        <RelativeLayout  android:background="@drawable/w01"    
 android:layout_width="fill_parent"    
 android:layout_height="fill_parent"
 >
 <com.huicui.music.custom.MyListView
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/mainlistview"
     >
 </com.huicui.music.custom.MyListView>
      </RelativeLayout>
   <RelativeLayout  android:background="@drawable/w02"    
  android:layout_width="fill_parent"    
  android:layout_height="fill_parent"
  >
   </RelativeLayout>
    </com.huicui.music.custom.MyScrollLayout>
下面是重头戏,(我的listview也是自定义控件),要实现上下和左右滑动无非就是判断什么时候把焦点给listview和父控件,但是这个父控件焦点获取我一直没有搞好,不知道该怎么做,什么方法都试过了,listview不是死死控制着焦点就算滑动混乱,今天突然灵机一动,也许我可以让listview的touch事件return true,然后人为控制滑动动画就好了,先根据移动距离判断是listview上下滑动还是父控件左右滑动,如果是左右滑动则让listview的touch事件return true,不让他乱动,同时调用 snapToScreen()方法人为滚动,如果是listview滚动则在触发ACTION_UP之前不允许触发左右滑动事件(就看谁先满足滚动条件了), 至于随手指移动而缓慢滑动的方式大家可以尝试一下,应该也是可以的,下面贴代码。
class ontouchlistener implements OnTouchListener{

@Override
public boolean onTouch(View v, MotionEvent event) {
// listview.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
// BaseTools.Log(TAG, "touchlisview", 1);
// ((HuicuiMusic)context).mScrollLayout.snapToScreen(1);
final int action = event.getAction();
       final float x = event.getX();
       final float y = event.getY();
       
       switch (action) {
       case MotionEvent.ACTION_DOWN:
        BaseTools.Log(TAG, "Acttion down", 2);
        startPoint.set((int)event.getX(), (int)event.getY());
        relevantListView = false;
        break;
       case MotionEvent.ACTION_MOVE:
        movePoint.set((int)event.getX(), (int)event.getY());
        BaseTools.Log(TAG, "X:" + Math.abs(movePoint.x - startPoint.x) + ";Y:" + Math.abs(movePoint.y - startPoint.y), 1);
        if(!focusInListView && startPoint.x - movePoint.x  > 60){
        ((HuicuiMusic)context).mScrollLayout.snapToScreen(1);
        relevantListView = true;
        return true;
        }
        if(Math.abs(movePoint.y - startPoint.y) > 60){
        focusInListView = true;
        return false;
        }
        break;
       case MotionEvent.ACTION_UP:
        focusInListView = false;
        break;
       }
       if(relevantListView){
        return true;
       }else
       return false;
}

}
给listview设置这个listener就OK了。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
现在ViewPager就能解决这种问题了吧,难点也不过是listView和ViewPager的焦点问题都很好解决,唉,当初那是得有多累。。。。不过编程思想可以拿来借鉴。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值