`
yanfaguanli
  • 浏览: 686130 次
文章分类
社区版块
存档分类
最新评论

Android学习小demo(3)自定义ViewGroup 利用 scroller 实行屏幕滚动

 
阅读更多

前面一篇博文《关于View的ScrollTo, getScrollX 和 getScrollY》,我们讲解View 的scrollTo() 和 getScrollX() 的功能,也提到了它们其实是一般是配合scroller 类来进行屏幕的滑动的。有的朋友可能会问,为什么有了scrollTo() 之后,还要有scroller 类呢,区别在于,scrollTo() 和 scrollBy() 他们实现的是一个结果,也就是说,当你调用scrollTo(100, 0) 的时候,再重新绘制的时候,内容就已经出现在(100,0)的位置上了,缺少一个过程,而scroller 类就是来帮助我们展现这个滚动的过程的。

动画的原理其实是不停地重绘位置变化的内容,在视觉效果上,就会产生动画的效果。scroller 类的原理其实也正是如此,通过循环地绘制不同位置上的内容,来展现屏幕滚动。

它的原理图大概如下:


这样讲就太抽象了,还是结合例子来看看吧。

拿上次写的自定义ViewGroup, 我们来进行扩展一下,代码如下:

public class CustomRotateViewGroup extends ViewGroup implements OnTouchListener{

	private static final int snapVelocity = 200;
	
	private static final int rows = 3;
	
	private static final int padding = 10;
	
	private Scroller mScroller;		
	
	private float downX;
	
	private float curX;	
	
	private int distanceX;
	
	private int screenWidth;	
	
	private VelocityTracker velocityTracker;
	
	private enum Direction{
		Current,
		Next		
	}
		
	public CustomRotateViewGroup(Context context) {
		super(context);
		initialize(context);
	}
	
	public CustomRotateViewGroup(Context context, AttributeSet attrs) {
		super(context, attrs);
		initialize(context);
	}
	
	//初始化操作
	public void initialize(Context context){
		DisplayMetrics displayMetrics = new DisplayMetrics();
		WindowManager windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
		windowManager.getDefaultDisplay().getMetrics(displayMetrics);
		screenWidth = displayMetrics.widthPixels;//获得屏幕宽度
		
		mScroller = new Scroller(context);//获取scroller实例
		setOnTouchListener(this);//设置触摸监听
		//添加图片
		addCustomRotateView(context, R.drawable.photo1, 60, Color.YELLOW);
		addCustomRotateView(context, R.drawable.photo2, 30, Color.CYAN);
		addCustomRotateView(context, R.drawable.photo3, -30, Color.MAGENTA);
		addCustomRotateView(context, R.drawable.photo4, 60, Color.CYAN);
		addCustomRotateView(context, R.drawable.photo5, 30, Color.MAGENTA);
		addCustomRotateView(context, R.drawable.photo6, -30, Color.LTGRAY);
		addCustomRotateView(context, R.drawable.photo3, 60, Color.YELLOW);
		addCustomRotateView(context, R.drawable.photo4, 30, Color.LTGRAY);
		addCustomRotateView(context, R.drawable.photo5, -30, Color.CYAN);
	}
	
	private void addCustomRotateView(Context context, int resId, float degree, int color){
		addView(new CustomRotateView(context, resId, degree, color));
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int len = getChildCount();
		int parentHeight = MeasureSpec.getSize(heightMeasureSpec);		
		parentHeight -= padding * rows;
		for(int i = 0; i < len; i++){
			View child = getChildAt(i);
			int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(parentHeight / rows, MeasureSpec.AT_MOST);
			measureChild(child, widthMeasureSpec, childHeightMeasureSpec);
		}
	}
        //布局函数,将图片从左向右排列,一列3个。
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int startX = 0;
		int startY = 0;		
		int len = getChildCount();
		boolean flagVertical = true;
		for(int i = 0; i <len; i++){
			View child = getChildAt(i);
			int childHeight = child.getMeasuredHeight();
			int childWidth = child.getMeasuredWidth();			
			child.layout(startX, startY, startX + childWidth, startY + childHeight);
			if(flagVertical){								
				if((i + 1) % rows == 0){					
					startX += childWidth;
					flagVertical = false;
					startY = 0;					
				}else{
					startY += childHeight + padding;
				}
			}else{												
				flagVertical = true;
				startY += childHeight + padding;				
			}			
		}
	}
	//重载View的computeScroll函数
	@Override
	public void computeScroll(){
		if(mScroller.computeScrollOffset()){//滚动是否结束,返回true表明滚动还未结束,同时在函数里获取currX和currY的值
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//滚动到当前位置
			postInvalidate();//强制重新绘制View,在框架里,View 的绘制会重新调用到computeScroll方法,达到循环绘制的目的
		}
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		//to add the event to the velocityTracker, in order to calculate the velocity of the slop
		if(velocityTracker == null){
			velocityTracker = VelocityTracker.obtain();
		}
		velocityTracker.addMovement(event);
		
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			//当停止动画的时候,它会马上滚动到终点,然后向动画设置为结束。 
			if(mScroller != null && !mScroller.isFinished()){
				mScroller.abortAnimation();				
			}
			downX = event.getX();
			curX = event.getX();			
			break;
		case MotionEvent.ACTION_MOVE:
			float moveX = event.getX();
			distanceX = (int)(moveX - curX);
			scrollBy(-distanceX, 0);			
			curX = moveX;
			break;
		case MotionEvent.ACTION_UP:			
			float upX = event.getX();
			distanceX = (int)(upX - downX);			
			velocityTracker.computeCurrentVelocity(1000);
			float vx = velocityTracker.getXVelocity();

			if (getScrollX() < 0) { // scroll to right (the first screen)
				mScroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0,1000);
			} else if (getScrollX() > screenWidth * (getChildCount() / 3 - 1)) { // scroll to left
				scrollToScreen(Direction.Current);
			} else {
				if (distanceX > 0) { // scroll to left
					if (distanceX > screenWidth / 2
							|| vx > snapVelocity) {
						scrollToScreen(Direction.Current);
					} else {
						scrollToScreen(Direction.Next);
					}
				} else if (distanceX < 0) {
					if (Math.abs(distanceX) > screenWidth / 2 || vx < -snapVelocity) {
						// scroll to right (the next screen)
						scrollToScreen(Direction.Next);
					} else {
						// scroll to left (the current screen)
						scrollToScreen(Direction.Current);
					}
				}
			}
			velocityTracker.recycle();
			invalidate();// 在这里一定要记得调用invalidate 来促使屏幕重绘,开始循环绘制的过程,不然。。。没有入口去滚。		
			break;
		}
		return true;
	}
	
	private void scrollToScreen(Direction to){
		if(to == Direction.Next){
			mScroller.startScroll(getScrollX(), 0, screenWidth - getScrollX() % screenWidth, 0, 1000);
		}else{
			mScroller.startScroll(getScrollX(), 0,  - getScrollX() % screenWidth, 0, 1000);
		}
	}

}

这个自定义的滚动ViewGroup 实现的主要功能如下:

1)首先我们添加了N(在这个例子中是9) 张自定义的图片放在这个ViewGroup里面

2)将这 N 张图片按照 3 个一列,从左向右排,为了易于展示和写代码,每一列都跟屏幕一样宽,这样在界面上的时候,就只看到第一列图片

3)当我们手指向左向右滑动的时候,屏幕会跟随着手指进行滑动

4)当我们手指滑过窗口的二分之一宽或者滑动的速度大于200的时候,手指松开,屏幕会自动滑向下一屏或者上一屏。

5)边界检测,当屏幕滑到最左屏最右屏的时候,再继续滑动,到达黑暗地域的时候,手指松开,会自动滚动回来。

下面是效果Gif:(之前滑动效果做反了,经同事提醒,才发现的确是有点不对劲,=_=!! ---修正于20140112)



最后附上源代码,请点击 源代码下载,希望各位指出需要完善的地方,一起进步。

分享到:
评论

相关推荐

    Scroller的应用

    这个是Scroller应用上的demo 而且结合了自定义ViewGroup 很高的学习价值以及收藏价值

    Android实现高级图片滚动控件,3D版的图片轮播器Demo_图片滚动展示.zip

    在Android开发中,图片...通过研究这个3D图片轮播器Demo,开发者不仅可以学习到如何创建一个高级的图片滚动控件,还能深入理解Android图形绘制和动画实现的底层机制,对提升Android应用的用户体验有着重要的实践意义。

    Android自定义滚动式时间选择器(在他人基础上修改)Demo

    首先,从标题“Android自定义滚动式时间选择器(在他人基础上修改)Demo”可以看出,这个项目是一个示例程序,它是在其他开发者的工作基础上进行改进的。通常,这种改进可能包括优化性能、增强功能、调整UI设计或者...

    Android高级图片滚动控件,3D版的图片轮播器Demo

    本文将深入探讨如何创建一个3D效果的高级图片滚动控件,基于给定的"Android高级图片滚动控件,3D版的图片轮播器Demo",我们将分析其核心知识点。 首先,我们要理解3D图片轮播器的核心特性。在2D平面上实现3D效果...

    自定义类型ViewPager组件及同步滚动条

    在提供的`ViewPagerDemo`中,开发者可能已经实现了上述的自定义`ViewPager`及其同步滚动条的代码示例。通过查看和学习这个示例代码,你可以更直观地了解这些概念是如何在实践中应用的。记得分析代码中的关键部分,如...

    galley3D效果+滑动敏感控制demo

    2. GestureDetector或Scroller类:利用手势检测器识别用户滑动行为,通过Scroller类控制平滑滚动。 3. 动画框架:如Android的Animation或Animator类,用于实现3D翻转效果。 4. 滑动速度计算:通过MotionEvent的...

    Android 事件处理机制 demo

    9. **ViewGroup的嵌套滚动**:在处理包含多个可滚动组件的布局时,如NestedScrollView内嵌RecyclerView,Android提供了NestedScrolling机制,允许子View和父View协调滚动行为。 10. **自定义事件**:除了系统提供的...

    android自定义slidingdrawer

    3. **动画与平滑滚动**:为了让抽屉的打开和关闭更加流畅,可以使用ObjectAnimator或ValueAnimator来实现动画效果。通过改变视图的translationY或translationX属性,可以实现抽屉的平滑移动。 4. **部分数据可见**...

    横向滑动ListView包括Demo

    它可能使用了Scroller类来处理平滑滚动效果,并且可能重写了onMeasure()和onLayout()方法来适应水平布局。 2. **HorizontalListAdapter** 类:这是自定义的Adapter,它需要处理数据集合,并将每个数据项转化为水平...

    ApiDemoStudy

    在Android开发领域,API Demo Study是一个非常有价值的实践项目,它涵盖了多个关键知识点,包括Transition3D效果、自定义View和自定义ViewGroup的实现,以及如何利用HorizontalScrollView实现水平滚动的功能。...

    Android 菜单动画(类似QQ空间)Demo-IT计算机-毕业设计.zip

    QQ空间的菜单动画通常包括滑动、淡入淡出、缩放等效果,这些效果可以通过自定义ViewGroup、动画库(如Android的Animation或Animator API)以及手势识别来实现。在这个Demo中,开发者可能已经展示了如何创建一个可...

    android仿淘宝购买商品,主页后缩Demo.zip

    1. **UI设计**:此Demo的核心在于模拟淘宝应用的界面设计,这可能涉及到自定义ViewGroup和自定义控件的创建,以达到与淘宝应用相似的视觉效果。这包括商品列表的展示、商品详情页的设计、购物车功能的实现等。 2. *...

    Android照片墙加强版,使用ViewPager实现画廊效果Demo

    在本项目中,开发者可能自定义了一个继承自PagerAdapter的类,重写了`instantiateItem(ViewGroup container, int position)`和`destroyItem(ViewGroup container, int position, Object object)`方法,分别用于创建...

    安卓Android源码——wheel时间控件.zip

    为了实现滚动效果,开发者可能会利用`Scroller`类来平滑滚动。`Scroller`提供了一种方式来模拟物理世界的弹性效果,使得滚动更加自然流畅。当用户停止触摸屏幕时,`Scroller`会计算剩余的滚动距离并自动执行动画。 ...

    android日期滑动选择框

    通过阅读和分析这个Demo,我们可以学习如何在Android中创建自定义控件,如何处理触摸事件,以及如何动态更新UI内容。 在实现自定义日期滑动选择框时,以下是一些关键步骤: 1. 创建自定义View:定义一个新的View类...

    实现带悬浮条的RecycleView Demo

    6. **自定义悬浮效果**:如果你需要更复杂的悬浮效果,比如跟随滑动的速度变化,可以使用`addOnScrollListener`的`onScrollStateChanged`方法,结合`scroller`类来计算平滑滚动速度。 通过以上步骤,你就能实现一个...

    android旋转木马滑动效果源码

    这通常通过自定义ViewGroup(如HorizontalScrollView或ViewPager)来完成。开发者需要重写测量、布局以及绘制方法,确保子视图能够正确地按照旋转木马的样式排列和显示。在测量阶段,要计算出每个子视图的大小,而在...

    Android ViewFlipper水平滑动-IT计算机-毕业设计.zip

    本资源是一个几年前的Android应用源码Demo,旨在帮助学生进行毕业设计学习。通过分析这个Demo,我们可以深入理解并掌握ViewFlipper在水平滑动中的应用。 首先,`ViewFlipper`是`ViewGroup`的一个子类,它可以包含多...

    ScrollerDemo:是一个Scroller的演示demo

    Scroller组件在Android开发中扮演着重要角色,它主要用于实现平滑滚动效果,尤其是在ViewGroup如ScrollView、HorizontalScrollView等中。Scroller并非直接控制View的滚动,而是提供了一个计算滚动过程的缓动函数,...

    Android的书籍翻页动画代码例子

    7. **自定义View**:为了实现这些功能,通常需要创建一个自定义的`View`或`ViewGroup`,在这个自定义组件中处理所有翻页逻辑和渲染。 总的来说,“Android的书籍翻页动画代码例子”应该包含以上所述的各种技术细节...

Global site tag (gtag.js) - Google Analytics