前面一篇博文《关于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应用上的demo 而且结合了自定义ViewGroup 很高的学习价值以及收藏价值
在Android开发中,图片...通过研究这个3D图片轮播器Demo,开发者不仅可以学习到如何创建一个高级的图片滚动控件,还能深入理解Android图形绘制和动画实现的底层机制,对提升Android应用的用户体验有着重要的实践意义。
首先,从标题“Android自定义滚动式时间选择器(在他人基础上修改)Demo”可以看出,这个项目是一个示例程序,它是在其他开发者的工作基础上进行改进的。通常,这种改进可能包括优化性能、增强功能、调整UI设计或者...
本文将深入探讨如何创建一个3D效果的高级图片滚动控件,基于给定的"Android高级图片滚动控件,3D版的图片轮播器Demo",我们将分析其核心知识点。 首先,我们要理解3D图片轮播器的核心特性。在2D平面上实现3D效果...
在提供的`ViewPagerDemo`中,开发者可能已经实现了上述的自定义`ViewPager`及其同步滚动条的代码示例。通过查看和学习这个示例代码,你可以更直观地了解这些概念是如何在实践中应用的。记得分析代码中的关键部分,如...
2. GestureDetector或Scroller类:利用手势检测器识别用户滑动行为,通过Scroller类控制平滑滚动。 3. 动画框架:如Android的Animation或Animator类,用于实现3D翻转效果。 4. 滑动速度计算:通过MotionEvent的...
9. **ViewGroup的嵌套滚动**:在处理包含多个可滚动组件的布局时,如NestedScrollView内嵌RecyclerView,Android提供了NestedScrolling机制,允许子View和父View协调滚动行为。 10. **自定义事件**:除了系统提供的...
3. **动画与平滑滚动**:为了让抽屉的打开和关闭更加流畅,可以使用ObjectAnimator或ValueAnimator来实现动画效果。通过改变视图的translationY或translationX属性,可以实现抽屉的平滑移动。 4. **部分数据可见**...
它可能使用了Scroller类来处理平滑滚动效果,并且可能重写了onMeasure()和onLayout()方法来适应水平布局。 2. **HorizontalListAdapter** 类:这是自定义的Adapter,它需要处理数据集合,并将每个数据项转化为水平...
在Android开发领域,API Demo Study是一个非常有价值的实践项目,它涵盖了多个关键知识点,包括Transition3D效果、自定义View和自定义ViewGroup的实现,以及如何利用HorizontalScrollView实现水平滚动的功能。...
QQ空间的菜单动画通常包括滑动、淡入淡出、缩放等效果,这些效果可以通过自定义ViewGroup、动画库(如Android的Animation或Animator API)以及手势识别来实现。在这个Demo中,开发者可能已经展示了如何创建一个可...
1. **UI设计**:此Demo的核心在于模拟淘宝应用的界面设计,这可能涉及到自定义ViewGroup和自定义控件的创建,以达到与淘宝应用相似的视觉效果。这包括商品列表的展示、商品详情页的设计、购物车功能的实现等。 2. *...
在本项目中,开发者可能自定义了一个继承自PagerAdapter的类,重写了`instantiateItem(ViewGroup container, int position)`和`destroyItem(ViewGroup container, int position, Object object)`方法,分别用于创建...
为了实现滚动效果,开发者可能会利用`Scroller`类来平滑滚动。`Scroller`提供了一种方式来模拟物理世界的弹性效果,使得滚动更加自然流畅。当用户停止触摸屏幕时,`Scroller`会计算剩余的滚动距离并自动执行动画。 ...
通过阅读和分析这个Demo,我们可以学习如何在Android中创建自定义控件,如何处理触摸事件,以及如何动态更新UI内容。 在实现自定义日期滑动选择框时,以下是一些关键步骤: 1. 创建自定义View:定义一个新的View类...
6. **自定义悬浮效果**:如果你需要更复杂的悬浮效果,比如跟随滑动的速度变化,可以使用`addOnScrollListener`的`onScrollStateChanged`方法,结合`scroller`类来计算平滑滚动速度。 通过以上步骤,你就能实现一个...
这通常通过自定义ViewGroup(如HorizontalScrollView或ViewPager)来完成。开发者需要重写测量、布局以及绘制方法,确保子视图能够正确地按照旋转木马的样式排列和显示。在测量阶段,要计算出每个子视图的大小,而在...
本资源是一个几年前的Android应用源码Demo,旨在帮助学生进行毕业设计学习。通过分析这个Demo,我们可以深入理解并掌握ViewFlipper在水平滑动中的应用。 首先,`ViewFlipper`是`ViewGroup`的一个子类,它可以包含多...
Scroller组件在Android开发中扮演着重要角色,它主要用于实现平滑滚动效果,尤其是在ViewGroup如ScrollView、HorizontalScrollView等中。Scroller并非直接控制View的滚动,而是提供了一个计算滚动过程的缓动函数,...
7. **自定义View**:为了实现这些功能,通常需要创建一个自定义的`View`或`ViewGroup`,在这个自定义组件中处理所有翻页逻辑和渲染。 总的来说,“Android的书籍翻页动画代码例子”应该包含以上所述的各种技术细节...