在android应用程序的开发过程中,相信我们很多人都想把应用的交互做的比较绚丽,比如让界面切换平滑的滚动,还有热度灰常高的伪3D等界面效果,通常情况下,系统提供的应用在特效这方面只能为我们提供简单的动画接口,所以要想实现比较酷炫的效果还是要自己去开发布局控件(即所谓的自定义View、ViewGroup)。
下面就自定义控件开发做一些简单的介绍,其实那个地方原本可以用ScrollView解决很大一部分问题的,但有一些效果确实需要对控件进行重新定义,在继承ScrollView开发中仍然会遇到一些ScrollView自身的限制,所以就仿照ScrollView自己做了一个控件。在其中遇到了一些问题自然就是像ScrollView中拖动的效果(比如快速拖动在手指离开屏幕时控件依旧会由于惯性继续滑动一段距离后才会停止运动),所以就对这个东东做了一下仔细的研究,虽然以前也做过类似的开发,这次由于时间比较充裕,所以将开发中遇到的一些问题都一一记录了下来。下面开始正题:
自定义布局控件自然是要继承某个View或ViewGroup
由于是根据项目的开发来写的这篇博客,所以我就以自定义布局控件(ViewGroup)来做介绍了。
开发一个自定义的ViewGroup自然是要继承ViewGroup类了,在继承这个类之后必须要重写的方法就是
onLayout(boolean changed, int l, int t, int r, int b)
另外至少要有一个构造方法,我个人习惯重写那个有两个参数的构造方法(XXX(Context context, AttributeSet attrs)),因为有了这个构造方法就可以在xml布局文件里使用这个类了,如果想要对这个布局控件以及其子控件的尺寸进行精确的控制那就要重写下面这个方法了
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
这个方法从字面理解就是估算控件的尺寸大小了.
下面开始介绍关于如何让自定义的控件进行平滑的移动,并能够根据手势的情况产生惯性滑动的效果
先介绍一下开发这种滑动效果需要用到的各种工具类:
android.view.VelocityTracker
android.view.Scroller
android.view.ViewConfiguration
VelocityTracker从字面意思理解那就是速度追踪器了,在滑动效果的开发中通常都是要使用该类计算出当前手势的初始速度(不知道我这么理解是否正确,对应的方法是velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity))并通过getXVelocity或getYVelocity方法得到对应的速度值initialVelocity,并将获得的速度值传递给Scroller类的fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) 方法进行控件滚动时各种位置坐标数值的计算,API中对fling 方法的解释是基于一个fling手势开始滑动动作,滑动的距离将由所获得的初始速度initialVelocity来决定。
关于ViewConfiguration 的使用主要使用了该类的下面三个方法:
configuration.getScaledTouchSlop() //获得能够进行手势滑动的距离
configuration.getScaledMinimumFlingVelocity()//获得允许执行一个fling手势动作的最小速度值
configuration.getScaledMaximumFlingVelocity()//获得允许执行一个fling手势动作的最大速度值
需要重写的方法至少要包含下面几个方法:
onTouchEvent(MotionEvent event)//有手势操作必然少不了这个方法了
computeScroll()//必要时由父控件调用请求或通知其一个子节点需要更新它的mScrollX和mScrollY的值。典型的例子就是在一个子节点正在使用Scroller进行滑动动画时将会被执行。所以,从该方法的注释来看,继承这个方法的话一般都会有Scroller对象出现。
在往下就是介绍比较具体的开发思路
首先我们要初始化一些变量,其中的多数代码已经在上面做出介绍了
void init(Context context) { mScroller = new Scroller(getContext()); setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); }
public void fling(int velocityY) { if (getChildCount() > 0) { mScroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0, maxScrollEdge); final boolean movingDown = velocityY > 0; awakenScrollBars(mScroller.getDuration()); invalidate(); } }
private void obtainVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } private void releaseVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } }
public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) { return false; } obtainVelocityTracker(event); final int action = event.getAction(); final float x = event.getX(); final float y = event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: LogUtil.log(TAG, "ACTION_DOWN#currentScrollY:" + getScrollY() + ", mLastMotionY:" + mLastMotionY, LogUtil.LOG_E); if (!mScroller.isFinished()) { mScroller.abortAnimation(); } mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: final int deltaY = (int) (mLastMotionY - y); mLastMotionY = y; if (deltaY < 0) { if (getScrollY() > 0) { scrollBy(0, deltaY); } } else if (deltaY > 0) { mIsInEdge = getScrollY() <= childTotalHeight - height; if (mIsInEdge) { scrollBy(0, deltaY); } } break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) velocityTracker.getYVelocity(); if ((Math.abs(initialVelocity) > mMinimumVelocity) && getChildCount() > 0) { fling(-initialVelocity); } releaseVelocityTracker(); break; } return true; }
public void computeScroll() { if (mScroller.computeScrollOffset()) { int scrollX = getScrollX(); int scrollY = getScrollY(); int oldX = scrollX; int oldY = scrollY; int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); scrollX = x; scrollY = y; scrollY = scrollY + 10; scrollTo(scrollX, scrollY); postInvalidate(); } }
public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { invalidate(); } } }
相关推荐
在Android中,我们可以使用FlowLayout或者自定义布局来实现这种效果。自定义布局通常会重写onMeasure()和onLayout()方法,以根据子视图的数量和大小动态调整它们的排列。流式布局特别适用于展示多个不固定大小的元素...
本教程以"android自定义View滑块移动"为例,深入探讨如何实现一个可以在屏幕上自由移动的滑块,并理解Android中View的绘制流程。 首先,我们要明白在Android中创建自定义View的基本步骤。这通常包括以下几个部分: ...
"Android自定义View-圆环布局"是一个专为实现特定视觉效果和交互设计的项目,它包含了圆圈旋转、圆环拖动以及图标拖动和图片缩放等多种功能。 首先,我们来深入理解圆环布局。圆环布局是一种非标准的布局方式,它将...
- 在ACTION_DOWN事件中记录初始触点坐标,ACTION_MOVE事件中计算移动距离,并更新TextView的位置。 - 使用`LayoutParams`来调整TextView的坐标,调用`requestLayout()`使改变生效。 2. **缩放功能**: - 缩放...
本文将深入探讨如何创建一个美观的自定义拖动条控件,即"Android自定义控件之拖动条"。我们将讨论以下几个关键知识点: 1. **基础知识**:在Android中,基本的滑动条控件是`SeekBar`,它允许用户通过拖动滑块来选择...
在Android开发中,自定义布局是一项重要的技能,它允许开发者根据特定需求定制界面。本篇文章将探讨如何通过自定义布局实现一个仿QQ的侧滑效果。这个效果常见于许多应用中,通常用于显示侧边栏菜单,用户可以通过...
当用户试图将滑块滑至两端时,阻止它直接到达,而是让其在0%-100%之间平滑移动。 6. **属性定制**: 为了让自定义的CircleSeekBar更具可配置性,我们可以添加一些自定义属性,如圆的半径、滑块的颜色、轨道的颜色等...
在Android开发中,自定义布局是一项重要的技能,它允许开发者根据特定需求创建独特的用户界面。本教程将聚焦于如何继承`ViewGroup`来构建自定义布局,并实现拖动效果以及增删元素时的动画操作。 首先,让我们了解`...
4. **动画效果**:为了模仿iOS开关滑动的平滑过渡,可以使用`ObjectAnimator`或者自定义动画来实现滑块移动的过程,使切换过程更具有视觉吸引力。 5. **属性设置**:提供一些自定义属性,如滑块颜色、轨道颜色、...
在Android开发中,自定义ViewGroup是实现复杂布局和动画效果的重要手段。本文将深入探讨如何利用自定义ViewGroup来实现一个独特的“竖直跑马灯”效果,这种效果常见于各种信息展示或广告轮播场景,使得内容能沿着...
总的来说,Android自定义控件的下拉刷新涉及到以下几个关键步骤: 1. 创建自定义布局,包含刷新指示器(如箭头和进度条)。 2. 在自定义视图中处理触摸事件,判断下拉手势并更新视图状态。 3. 触发刷新逻辑,加载新...
在Android开发中,自定义控件是提升应用用户体验和界面独特性的重要手段。GridView作为一种常见的布局控件,通常用于展示网格状的数据,如图片、列表项等。在本主题中,我们将深入探讨如何创建两个可相互拖拉的...
在Android开发中,自定义...总的来说,实现Android自定义歌词显示涉及文件解析、视图绘制、时间同步等多个技术点。通过深入理解这些知识点,我们可以构建出功能完备、用户体验良好的歌词显示功能,提升音乐应用的品质。
2. **Title背景移位效果组件**:用于创建标题栏,实现标题背景的平滑移动,包含titleLayout(标题栏布局)、bgImage(背景图片)、bgLeftMargin(背景初始左边距)和animTime(移动动画时间)属性。 3. **ViewPager...
最后,作者可能分享了如何在布局文件中添加自定义View,并在运行时根据需要调整参数,如改变扫描速度、范围等。这涉及到XML属性的声明和Java代码中的解析。 总的来说,自定义View—仿雷达扫描效果是一个结合了图形...
本教程将围绕“Android自定义`Switch`”这一主题,深入讲解如何在Android 4.0及以上版本中创建并优化自定义`Switch`。 首先,我们来看`Switch`的基本结构。一个自定义`Switch`通常由两部分组成:滑动条(Track)和...
首先,我们需要了解Android自定义控件的基本流程。自定义控件通常涉及以下步骤: 1. 创建一个新的Java类,继承自Android的View或ViewGroup类。在这个例子中,我们可以选择继承自CompoundButton,因为Toggle Switch...
在Android开发中,自定义控件是提升应用用户体验和界面独特性的关键。滑动开关(Slide Switch)是一种常见的UI元素,常用于开启或关闭某个功能。这篇博客文章的配套源码提供了一个自定义滑动开关的实现,让我们深入...
"Android-Android自定义控件之局部图片放大镜--BiggerView"这个项目,专注于为用户提供一种可以局部放大图片的功能,类似于我们在电商平台浏览商品详情时常见的图片放大镜效果。这种功能能够帮助用户更清晰地查看...