使用viewGroupe实现左右拖到的效果

本文详细介绍了如何在滚动视图中通过触摸事件 ACTION_DOWN、ACTION_MOVE 和 ACTION_UP 进行滚动操作,并在手指离开屏幕时判断是否滑动到下一屏或返回原位,同时实现了滑动过程中的过渡效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

重写了 ViewGroupe的 onlayout  onMeasure方法 然后通过onTouchEvent事件的Action_down  Action_move 和 Activion_up进行相关处理

主要方法:

随手指拖动 滚动:

case MotionEvent.ACTION_MOVE:

			int deltaX = (int) (mLastMotionX - x);

			mLastMotionX = x;
			//Log.e(TAG, "moving slop"+deltaX);
			scrollBy(deltaX, 0);

			break;

手指离开判断是 滚到下一屏还是 回到原位:

case MotionEvent.ACTION_UP:

			Log.e(TAG, "event : up");


			final VelocityTracker velocityTracker = mVelocityTracker;

			velocityTracker.computeCurrentVelocity(1000);

			int velocityX = (int) velocityTracker.getXVelocity();//滑动的速度

			Log.e(TAG, "velocityX:" + velocityX);
			
			
         // 当快速滑动的时候(速度超过此值) 不必过中间点也可以划过一屏    前提当前屏不能是第一屏和最后一屏
			if (velocityX > SNAP_VELOCITY && mCurScreen > 0) { 

				// Fling enough to move left

				Log.e(TAG, "snap left");

				snapToScreen(mCurScreen - 1);

			} else if (velocityX < -SNAP_VELOCITY

			&& mCurScreen < getChildCount() - 1) {

				// Fling enough to move right

				Log.e(TAG, "snap right");

				snapToScreen(mCurScreen + 1);

			} else {

				snapToDestination(); 

			}

			if (mVelocityTracker != null) {

				mVelocityTracker.recycle();

				mVelocityTracker = null;

			}

			mTouchState = TOUCH_STATE_REST;

			break;

   通过滚到距离是否过中间点判断是否滑动回来 还是下一屏

 

	public void snapToDestination() {

		final int screenWidth = getWidth();

		final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth; //计算中间分割点 是滑动一瓶 还是恢复原来的位置

		snapToScreen(destScreen);

	}
 

手指离开时滑动操作:

	/**
	 * 
	 * 方法名称:snapToScreen 方法描述:滑动到到第whichScreen(从0开始)个界面,有过渡效果
	 * 
	 * @param whichScreen
	 */

	public void snapToScreen(int whichScreen) {

		// get the valid layout page

		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));//确保大于0 小于最大的一屏
		Log.e(TAG, "snapToScreen"+whichScreen);

		if (getScrollX() != (whichScreen * getWidth())) {

			final int delta = whichScreen * getWidth() - getScrollX();

			mScroller.startScroll(getScrollX(), 0, delta, 0,

			Math.abs(delta) * 2);

			mCurScreen = whichScreen;

			doScrollAction(mCurScreen);

			invalidate(); // Redraw the layout  必须在主线程调用此方法 不是UI线程 用 postInvalidate();
			

		}

	}

要想滑动有效果 必须重新computeScroll 方法

/*
 * 重新方法  请求滚动的 scrollX 和 scrollY 如不重写此方法 子视图无法更新x和y 不能产生滚动效果
 *  被父类调用来请求子类更新它们的scrollX 和y,特别当子类使用Scroller进行滑动动作的时候
 */

	@Override
	public void computeScroll() {

		// TODO Auto-generated method stub

		if (mScroller.computeScrollOffset()) {

			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

			postInvalidate();

		}
	}

初始化操作:

	public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {

		super(context, attrs, defStyle);

		mScroller = new Scroller(context);

		// mCurScreen = mDefaultScreen;

		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

		System.out.println("aaaaaaaaaaaaaaaaaaaaa" + mTouchSlop);

	}

	@Override
	// 当这个视图为它的子类分配大小或确定位置的时候会调用此方法 左 上 右 下 子类要调layout
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		Log.e(TAG, "onLayout");
		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());// 确定三个视图的位置 大小

				childLeft += childWidth;

			}

		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		Log.e(TAG, "onMeasure");
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		final int width = MeasureSpec.getSize(widthMeasureSpec);

		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);

		if (widthMode != MeasureSpec.EXACTLY) {

			throw new IllegalStateException(

			"ScrollLayout only canmCurScreen run at EXACTLY mode!");

		}

		final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

		if (heightMode != MeasureSpec.EXACTLY) {

			throw new IllegalStateException(

			"ScrollLayout only can run at EXACTLY mode!");

		}

		// The children are given the same width and height as the scrollLayout

		final int count = getChildCount();

		for (int i = 0; i < count; i++) {

			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);

		}

		// Log.e(TAG, "moving to screen "+mCurScreen);

		scrollTo(mCurScreen * width, 0);// 滑动到现在指定的屏

		doScrollAction(mCurScreen);//初始化时提醒现在是第几个屏幕
	}

 xml 配置文件:

<?xml version="1.0" encoding="utf-8"?>
<com.soom.scrollLayout.ScrollLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ScrollLayoutTest"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#FF0000" >
    </LinearLayout>

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#00FF00" >
    </FrameLayout>

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#0000FF" >
    </FrameLayout>

</com.soom.scrollLayout.ScrollLayout>


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值