以下是ScrollView的滑动效果代码,其中修复了一个事件拦截的BUG. public class MyScrollView extends ViewGroup { private PageChangeListener pageChangeListener; public PageChangeListener getPageChangeListener() { return pageChangeListener; } public void setPageChangeListener(PageChangeListener pageChangeListener) { this.pageChangeListener = pageChangeListener; } /** * 页面改变的监听者,当页面改变的时候就会回调 * * @author afu * */ public interface PageChangeListener{ /** * 页面改变的时候回调,回传当前页面的下标 * @param currIndex 下标值 */ public void moveTo(int currIndex); } /** * 测量当前View * 1.如果当前view是ViewGroup,就义务测量它的孩子,孩子在测量它的孩子。形成树状测量 * 2.测量过程,测量父类,测量孩子; * 3.测量不只一次,测量多次; * 4.widthMeasureSpec很大,包含了父类给这个控件的宽和父类给的模式 * 5.模式:1.未指定;2.指定,在一定的位置内;3.至多 * * onMeasure测量过程 * 1.父类给当前View的widthMeasureSpec保护宽和父类给的 模式,根据这两个只就可以得到width和模式 * 2.给孩子的宽的计算方式,宽减掉padding的值,得到孩子该有的空间 * 3.根据width 和父类的模式MeasureSpec, 得到新的newWith如下: * MeasureSpec.makeMeasureSpec * 4.孩子再去测量还在。 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //父类给这是控件的宽 int width = MeasureSpec.getSize(widthMeasureSpec); //这个是父类给孩子的模式 int Mode = MeasureSpec.getMode(widthMeasureSpec); //得到宽,父类给的宽减掉padding值,得到该给孩子多少空间 // getWidth(); //根据当前View的width和模式,得到该给孩子多少宽度 int newWidth = MeasureSpec.makeMeasureSpec(width, Mode); // System.out.println(width+" : "+Mode); //把孩子遍历出来测量-才能得到父类给孩子的大小-当前View该显示多宽 for(int i=0;i<getChildCount();i++){ View view = getChildAt(i); //父类有多高就给多高,还包括宽 view.measure(widthMeasureSpec, heightMeasureSpec); } } // View加载到显示的过程 // 1.构造方法执行 // 2.测量-onMeaseure // 3.指定View的位置和大小onLayout // 4.绘制View,onDraw /** * 指定View的位置和大小 如果这个View是ViewGroup的孩子,它将有义务和责任指定它孩子的位置和大小 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 指定每个孩子的位置和大小 // 遍历 for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // 设置高和宽 child.layout(0 + i * getWidth(), 0, getWidth() + i * getWidth(), getHeight()); } } /** * 事件的分发 * 一般是不实现这个方法 */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } // 手势识别器的使用 // 1.定义手势识别器 // 2.实例化 // 3.使用手势识别器onTouchEnvent(); private GestureDetector detector; private Scroller scroller; private void initView(Context context){ scroller = new Scroller(context); detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){ //手指触摸滚动 /** * distanceX:水平方向要移动的距离 * distanceY:竖直方向要移动的距离 */ @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { /** * 根据距离移动到指定的位置 * x :在X轴上要移动的距离 * Y :在Y轴上要移动的距离 */ scrollBy((int)distanceX, 0); /** * 根据坐标移动到指定的位置 * x:在x轴要移动的坐标 * y:在y轴要移动的坐标 */ // scrollTo(x, y); return true; } /** * 滑动效果 */ @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO Auto-generated method stub return super.onFling(e1, e2, velocityX, velocityY); } }); } /** * 第一次按下的坐标 */ private float startX; /** * 代表当前在那个页面,下标 */ private int currIndex; /** * 第一按下的X轴坐标 */ private float downX; /** * 第一次按下的Y轴坐标 */ private float downY; /** * 拦截onTouchEvent事件 * 但返回true,就触发当前View的onTouchEvent事件,当前View也就是myScrollView的左右滚动就起效果了 * 如果返回flase,就把这个事件传给当前View(myScrollView)的孩子,孩子得到这个事件后就可以,触发孩子的onTouchEvent事件 * 这个方法默认是传递给孩子 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean result = false; // return super.onInterceptTouchEvent(ev); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN://按下 detector.onTouchEvent(ev); System.out.println("onInterceptTouchEvent"+": ACTION_DOWN "+result); //1.记录第一次按下的坐标 downX = ev.getX(); downY = ev.getY(); break; case MotionEvent.ACTION_MOVE://移动 System.out.println("onInterceptTouchEvent"+": ACTION_MOVE "+result); //2.来到新的坐标 float newX = ev.getX(); float newY = ev.getY(); //3.计算偏移量-计算距离-在水平方向和竖直方向的距离 float dX = Math.abs(newX - downX); float dY = Math.abs(newY - downY); //水平方向滑动大于竖直方向 if(dX > dY){ result = true; } break; case MotionEvent.ACTION_UP://离开 System.out.println("onInterceptTouchEvent"+": ACTION_UP "+result); break; default: break; } System.out.println("onInterceptTouchEvent=="+ result); return result; } @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event);//执行父类的方法 System.out.println("onTouchEvent"); detector.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN://按下 System.out.println("onTouchEvent"+": ACTION_DOWN "); //1.按下坐标记录 startX = event.getX(); break; case MotionEvent.ACTION_MOVE://移动 System.out.println("onTouchEvent"+": ACTION_MOVE "); break; case MotionEvent.ACTION_UP://离开 System.out.println("onTouchEvent"+": ACTION_UP "); //2.记录新的点的坐标 float endX = event.getX(); //3.计算偏移量 // float dX = endX - startX; int temIndex = currIndex; if((endX - startX)>getWidth()/2){ //显示上一个子页面 temIndex--; }else if((startX - endX)>getWidth()/2){ //显示下一个子页面 temIndex++; } //移动到那个要么 moveTo(temIndex); break; default: break; } return true; } /** * 根据下标位置移动到对应的页面和屏蔽非法值 * @param temIndex 当前页面的下标 */ public void moveTo(int temIndex) { if(temIndex < 0){ temIndex = 0; } if(temIndex > getChildCount()-1){ temIndex = getChildCount()-1; } //新的当前坐标 currIndex = temIndex; if(pageChangeListener!= null){ pageChangeListener.moveTo(currIndex); } //移动的是坐标currIndex*getWidth():就是要移动到的X轴的坐标 //距离=末尾-起始 int distanceX = currIndex*getWidth() - getScrollX(); // scroller.startScroll(getScrollX(), 0, distanceX, 0); scroller.startScroll(getScrollX(), 0, distanceX, 0, Math.abs(distanceX)); // scrollTo(currIndex*getWidth(), 0); //刷新View会导致onDraw方法执行 computeScroll(); invalidate(); } @Override public void computeScroll() { // super.computeScroll(); if(scroller.computeScrollOffset()){ float X = scroller.getCurrX(); //根据距离去移动 // scrollBy((int)distanceX, 0); //根据坐标去移动 scrollTo((int)X, 0); invalidate(); } } public MyScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context); } // 在布局文件中使用,实例化时候用到它 public MyScrollView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } // 代码中用到 public MyScrollView(Context context) { super(context); initView(context); } }
同时,自定义了MyScroller,实现了页面跳转时的缓慢移动效果。
public class MyScroller { public MyScroller(Context context){ } /** * 开始滑动的X轴的起始坐标 */ private float startX; /** * 开始滑动的Y轴的起始坐标 */ private float startY; /** * 在X轴的移动的距离 */ private float distanceX; /** * 在Y轴的移动的距离 */ private float distanceY; /** * 开始移动一小段的起始时间 */ private long startTime; /** * 是否移动完成 * true已经移动完成了 * false没有已经完成 */ private boolean isFinish; /** * 人为指定总共距离的总共时间为500毫秒 */ private long totalTime = 500; /** * 移动到那个坐标 */ private float currX; /** * 开始滚动或者开始滚动了 * 还要计算起始时间和是否已经移动完成了 * @param startX * @param startY * @param distanceX * @param distanceY */ public void startScroll(float startX,float startY,float distanceX,float distanceY){ this.startX = startX; this.startY = startY; this.distanceX = distanceX; this.distanceY = distanceY; //记录开始时间 this.startTime =SystemClock.uptimeMillis(); this.isFinish = false; } /** * 计算一小段一小段的距离后的坐标和这一小段花的时间 * @return * true正在移动 * false已经移动结束 */ public boolean computeScrollOffset(){ if(isFinish){ return false; } //这一小段结束的时间 long endTime = SystemClock.uptimeMillis(); //这一小段花的时间 long passTime = endTime - startTime; if(passTime < totalTime){ //还在移动 //计算移动的速度(平均速度) = 距离/时间 // float volecityX = distanceX / totalTime; this.currX = startX + passTime * distanceX / totalTime; }else{ //不一定了 this.currX = startX + distanceX; isFinish = true; } return true; } /** * 得到要移动这一小段的距离 * @return */ public float getCurrX() { return currX; } public void setCurrX(float currX) { this.currX = currX; } }
在activity中,通过RadioButton来控制页面的变化
/** * 图片资源ID */ private int[] ids = { R.drawable.a1, R.drawable.a2, R.drawable.a3, R.drawable.a4, R.drawable.a5, R.drawable.a6 }; private MyScrollView msv; private RadioGroup rg_point_group; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); msv = (MyScrollView) findViewById(R.id.msv); rg_point_group = (RadioGroup) findViewById(R.id.rg_point_group); //添加子者六个页面 for(int i=0;i<ids.length;i++){ ImageView imageView = new ImageView(this); imageView.setBackgroundResource(ids[i]); msv.addView(imageView); } //添加测试页面 View test = View.inflate(this, R.layout.test, null); msv.addView(test, 2); //有多少个页面就添加多少个指示点 for(int i=0;i<msv.getChildCount();i++){ RadioButton rb = new RadioButton(this); rb.setId(i); if(i ==0){ rb.setChecked(true); }else{ rb.setChecked(false); } rg_point_group.addView(rb); } //设置页面改变的监听 msv.setPageChangeListener(new PageChangeListener() { //当前的ID和下标值是一样 @Override public void moveTo(int currIndex) { //把全部设置为默认的 // for(int i =0;i<rg_point_group.getChildCount();i++){ // RadioButton radioButton = (RadioButton) rg_point_group.getChildAt(i); // radioButton.setChecked(false); // } // //把当前RadioButton设置为选中状态 // RadioButton radioButton = (RadioButton) rg_point_group.getChildAt(currIndex); // radioButton.setChecked(true); rg_point_group.check(currIndex); } }); //设置RadioGroup的状态,监听选中那个孩子 rg_point_group.setOnCheckedChangeListener(new OnCheckedChangeListener() { //id和指示点的下标一样 @Override public void onCheckedChanged(RadioGroup group, int checkedId) { //移动到指定的页面 msv.moveTo(checkedId); } }); }
相关推荐
为了实现如同iOS那样具有真实感的滚动回弹效果,开发者通常需要自定义ScrollView。下面将详细介绍如何在Android中实现自定义ScrollView的反弹效果。 首先,我们需要创建一个新的ScrollView子类,例如命名为`...
要实现回弹效果,我们需要创建一个新的自定义ScrollView类,继承自Android的ScrollView,并重写其滚动相关的函数。关键在于计算当前滑动的位置,判断是否超过边界,然后模拟出回弹的动画效果。以下是一些核心步骤: ...
本话题主要探讨如何自定义ScrollView以实现阻尼效果和控件悬浮这两个功能。 一、自定义ScrollView实现阻尼效果 阻尼效果通常是指在滚动过程中,滚动速度会随着手指离开屏幕而逐渐减慢,直到停止,这种模拟真实物理...
在Android开发中,自定义ScrollView和ListView是实现复杂布局和流畅滚动效果的重要手段。这两个组件在许多场景下都有着广泛的应用,比如长列表、嵌套滚动等。本篇将深入探讨如何根据具体需求来定制这两个组件。 ...
然而,系统默认的ScrollView并不直接提供滑动事件的监听接口,这就需要我们自定义ScrollView来实现这一功能。下面我们将详细探讨如何在Android中自定义ScrollView并添加滑动监听事件。 首先,我们要了解ScrollView...
自定义scrollview来实现滑动列表项。PS:使用时在xml中调用,只能有一个直接子组件
在Android开发中,自定义ScrollView是一项常见的需求,特别是在构建具有复杂滚动行为的应用时。"Android自定义ScrollView:实现滑动顶部停靠"这个主题聚焦于如何创建一个在用户上滑时能够固定在顶部的组件,这通常被...
自定义的collectionView 仿照scrollview的侧滑效果,每次只能滑动一张图片。
本示例探讨的主题是如何自定义ScrollView以实现类似淘宝应用中的Toolbar透明度渐变效果。这种效果通常被称为“沉浸式”或“半透明”状态栏,它在用户滚动页面时会改变Toolbar的透明度,从而增强视觉体验。 首先,...
首先,我们来看一下如何自定义ScrollView。在Android中,自定义控件通常涉及继承已有的View或ViewGroup类,然后重写其中的方法来实现特定的功能。对于ScrollView,我们需要继承`android.widget.ScrollView`,并重写...
自定义ScrollView和ListView可以实现更多的功能和效果,例如: 1. **添加滚动监听**:通过重写滚动事件处理,可以实现定制的滚动动画或通知其他组件滚动状态。 2. **自定义Item布局**:对于ListView,可以创建多种...
Android UI设计之<十三>自定义ScrollView,实现QQ空间阻尼下拉刷新和渐变菜单栏效果,详http://blog.csdn.net/llew2011/article/details/52626148
本篇文章将深入探讨如何实现自定义ScrollView的滑动间距,使得每次滑动的距离小于整个屏幕的宽度,以此来创建类似“日报推送”效果。 首先,我们需要了解UIScrollView的基本工作原理。UIScrollView内部包含一个...
本教程将详细介绍如何在Android中自定义ScrollView,以实现类似的效果。 首先,我们需要创建一个新的Java类,继承自Android的ScrollView。在这个自定义的ScrollView中,我们将重写onTouchEvent()方法来处理触摸事件...
本教程将重点讲解如何通过自定义ScrollView来实现图片的拉伸放大效果,并在用户上滑时动态修改标题栏的状态。这一功能常用于诸如新闻详情页或者电商商品详情页等场景,能够提升用户的浏览体验。 首先,我们需要创建...
仿招行手机银行APP8.1首页下拉效果...下拉震动出现二楼效果、上滑改变Title背景透明度效果,通过自定义ScrollView监听onTouchEvent下拉震动出现二楼效果,通过监听ScrollView滚动事件实现上滑改变Title背景透明度效果。
Android自定义ScrollView仿团购顶部标题,下拉过程中设置显示顶部布局,详细信息可以参考博客地址:http://blog.csdn.net/lr809174917/article/details/53728622
然而,原生的ScrollView并未提供直接的滑动监听事件,为了实现特定的交互效果,如标题栏背景颜色随滑动渐变,我们需要自定义ScrollView并添加相应的滑动监听。本教程将详细介绍如何实现这一功能。 首先,我们要理解...
自定义下拉ScrollView头部变大,动画缩回原型的