Android Scroller大揭秘

在学习使用Scroller之前,需要明白scrollTo()、scrollBy()方法。

一、View的scrollTo()、scrollBy()

scrollTo、scrollBy方法是View中的,因此任何的View都可以通过这两种方法进行移动。首先要明白的是,scrollTo、scrollBy滑动的是View中的内容(而且还是整体滑动),而不是View本身。我们的滑动控件如SrollView可以限定宽、高大小,以及在布局中的位置,但是滑动控件中的内容(或者里面的childView)可以是无限长、宽的,我们调用View的scrollTo、scrollBy方法,相当于是移动滑动控件中的画布Canvas,然后进行重绘,屏幕上也就显示相应的内容。如下:

1、getScrollX()、getScrollY()

在学习scrollTo()、scrollBy()之前,先来了解一下getScrollX()、getScrollY()方法。

getScrollX()、getScrollY()得到的是偏移量,是相对自己初始位置的滑动偏移距离,只有当有scroll事件发生时,这两个方法才能有值,否则getScrollX()、getScrollY()都是初始时的值0,而不管你这个滑动控件在哪里。所谓自己初始位置是指,控件在刚开始显示时、没有滑动前的位置。以getScrollX()为例,其源码如下:

 public final int getScrollX() {
 return mScrollX;
}

可以看到getScrollX()直接返回的就是mScrollX,代表水平方向上的偏移量,getScrollY()也类似。偏移量mScrollX的正、负代表着,滑动控件中的内容相对于初始位置在水平方向上偏移情况,mScrollX为正代表着当前内容相对于初始位置向左偏移了mScrollX的距离,mScrollX为负表示当前内容相对于初始位置向右偏移了mScrollX的距离。

这里的坐标系和我们平常的认知正好相反。为了以后更方便的处理滑动相关坐标和偏移,在处理偏移、滑动相关的功能时,我们就可以把坐标反过来看,如下图:

因为滑动控件中的内容是整体进行滑动的,同时也是相对于自己显示时的初始位置的偏移,对于View中内容在偏移时的参考坐标原点(注意是内容视图的坐标原点,不是图中说的滑动控件的原点),可以选择初始位置的某一个地方,因为滑动时整体行为,在进行滑动的时候从这个选择的原点出进行分析即可。

2、scrollTo()、scrollBy()

scrollTo(int x,int y)移动的是View中的内容,而滑动控件中的内容都是整体移动的,scrollTo(int x,int y)中的参数表示View中的内容要相对于内容初始位置移动x和y的距离,即将内容移动到距离内容初始位置x和y的位置。正如前面所说,在处理偏移、滑动问题时坐标系和平常认知的坐标系是相反的。以一个例子说明scrollTo():

说明:图中黄色矩形区域表示的是一个可滑动的View控件,绿色虚线矩形为滑动控件中的滑动内容。注意这里的坐标是相反的。(例子来源于:http://blog.csdn.net/bigconvience/article/details/26697645

(1)调用scrollTo(100,0)表示将View中的内容移动到距离内容初始显示位置的x=100,y=0的地方,效果如下图:

(2)调用scrollTo(0,100)效果如下图:

(3)调用scrollTo(100,100)效果如下图:

(4)调用scrollTo(-100,0)效果如下图:

通过上面几个图,可以清楚看到scrollTo的作用和滑动坐标系的关系。在实际使用中,我们一般是在onTouchEvent()方法中处理滑动事件,在MotionEvent.ACTION_MOVE时调用scrollTo(int x,int y)进行滑动,在调用scrollTo(int x,int y)前,我们先要计算出两个参数值,即水平和垂直方向需要滑动的距离,如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
 int y = (int) event.getY();
 int action = event.getAction();
 switch (action){
 case MotionEvent.ACTION_DOWN:
 mLastY = y;
 break;
 case MotionEvent.ACTION_MOVE:
 int dy = mLastY - y;//本次手势滑动了多大距离
 int oldScrollY = getScrollY();//先计算之前已经偏移了多少距离
 int scrollY = oldScrollY + dy;//本次需要偏移的距离=之前已经偏移的距离+本次手势滑动了多大距离
 if(scrollY < 0){
 scrollY = 0;
 }
 if(scrollY > getHeight() - mScreenHeight){
 scrollY = getHeight() - mScreenHeight;
 }
 scrollTo(getScrollX(),scrollY);
 mLastY = y;
 break;
 }
 return true;
} 

上面在计算参数时,分为了三步。第一是,通过int dy = mLastY - y;得到本次手势在屏幕上滑动了多少距离,这里要特别注意这个相减顺序,因为这里的坐标与平常是相反的,因此,手势滑动距离是按下时的坐标mLastY - 当前的坐标y;第二是,通过oldScrollY = getScrollY();获得滑动内容之前已经距初始位置便宜了多少;第三是,计算本次需要偏移的参数int scrollY = oldScrollY + dy; 后面通过两个if条件进行了边界处理,然后调用scrollTo进行滑动。调用完scrollTo后,新的偏移量又重新产生了。从scrollTo源码中可以看到:

public void scrollTo(int x, int y) {
 if (mScrollX != x || mScrollY != y) {
 int oldX = mScrollX;
 int oldY = mScrollY;
 mScrollX = x;//赋值新的x偏移量
 mScrollY = y;//赋值新的y偏移量
 invalidateParentCaches();
 onScrollChanged(mScrollX, mScrollY, oldX, oldY);
 if (!awakenScrollBars()) {
 postInvalidateOnAnimation();
 }
 }
 } 

scrollTo是相对于初始位置来进行移动的,而scrollBy(int x ,int y)则是相对于上一次移动的距离来进行本次移动。scrollBy其实还是依赖于scrollTo的,如下源码:

public void scrollBy(int x, int y) {
 scrollTo(mScrollX + x, mScrollY + y);
 } 

可以看到,使用scrollBy其实就是省略了我们在计算scrollTo参数时的第三步而已,因为scrollBy内部已经自己帮我加上了第三步的计算。因此scrollBy的作用就是相当于在上一次的偏移情况下进行本次的偏移。

一个完整的水平方向滑动的例子:

public class MyViewPager extends ViewGroup {
 private int mLastX;
 public MyViewPager(Context context) {
 super(context);
 init(context);
 }
 public MyViewPager(Context context, AttributeSet attrs) {
 super(context, attrs);
 init(context);
 }
 public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 init(context);
 }
 private void init(Context context) {
 }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int count = getChildCount();
 for(int i = 0; i < count; i++){
 View child = getChildAt(i);
 child.measure(widthMeasureSpec,heightMeasureSpec);
 }
 }
 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 int count = getChildCount();
 Log.d("TAG","--l-->"+l+",--t-->"+t+",-->r-->"+r+",--b-->"+b);
 for(int i = 0; i < count; i++){
 View child = getChildAt(i);
 child.layout(i * getWidth(), t, (i+1) * getWidth(), b);
 }
 }
 @Override
 public boolean onTouchEvent(MotionEvent ev) {
 int x = (int) ev.getX();
 switch (ev.getAction()){
 case MotionEvent.ACTION_DOWN:
 mLastX = x;
 break;
 case MotionEvent.ACTION_MOVE:
 int dx = mLastX - x;
 int oldScrollX = getScrollX();//原来的偏移量
 int preScrollX = oldScrollX + dx;//本次滑动后形成的偏移量
 if(preScrollX > (getChildCount() - 1) * getWidth()){
  preScrollX = (getChildCount() - 1) * getWidth();
 }
 if(preScrollX < 0){
  preScrollX = 0;
 }
 scrollTo(preScrollX,getScrollY());
 mLastX = x;
 break;
 }
 return true;
 }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> 

 <com.scu.lly.viewtest.view.MyViewPager
android:layout_width="match_parent"
android:layout_height="300dp"
>
 <ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/test1" /> 

 <ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/test2" /> 

 <ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/test3" /> 

 <ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/test4" />
 </com.scu.lly.viewtest.view.MyViewPager>
</LinearLayout>

效果如图:

二、Scroller滑动辅助类

根据我们上面的分析,可知View的scrollTo()、scrollBy()是瞬间完成的,当我们的手指在屏幕上移动时,内容会跟着手指滑动,但是当我们手指一抬起时,滑动就会停止,如果我们想要有一种惯性的滚动过程效果和回弹效果,此时就需要使用Scroller辅助类。

但是注意的是,Scroller本身不会去移动View,它只是一个移动计算辅助类,用于跟踪控件滑动的轨迹,只相当于一个滚动轨迹记录工具,最终还是通过View的scrollTo、scrollBy方法完成View的移动的。

在使用Scroller类之前,先了解其重要的两个方法:

(1)startScroll()

public void startScroll(int startX, int startY, int dx, int dy, int duration)

开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达偏移坐标为(startX+dx , startY+dy)处。

(2)computeScrollOffset()

public boolean computeScrollOffset()

滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrX和mCurrY值中。

上面两个方法的源码如下:

public class Scroller {
private int mStartX;//水平方向,滑动时的起点偏移坐标
private int mStartY;//垂直方向,滑动时的起点偏移坐标
private int mFinalX;//滑动完成后的偏移坐标,水平方向
private int mFinalY;//滑动完成后的偏移坐标,垂直方向
private int mCurrX;//滑动过程中,根据消耗的时间计算出的当前的滑动偏移距离,水平方向
private int mCurrY;//滑动过程中,根据消耗的时间计算出的当前的滑动偏移距离,垂直方向
private int mDuration; //本次滑动的动画时间
private float mDeltaX;//滑动过程中,在达到mFinalX前还需要滑动的距离,水平方向
private float mDeltaY;//滑动过程中,在达到mFinalX前还需要滑动的距离,垂直方向
public void startScroll(int startX, int startY, int dx, int dy) {
 startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
/**
 * 开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达偏移坐标为(startX+dx , startY+dy)处
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
 mMode = SCROLL_MODE;
 mFinished = false;
 mDuration = duration;
 mStartTime = AnimationUtils.currentAnimationTimeMillis();
 mStartX = startX;
 mStartY = startY;
 mFinalX = startX + dx;//确定本次滑动完成后的偏移坐标
 mFinalY = startY + dy;
 mDeltaX = dx;
 mDeltaY = dy;
 mDurationReciprocal = 1.0f / (float) mDuration;
}
/**
 * 滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrX和mCurrY值中
 * @return
*/
public boolean computeScrollOffset() {
 if (mFinished) {//已经完成了本次动画控制,直接返回为false
 return false;
 }
 int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
 if (timePassed < mDuration) {
 switch (mMode) {
 case SCROLL_MODE:
 final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
 mCurrX = mStartX + Math.round(x * mDeltaX);//计算出当前的滑动偏移位置,x轴
 mCurrY = mStartY + Math.round(x * mDeltaY);//计算出当前的滑动偏移位置,y轴
 break;
 ...
 }
 }else {
 mCurrX = mFinalX;
 mCurrY = mFinalY;
 mFinished = true;
 }
 return true;
 }
 ...
}

Scroller类中最重要的两个方法就是startScroll()和computeScrollOffset(),但是Scroller类只是一个滑动计算辅助类,它的startScroll()和computeScrollOffset()方法中也只是对一些轨迹参数进行设置和计算,真正需要进行滑动还是得通过View的scrollTo()、scrollBy()方法。为此,View中提供了computeScroll()方法来控制这个滑动流程。computeScroll()方法会在绘制子视图的时候进行调用。其源码如下:

/**
 * Called by a parent to request that a child update its values for mScrollX
 * and mScrollY if necessary. This will typically be done if the child is
 * animating a scroll using a {@link android.widget.Scroller Scroller}
 * object.
 * 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
 */
public void computeScroll() { //空方法 ,自定义滑动功能的ViewGroup必须实现方法体 

} 

因此Scroller类的基本使用流程可以总结如下:

(1)首先通过Scroller类的startScroll()开始一个滑动动画控制,里面进行了一些轨迹参数的设置和计算;

(2)在调用startScroll()的后面调用invalidate();引起视图的重绘操作,从而触发ViewGroup中的computeScroll()被调用;

(3)在computeScroll()方法中,先调用Scroller类中的computeScrollOffset()方法,里面根据当前消耗时间进行轨迹坐标的计算,然后取得计算出的当前滑动的偏移坐标,调用View的scrollTo()方法进行滑动控制,最后也需要调用invalidate();进行重绘。

如下的一个简单代码示例:

@Override
 public boolean onTouchEvent(MotionEvent ev) {
 initVelocityTrackerIfNotExists();
 mVelocityTracker.addMovement(ev);
 int x = (int) ev.getX();
 switch (ev.getAction()){
 case MotionEvent.ACTION_DOWN:
 if(!mScroller.isFinished()){
  mScroller.abortAnimation();
 }
 mLastX = x;
 break;
 case MotionEvent.ACTION_MOVE:
 int dx = mLastX - x;
 int oldScrollX = getScrollX();//原来的偏移量
 int preScrollX = oldScrollX + dx;//本次滑动后形成的偏移量
 if(preScrollX > (getChildCount() - 1) * getWidth()){
  preScrollX = (getChildCount() - 1) * getWidth();
 }
 if(preScrollX < 0){
  preScrollX = 0;
 }
 //开始滑动动画
 mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),dx,0);//第一步
 //注意,一定要进行invalidate刷新界面,触发computeScroll()方法,因为单纯的startScroll()是属于Scroller的,只是一个辅助类,并不会触发界面的绘制
 invalidate();
 mLastX = x;
 break;
 }
 return true;
 }
 @Override
 public void computeScroll() {
 super.computeScroll();
 if(mScroller.computeScrollOffset()){//第二步
 scrollTo(mScroller.getCurrX(),mScroller.getCurrY());//第三步
 invalidate();
 }
 } 

下面是一个完整的例子:一个类似ViewPager的Demo,效果图如下:

代码如下:

public class MyViewPager3 extends ViewGroup { 

 private int mLastX; 

 private Scroller mScroller;
 private VelocityTracker mVelocityTracker;
 private int mTouchSlop;
 private int mMaxVelocity;
 /**
 * 当前显示的是第几个屏幕
 */
 private int mCurrentPage = 0; 

 public MyViewPager3(Context context) {
 super(context);
 init(context);
 } 

 public MyViewPager3(Context context, AttributeSet attrs) {
 super(context, attrs);
 init(context);
 } 

 public MyViewPager3(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 init(context);
 } 

 private void init(Context context) {
 mScroller = new Scroller(context);
 ViewConfiguration config = ViewConfiguration.get(context);
 mTouchSlop = config.getScaledPagingTouchSlop();
 mMaxVelocity = config.getScaledMinimumFlingVelocity();
 } 

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int count = getChildCount();
 for(int i = 0; i < count; i++){
 View child = getChildAt(i);
 child.measure(widthMeasureSpec, heightMeasureSpec);
 }
 } 

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 int count = getChildCount();
 Log.d("TAG","--l-->"+l+",--t-->"+t+",-->r-->"+r+",--b-->"+b);
 for(int i = 0; i < count; i++){
 View child = getChildAt(i);
 child.layout(i * getWidth(), t, (i + 1) * getWidth(), b);
 }
 } 

 @Override
 public boolean onTouchEvent(MotionEvent ev) {
 initVelocityTrackerIfNotExists();
 mVelocityTracker.addMovement(ev);
 int x = (int) ev.getX();
 switch (ev.getAction()){
 case MotionEvent.ACTION_DOWN:
 if(!mScroller.isFinished()){
  mScroller.abortAnimation();
 }
 mLastX = x;
 break;
 case MotionEvent.ACTION_MOVE:
 int dx = mLastX - x;
 /* 注释的里面是使用startScroll()来进行滑动的
 int oldScrollX = getScrollX();//原来的偏移量
 int preScrollX = oldScrollX + dx;//本次滑动后形成的偏移量
 if (preScrollX > (getChildCount() - 1) * getWidth()) {
  preScrollX = (getChildCount() - 1) * getWidth();
  dx = preScrollX - oldScrollX;
 }
 if (preScrollX < 0) {
  preScrollX = 0;
  dx = preScrollX - oldScrollX;
 }
 mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, 0);
 //注意,使用startScroll后面一定要进行invalidate刷新界面,触发computeScroll()方法,因为单纯的startScroll()是属于Scroller的,只是一个辅助类,并不会触发界面的绘制
 invalidate();
 */
 //但是一般在ACTION_MOVE中我们直接使用scrollTo或者scrollBy更加方便
 scrollBy(dx,0);
 mLastX = x;
 break;
 case MotionEvent.ACTION_UP:
 final VelocityTracker velocityTracker = mVelocityTracker;
 velocityTracker.computeCurrentVelocity(1000);
 int initVelocity = (int) velocityTracker.getXVelocity();
 if(initVelocity > mMaxVelocity && mCurrentPage > 0){//如果是快速的向右滑,则需要显示上一个屏幕
  Log.d("TAG","----------------快速的向右滑--------------------");
  scrollToPage(mCurrentPage - 1);
 }else if(initVelocity < -mMaxVelocity && mCurrentPage < (getChildCount() - 1)){//如果是快速向左滑动,则需要显示下一个屏幕
  Log.d("TAG","----------------快速的向左滑--------------------");
  scrollToPage(mCurrentPage + 1);
 }else{//不是快速滑动的情况,此时需要计算是滑动到
  Log.d("TAG","----------------慢慢的滑动--------------------");
  slowScrollToPage();
 }
 recycleVelocityTracker();
 break;
 }
 return true;
 } 

 /**
 * 缓慢滑动抬起手指的情形,需要判断是停留在本Page还是往前、往后滑动
 */
 private void slowScrollToPage() {
 //当前的偏移位置
 int scrollX = getScrollX();
 int scrollY = getScrollY();
 //判断是停留在本Page还是往前一个page滑动或者是往后一个page滑动
 int whichPage = (getScrollX() + getWidth() / 2 ) / getWidth() ;
 scrollToPage(whichPage);
 } 

 /**
 * 滑动到指定屏幕
 * @param indexPage
 */
 private void scrollToPage(int indexPage) {
 mCurrentPage = indexPage;
 if(mCurrentPage > getChildCount() - 1){
 mCurrentPage = getChildCount() - 1;
 }
 //计算滑动到指定Page还需要滑动的距离
 int dx = mCurrentPage * getWidth() - getScrollX();
 mScroller.startScroll(getScrollX(),0,dx,0,Math.abs(dx) * 2);//动画时间设置为Math.abs(dx) * 2 ms
 //记住,使用Scroller类需要手动invalidate
 invalidate();
 } 

 @Override
 public void computeScroll() {
 Log.d("TAG", "---------computeScrollcomputeScrollcomputeScroll--------------");
 super.computeScroll();
 if(mScroller.computeScrollOffset()){
 scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
 invalidate();
 }
 } 

 private void recycleVelocityTracker() {
 if (mVelocityTracker != null) {
 mVelocityTracker.recycle();
 mVelocityTracker = null;
 }
 } 

 private void initVelocityTrackerIfNotExists() {
 if(mVelocityTracker == null){
 mVelocityTracker = VelocityTracker.obtain();
 }
 }
}

布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">

 <com.lusheep.viewtest.view.MyViewPager3
 android:layout_width="match_parent"
 android:layout_height="200dp"
 android:background="#999" >
 <ImageView
 android:layout_width="300dp"
 android:layout_height="match_parent"
 android:scaleType="fitXY"
 android:src="@drawable/test1" />

 <ImageView
 android:layout_width="300dp"
 android:layout_height="match_parent"
 android:scaleType="fitXY"
 android:src="@drawable/test2" />

 <ImageView
 android:layout_width="300dp"
 android:layout_height="match_parent"
 android:scaleType="fitXY"
 android:src="@drawable/test3" />

 <ImageView
 android:layout_width="300dp"
 android:layout_height="match_parent"
 android:scaleType="fitXY"
 android:src="@drawable/test4" />
 </com.lusheep.viewtest.view.MyViewPager3>
</LinearLayout>

点此下载

简单总结:

(1)Scroller类能够帮助我们实现高级的滑动功能,如手指抬起后的惯性滑动功能。使用流程为,首先通过Scroller类的startScroll()+invalidate()触发View的computeScroll(),在computeScroll()中让Scroller类去计算最新的坐标信息,拿到最新的坐标偏移信息后还是要调用View的scrollTo来实现滑动。可以看到,使用Scroller的整个流程比较简单,关键的是控制滑动的一些逻辑计算,比如上面例子中的计算什么时候该往哪一页滑动...

(2)Android后面推出了OverScroller类,OverScroller在整体功能上和Scroller类似,使用也相同。OverScroller类可以完全代替Scroller,相比Scroller,OverScroller主要是增加了对滑动到边界的一些控制,如增加一些回弹效果等,功能更加强大。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • 详解Android应用开发中Scroller类的屏幕滑动功能运用

    今天给大家介绍下Android中滑屏功能的一个基本实现过程以及原理初探,最后给大家重点讲解View视图中scrollTo 与scrollBy这两个函数的区别 .   首先 ,我们必须明白在Android View视图是没有边界的,Canvas是没有边界的,只不过我们通过绘制特定的View时对Canvas对象进行了一定的操作,例如 : translate(平移).clipRect(剪切)等,以便达到我们的对该Canvas对象绘制的要求 ,我们可以将这种无边界的视图称为"视图坐标"----

  • Android Scroller完全解析

    在Android中,任何一个控件都是可以滚动的,因为在View类当中有scrollTo()和scrollBy()这两个方法,如下图所示: 这两个方法的主要作用是将View/ViewGroup移至指定的坐标中,并且将偏移量保存起来.另外: mScrollX 代表X轴方向的偏移坐标 mScrollY 代表Y轴方向的偏移坐标 这两个方法都是用于对View进行滚动的,那么它们之间有什么区别呢?简单点讲,scrollBy()方法是让View相对于当前的位置滚动某段距离,而scrollTo()方法则是让Vi

  • Android Scroller及下拉刷新组件原理解析

    Android事件拦截机制 Android中事件的传递和拦截和View树结构是相关联的,在View树中,分为叶子节点和普通节点,普通节点有子节点只能是ViewGroup,叶子节点可以是View或者ViewGroup.Android和事件分发拦截相关的方法有 dispatchTouchEvent(MotionEvent ev) 事件分发相关的方法,沿着View树将一个用户的触摸事件向下分发. onInterceptTouchEvent(MotionEvent ev) 在dispatchTouchE

  • 深入理解Android中Scroller的滚动原理

    View的平滑滚动效果 什么是实现View的平滑滚动效果呢,举个简单的例子,一个View从在我们指定的时间内从一个位置滚动到另外一个位置,我们利用Scroller类可以实现匀速滚动,可以先加速后减速,可以先减速后加速等等效果,而不是瞬间的移动的效果,所以Scroller可以帮我们实现很多滑动的效果. 首先我们先来看一下Scroller的用法,基本可概括为"三部曲": 1.创建一个Scroller对象,一般在View的构造器中创建: public ScrollViewGroup(Cont

  • android使用 ScrollerView 实现 可上下滚动的分类栏实例

    如果不考虑更深层的性能问题,我个人认为ScrollerView还是很好用的.而且单用ScrollerView就可以实现分类型的RecyclerView或ListView所能实现的效果. 下面我单单从效果展示方面考虑,使用ScrollerView实现如下图所示的可滚动的多条目分类,只是为了跟大家一起分享一下新思路.(平时:若从复用性等方面考虑,这显然是存在瑕疵的~) 特点描述: 1.可上下滚动 2.有类似于网格布局的样式 3.子条目具有点击事件 刚看到这个效果时,首先想到的是使用分类型的Recyc

  • 详解Android Scroller与computeScroll的调用机制关系

    Android ViewGroup中的Scroller与computeScroll的有什么关系? 答:没有直接的关系 知道了答案,是不是意味着下文就没必要看了,如果说对ViewGroup自定义控件不感兴趣,可以不用看了. 1.Scroller到底是什么? 答:Scroller只是个计算器,提供插值计算,让滚动过程具有动画属性,但它并不是UI,也不是滑动辅助UI运动,反而是单纯地为滑动提供计算. 无论从构造方法还是其他方法,以及Scroller的属性可知,其并不会持有View,辅助ViewGrou

  • Android程序开发之UIScrollerView里有两个tableView

    一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController : UIViewController <UIScrollViewDelegate,UITableViewDelegate,UITableViewDataSource> { UIScrollView *_scrolView; UITableView *_tableView; UITableView

  • Android Scroller大揭秘

    在学习使用Scroller之前,需要明白scrollTo().scrollBy()方法. 一.View的scrollTo().scrollBy() scrollTo.scrollBy方法是View中的,因此任何的View都可以通过这两种方法进行移动.首先要明白的是,scrollTo.scrollBy滑动的是View中的内容(而且还是整体滑动),而不是View本身.我们的滑动控件如SrollView可以限定宽.高大小,以及在布局中的位置,但是滑动控件中的内容(或者里面的childView)可以是无

  • Android Scroller的使用方法

    本文实例为大家分享了Android Scroller的使用方法,供大家参考,具体内容如下 1.scrollTo和ScrollBy View类定义了两个用于滚动View内容的方法:scrollTo和scrollBy: /** * Set the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be *

  • Android 美食大转盘详解流程

    目录 效果视频 前言 美食大转盘 初始化SurfaceView 测量 绘制 绘制盘块 开始旋转转盘 停止旋转转盘 自定义转盘等份 控件引用 沉浸式体验 效果图 Reveal Animator 效果视频 自定义转盘代码 XML布局代码 Activity代码 代码下载地址 效果视频 前言 你还在为明天吃什么而烦恼嘛 美食大赏帮你解决选择困难症 帮你做出最佳的选择 做吃货,我们是认真的 美食大转盘 本示例使用SurfaceView绘制而成,接下来逐步分析, 文末会贴出全部代码``文末会贴出全部代码``

  • Android Scroller实现弹性滑动效果

    本文实例为大家分享了Android Scroller实现弹性滑动的具体代码,供大家参考,具体内容如下 首先看下实现效果,可以看到当我们手指松开时图片会逐渐滑动到初始位置,而不是直接跳变到中心点. 代码实现 当手指触摸到view上时即TouchEvent位MotionEvent.ACTION_DOWN时,记录开始的坐标位置,同时由于手指再次按到屏幕上的的时候view还在执行动画,所以当动画还在执行的时候我们需要将动画停止. if (!mScroller.isFinished()) {     mS

  • PHP生成条形码大揭秘

    1.什么是条形码? 百度百科定义:条形码(barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符.常见的条形码是由反射率相差很大的黑条(简称条)和白条(简称空)排成平行线的图案.在日常生活中,条形码可以标出物品的生产国.制造厂家.商品名称.生产日期.图书分类号.邮件地点起止.类别.日期等许多信息.条形码编码格式具体请参考 打印出来的优惠券,商家需要用验证器读取条形码,来获得其有效性. 2.如何生成条形码? 首先找到强大的开源资料,在barcode官网下

  • IOS安装包比Android容量大的原因

    昨天ios的硕士实习小伙伴,咳咳.在疑惑这个问题,于是就各种找资料.最后只找到两个权威的链接,根据这两个权威的链接,整理如下: ios的app包含所有版本. 在ios9之前,应用程序的所有版本我们都进行了下载和安装.这意味着不管你的手机需要app的什么图片和资源,所有屏幕尺寸的图片和资源都会被下载. 想想那是多少: Non retina 3.5" Retina 3.5" Retina 4", 4.7", 5.5" Non retina iPad full

  • Unicode编码大揭秘

    如果你是一个生活在2003年的程序员,却不了解字符.字符集.编码和Unicode这些基础知识.那你可要小心了,要是被我抓到你,我会让你在潜水艇里剥六个月洋葱来惩罚你. 这个邪恶的恐吓是Joel Spolsky在十年前首次发出的.不幸的是,很多人认为他只是在开玩笑,因此,现在仍有许多人不能完全理解Unicode,以及Unicode.UTF-8.UTF-16之间的区别.这就是我写这篇文章的原因. 言归正传,设想在一个晴朗的下午,你收到一封电子邮件,它来自一个你高中之后就失去联系的朋友,并带有一个tx

随机推荐