`

自定义ViewGroup 实现拖动跟快速滚动的效果

 
阅读更多
  之前做到个项目要类似listView或者GridView中的控件移动的效果(主屏上所有程序列表上的效果):
1:子控件跟着手指移动
2:快速拨动一下,根据拨动的速度 滑动过去
3:拖过头,放手后弹回去

   但是用listView或者GridView又不好实现项目要求的其他效果..于是继承viewGroup实现以上效果。

   既然要获取拨动速度,并以此滑动。首先想到了OnGestureListener 这个接口,实现这个接口并实现其onFling方法.

  还要控制拖动。重写onTouchEvent方法,并在其中控制内容控件的拖动,反弹等效果

这时候基本已经完成了。。。。测试了一下了,发现了一个问题,当手指点在viewGroup上

进行 拖动是没问题的,但是在子控件上就不行了,这是事件响应的问题 那么还要做如面的处

理:实现onInterceptTouchEvent方法,判断是拖动事件时 ,将事件传递下去。
import java.util.List;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.GestureDetector.OnGestureListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Scroller;
import android.widget.Toast;
import android.widget.ImageView.ScaleType;

public class MyViewGroup extends ViewGroup implements OnGestureListener {

	private float mLastMotionY;// 最后点击的点
	private GestureDetector detector;
	int move = 0;// 移动距离
	int MAXMOVE = 850;// 最大允许的移动距离
	private Scroller mScroller;
	int up_excess_move = 0;// 往上多移的距离
	int down_excess_move = 0;// 往下多移的距离
	private final static int TOUCH_STATE_REST = 0;
	private final static int TOUCH_STATE_SCROLLING = 1;
	private int mTouchSlop;
	private int mTouchState = TOUCH_STATE_REST;
	Context mContext;
	

	public MyViewGroup(Context context) {
		super(context);
		mContext = context;
		// TODO Auto-generated constructor stub
		setBackgroundResource(R.drawable.pic);
		mScroller = new Scroller(context);
		detector = new GestureDetector(this);

		final ViewConfiguration configuration = ViewConfiguration.get(context);
		// 获得可以认为是滚动的距离
		mTouchSlop = configuration.getScaledTouchSlop();

		// 添加子View
		for (int i = 0; i < 48; i++) {
			final Button 	MButton = new Button(context);
			MButton.setText("" + (i + 1));
			MButton.setOnClickListener(new OnClickListener() {
				
				public void onClick(View v) {
					// TODO Auto-generated method stub
					Toast.makeText(mContext, MButton.getText(), Toast.LENGTH_SHORT).show(); 
				}
			});
			addView(MButton);
		}
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			// 返回当前滚动X方向的偏移
			scrollTo(0, mScroller.getCurrY());
			postInvalidate();
		}
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		final int action = ev.getAction();

		final float y = ev.getY();
		switch (ev.getAction())
		{
		case MotionEvent.ACTION_DOWN:

			mLastMotionY = y;
			mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
					: TOUCH_STATE_SCROLLING;
			break;
		case MotionEvent.ACTION_MOVE:
			final int yDiff = (int) Math.abs(y - mLastMotionY);
			boolean yMoved = yDiff > mTouchSlop;
			// 判断是否是移动
			if (yMoved) {
				mTouchState = TOUCH_STATE_SCROLLING;
			}
			break;
		case MotionEvent.ACTION_UP:
			mTouchState = TOUCH_STATE_REST;
			break;
		}
		return mTouchState != TOUCH_STATE_REST;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {

		// final int action = ev.getAction();

		final float y = ev.getY();
		switch (ev.getAction())
		{
		case MotionEvent.ACTION_DOWN:
			if (!mScroller.isFinished()) {
				mScroller.forceFinished(true);
				move = mScroller.getFinalY();
			}
			mLastMotionY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			if (ev.getPointerCount() == 1) {
				
				// 随手指 拖动的代码
				int deltaY = 0;
				deltaY = (int) (mLastMotionY - y);
				mLastMotionY = y;
				Log.d("move", "" + move);
				if (deltaY < 0) {
					// 下移
					// 判断上移 是否滑过头
					if (up_excess_move == 0) {
						if (move > 0) {
							int move_this = Math.max(-move, deltaY);
							move = move + move_this;
							scrollBy(0, move_this);
						} else if (move == 0) {// 如果已经是最顶端 继续往下拉
							Log.d("down_excess_move", "" + down_excess_move);
							down_excess_move = down_excess_move - deltaY / 2;// 记录下多往下拉的值
							scrollBy(0, deltaY / 2);
						}
					} else if (up_excess_move > 0)// 之前有上移过头
					{					
						if (up_excess_move >= (-deltaY)) {
							up_excess_move = up_excess_move + deltaY;
							scrollBy(0, deltaY);
						} else {						
							up_excess_move = 0;
							scrollBy(0, -up_excess_move);				
						}
					}
				} else if (deltaY > 0) {
					// 上移
					if (down_excess_move == 0) {
						if (MAXMOVE - move > 0) {
							int move_this = Math.min(MAXMOVE - move, deltaY);
							move = move + move_this;
							scrollBy(0, move_this);
						} else if (MAXMOVE - move == 0) {
							if (up_excess_move <= 100) {
								up_excess_move = up_excess_move + deltaY / 2;
								scrollBy(0, deltaY / 2);
							}
						}
					} else if (down_excess_move > 0) {
						if (down_excess_move >= deltaY) {
							down_excess_move = down_excess_move - deltaY;
							scrollBy(0, deltaY);
						} else {
							down_excess_move = 0;
							scrollBy(0, down_excess_move);
						}
					}
				}		
			} 
			break;
		case MotionEvent.ACTION_UP:			
			// 多滚是负数 记录到move里
			if (up_excess_move > 0) {
				// 多滚了 要弹回去
				scrollBy(0, -up_excess_move);
				invalidate();
				up_excess_move = 0;
			}
			if (down_excess_move > 0) {
				// 多滚了 要弹回去
				scrollBy(0, down_excess_move);
				invalidate();
				down_excess_move = 0;
			}
			mTouchState = TOUCH_STATE_REST;
			break;
		}
		return this.detector.onTouchEvent(ev);
	}

	int Fling_move = 0;

	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		 //随手指 快速拨动的代码
		Log.d("onFling", "onFling");
		if (up_excess_move == 0 && down_excess_move == 0) {

			int slow = -(int) velocityY * 3 / 4;
			mScroller.fling(0, move, 0, slow, 0, 0, 0, MAXMOVE);
			move = mScroller.getFinalY();
			computeScroll();
		}
		return false;
	}

	public boolean onDown(MotionEvent e) {
		// TODO Auto-generated method stub
		return true;
	}

	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {
		return false;
	}

	public void onShowPress(MotionEvent e) {
		// // TODO Auto-generated method stub
	}

	public boolean onSingleTapUp(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	public void onLongPress(MotionEvent e) {
		// TODO Auto-generated method stub
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// TODO Auto-generated method stub
		int childTop = 0;
		int childLeft = 0;
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);
			if (child.getVisibility() != View.GONE) {
				child.setVisibility(View.VISIBLE);
				child.measure(r - l, b - t);
				child
						.layout(childLeft, childTop, childLeft + 80,
								childTop + 80);
				if (childLeft < 160) {
					childLeft += 80;
				} else {
					childLeft = 0;
					childTop += 80;
				}
			}
		}
	}

}

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

public class Workspace extends ViewGroup {

	public Workspace(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		addView(new MyViewGroup(context));
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// TODO Auto-generated method stub
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);
			child.measure(r - l, b - t);
			child.layout(0, 0, 320, 480);
		}
	}

}

import android.app.Activity;
import android.os.Bundle;

public class MoveViewGroup extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(new Workspace(this));

	}
}
分享到:
评论
4 楼 kaixinyou 2012-06-11  
haizige_2009 写道
哥们,我想问下你哈,如果是要动态更新GridView ,该怎么做呀?
你这种实现好象是一次性加载吧,
项目中不可能一次性加载所有的GridView吧,GridView里面的图片加载是很耗时间性能的。

兄弟想错了,这个就是一次性全部加载,然后各个页面之间再切换,可以参照launcher源码实现
3 楼 huangzclovetingzi 2012-03-02  
多谢楼主,学习了
2 楼 zhfally520 2011-10-08  

    [*]

    [*]
[img][/img]
[
[/align][/size][size=large]align=center]
1 楼 haizige_2009 2011-08-20  
哥们,我想问下你哈,如果是要动态更新GridView ,该怎么做呀?
你这种实现好象是一次性加载吧,
项目中不可能一次性加载所有的GridView吧,GridView里面的图片加载是很耗时间性能的。

相关推荐

    自定义ViewGroup实现拖动跟快速滚动的效果实例

    我们可以通过计算速度来实现快速滚动效果。 3. **在MoveViewGroup中集成手势检测**: 创建MoveViewGroup类,继承自ViewGroup,并在构造函数中初始化`GestureDetector`。在`onTouchEvent(MotionEvent event)`方法中...

    自定义ViewGroup仿QQ侧拉删除、侧拉抽屉;下拉刷新、上拉加载动画

    在Android开发中,自定义ViewGroup是提升用户体验和实现独特交互效果的重要手段。本文将深入讲解如何使用自定义ViewGroup来实现仿QQ的侧拉删除功能、侧拉抽屉(DrawerLayout)以及下拉刷新和上拉加载动画。首先,...

    自定义ViewGrounp——实现可拖动浮标广告

    总结起来,实现“自定义ViewGrounp——实现可拖动浮标广告”的核心在于自定义ViewGroup,通过处理触摸事件来实现拖动功能,并利用Scroller实现平滑滚动的效果。这个过程涉及到Android的视图层次、触摸事件机制以及...

    一个通过继承ViewGroup,应用Scroller实现的手动拖拽滚屏及自动滚屏的自定义UI控件

    1. **创建自定义ViewGroup**:首先,你需要创建一个新的Java类,继承自ViewGroup。在这个类中,你需要重写一些关键方法,如`onTouchEvent()`来处理触摸事件,`onLayout()`来确定子View的位置,以及`onDraw()`来绘制...

    自定义ScrollView实现弹簧效果

    这个自定义的ScrollView实现了弹簧效果,使得当用户在顶部或底部拖动超出范围时,ScrollView会像弹簧一样回弹,增加了一种动态的视觉反馈。 首先,我们来看一下如何自定义ScrollView。在Android中,自定义控件通常...

    自定义,实现下拉刷新、上拉加载功能

    1. 创建自定义ViewGroup:首先,我们需要创建一个新的ViewGroup,继承自LinearLayout或者其他的布局类,作为下拉刷新的基础组件。这个组件需要包含一个ListView,并处理触摸事件,检测用户的下拉手势。 2. 执行刷新...

    Android 仿UC,墨迹天气左右拖动多屏幕显示效果源码

    自定义ViewGroup需要重写`onLayout()`方法来确定子视图的位置,以及`onTouchEvent()`来处理触摸事件并实现滑动效果。 4. **动画效果**: 为了实现平滑的过渡,源码可能会包含自定义的动画效果,这可能是通过`...

    android拖拽效果仿zaker 优化性能版本

    3. **自定义ViewGroup**:为了实现自动移位的效果,可能需要自定义一个ViewGroup,重写onInterceptTouchEvent和onTouchEvent方法,以及布局的测量和布局过程。通过这些方法,我们可以精确控制子View的行为。 4. **...

    滚动条代码

    2. Android:在Android应用开发中,可以通过自定义ViewGroup或者使用第三方库如Android ScrollBar Library来自定义滚动条。 3. iOS:在iOS开发中,Swift和Objective-C提供了UIscrollView类,可以设置滚动条的显示和...

    安卓自定义控件相关-android实现滑动控制速度控制各种布局自定义组件.rar

    自定义滑动控制通常涉及到手势识别、滚动逻辑和动画效果的实现。开发者可能需要重写onTouchEvent()方法来处理触摸事件,通过计算手指移动的距离来驱动内容的滚动。此外,滑动速度的控制可以通过记录手指移动的速度并...

    自定义表格_自定义表格_android_TableView_

    在Android开发中,自定义表格(TableView)是一个常见的需求,特别是在展示数据时,为了实现更加灵活和个性化的显示效果。本实例将深入探讨如何在Android中实现自定义表格功能,结合"自定义表格_自定义表格_android_...

    Android 仿 窗帘效果 和 登录界面拖动效果

    在Android中,这种效果可以通过自定义ViewGroup和重写onDraw()以及onLayout()方法来实现。关键在于计算每个子View的位置和大小,使得它们按照预期的轨迹移动。同时,Scroller类可以帮助我们平滑地控制动画的滚动过程...

    android实现uc和墨迹天气那样的左右拖动效果

    综上所述,实现"android实现uc和墨迹天气那样的左右拖动效果"主要涉及Android的手势检测、`ViewPager`使用、自定义`ViewGroup`、滑动动画、性能优化等多个方面。通过深入理解和实践这些知识点,开发者可以构建出具有...

    android 用户自定义日期控件 可拖动

    这种自定义控件的实现往往涉及到Android的View或者ViewGroup的子类,通过重写onTouchEvent()方法来处理用户的触摸事件,根据滑动方向和距离来更新日期或时间。 在压缩包文件"DateSlider 1.1"中,我们可以期待找到...

    Android开发各种各种自定义TextView

    弹幕效果通常通过自定义ViewGroup,如HorizontalScrollView或LinearLayout,将TextView作为子View并控制其滚动来实现。关键在于计算滚动速度和时间,以及适时移除已滚动出屏幕的TextView实例。此外,可以使用Handler...

    Android-ImageNice9Layout仿Nice首页图片列表9图样式并实现拖拽效果

    ImageNice9Layout需要自定义一个ViewGroup,以实现特定的布局和交互效果。开发者需要了解如何创建自定义View类,继承自ViewGroup,并重写onMeasure()、onLayout()和onTouchEvent()等方法来控制布局和触摸事件处理。...

    RecyclerView实现拖拽排序侧滑删除效果

    本教程将深入讲解如何在RecyclerView中实现拖拽排序和侧滑删除的效果,这在许多应用中都非常常见,例如邮件应用、任务管理器等。 首先,我们需要创建一个RecyclerView并配置其适配器(Adapter)。适配器是连接数据...

    一种代替SurfaceView的自定义控件,可以缩放、无限延伸拖动

    为了实现平滑的滚动效果,可能还需要使用Scroller类来辅助动画过渡。 此外,自定义控件可能还会涉及Canvas的使用。在onDraw()方法中,开发者需要利用Canvas进行绘制,这包括将内容绘制到画布上,以及根据当前的缩放...

    Android学习记录使用Gallery实现炫丽的拖动效果

    在Android开发中,实现炫丽的拖动效果是提升用户体验的重要一环,特别是使用`Gallery`组件可以创建出类似轮播图或者选择器的效果。这篇博客"Android学习记录使用Gallery实现炫丽的拖动效果"深入探讨了如何利用`...

    ScrollLayout学习仿launcher的实现

    本项目“ScrollLayout学习仿launcher的实现”显然旨在教你如何创建一个类似手机启动器(Launcher)的滚动效果,这涉及到自定义ViewGroup、触摸事件处理、布局管理以及性能优化等多个知识点。 首先,我们需要理解`...

Global site tag (gtag.js) - Google Analytics