Android View - Scroll
2015-11-23 18:03
393 查看
1.Scroller类是为了实现 View 平滑滚动的一个Helper类
(自定义view种经常用到)
既然是helper类:
所以 只是记录了位置,设置,配置,需要用到时取出来就好了
所以Scroller 不要想太多,其实没什么的,就当成一个工具类就好(提供的辅助滑动的工具类),主要是他要 怎么样才能结合view 使用
需要注意:滚动的是 View 中的 子view,并不是view本身
2. View , ViewGroup 的结合使用
当自定义view 继承 View 或 ViewGroup 时罗列一下,
自定义如果需要移动等效果,常用到的几个方法,这些都是view 提供的,当继承后,可以利用达到自己想要的移动,滚动等效果
//获得当前的偏移量 x
public final int getScrollX() {
return mScrollX;
}
//获得当前的偏移量 y
public final int getScrollY() {
return mScrollY;
}
//视图内容相当于视图起始坐标的偏移量 ,X轴 方向
protected int mScrollX;
//该视图内容相当于视图起始坐标的偏移量 ,Y轴方向
protected int mScrollY;
// 移动到哪里,x,y 需要给出,也就会相应的调用onScrollChanged方法,并且view 会刷新(以前的视图无效)
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
//回调onScrollChanged方法
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
// 在本来的偏移量上再移动多少
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
// computeScroll 是个空方法
// 基本的解释就是 父View 需要子View 去更新 mScrollX,mScrollY 的时候会去调用,如果孩子用着 scroll,调用了invalidate()方法后,也就会相应的调用到这个方法,这2个方法的配合很重要。
/**
* 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.
*/
public void computeScroll() {
}
// 让界面刷新时,需要用到的 invalidate
/**
* This is where the invalidate() work actually happens. A full invalidate()
* causes the drawing cache to be invalidated, but this function can be
* called with invalidateCache set to false to skip that invalidation step
* for cases that do not need it (for example, a component that remains at
* the same dimensions with the same content).
*
* invalidateCache Whether the drawing cache for this view should be
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View’s contents or
* dimensions have not changed.
*/
void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
3. 配合的实现方式
需要先设想滑动的效果,需要移动到哪里等,然后塞入工具类 scroll种,
真正调用view 的方法实现时,再取出调用
例:
4. 总结:整个流程
View - (预设工具类) scroll.startScroll() - (刷新)invalidate() - (view重新绘制调用 ,一直重复调用哦)computeScroll - (判断是否执行完,是上面重复调用的关键)mScroller.computeScrollOffset()
网上的一个代码提供学习
(自定义view种经常用到)
既然是helper类:
所以 只是记录了位置,设置,配置,需要用到时取出来就好了
所以Scroller 不要想太多,其实没什么的,就当成一个工具类就好(提供的辅助滑动的工具类),主要是他要 怎么样才能结合view 使用
需要注意:滚动的是 View 中的 子view,并不是view本身
mScroller.getCurrX() //获取mScroller当前水平滚动的位置 mScroller.getCurrY() //获取mScroller当前竖直滚动的位置 mScroller.getFinalX() //获取mScroller最终停止的水平位置 mScroller.getFinalY() //获取mScroller最终停止的竖直位置 mScroller.setFinalX(int newX) //设置mScroller最终停留的水平位置,没有动画效果,直接跳到目标位置 mScroller.setFinalY(int newY) //设置mScroller最终停留的竖直位置,没有动画效果,直接跳到目标位置 //滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间 mScroller.startScroll(int startX, int startY, int dx, int dy) //使用默认完成时间250ms mScroller.startScroll(int startX, int startY, int dx, int dy, int duration) mScroller.extendDuration (int extend)//延长滚动多长时间 mScroller.abortAnimation() //停止,并立即运动到finalx,y // 当想要知道新的位置时,调用此函数。如果返回true,表示动画还没有结束。位置改变以提供一个新的位置。 mScroller.computeScrollOffset() //返回值为boolean,true说明动画尚未完成,false说明动画已经完成。这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束 //返回滚动事件的持续时间,以毫秒计算。 public final int getDuration () /** startX 滚动起始点X坐标 startY 滚动起始点Y坐标 velocityX 当滑动屏幕时X方向初速度,以每秒像素数计算 velocityY 当滑动屏幕时Y方向初速度,以每秒像素数计算 minX X方向的最小值,scroller不会滚过此点。 maxX X方向的最大值,scroller不会滚过此点。 minY Y方向的最小值,scroller不会滚过此点。 maxY Y方向的最大值,scroller不会滚过此点。 */ public void fling (int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) //返回滚动结束位置。仅针对“fling”手势有效 public final int getFinalX () //返回滚动结束位置。仅针对“fling”操作有效 public final int getFinalY () // 返回scroller是否已完成滚动,停止滚动返回true,否则返回false public final boolean isFinished () //返回自滚动开始经过的时间(毫秒) public int timePassed ()
2. View , ViewGroup 的结合使用
当自定义view 继承 View 或 ViewGroup 时罗列一下,
自定义如果需要移动等效果,常用到的几个方法,这些都是view 提供的,当继承后,可以利用达到自己想要的移动,滚动等效果
//获得当前的偏移量 x
public final int getScrollX() {
return mScrollX;
}
//获得当前的偏移量 y
public final int getScrollY() {
return mScrollY;
}
//视图内容相当于视图起始坐标的偏移量 ,X轴 方向
protected int mScrollX;
//该视图内容相当于视图起始坐标的偏移量 ,Y轴方向
protected int mScrollY;
// 移动到哪里,x,y 需要给出,也就会相应的调用onScrollChanged方法,并且view 会刷新(以前的视图无效)
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
//回调onScrollChanged方法
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
// 在本来的偏移量上再移动多少
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
// computeScroll 是个空方法
// 基本的解释就是 父View 需要子View 去更新 mScrollX,mScrollY 的时候会去调用,如果孩子用着 scroll,调用了invalidate()方法后,也就会相应的调用到这个方法,这2个方法的配合很重要。
/**
* 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.
*/
public void computeScroll() {
}
// 让界面刷新时,需要用到的 invalidate
/**
* This is where the invalidate() work actually happens. A full invalidate()
* causes the drawing cache to be invalidated, but this function can be
* called with invalidateCache set to false to skip that invalidation step
* for cases that do not need it (for example, a component that remains at
* the same dimensions with the same content).
*
* invalidateCache Whether the drawing cache for this view should be
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View’s contents or
* dimensions have not changed.
*/
void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { if (mGhostView != null) { mGhostView.invalidate(invalidateCache); return; } if (skipInvalidate()) { return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque)) { if (fullInvalidate) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; } mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); p.invalidateChild(this, damage); } // Damage the entire projection receiver, if necessary. if (mBackground != null && mBackground.isProjected()) { final View receiver = getProjectionReceiver(); if (receiver != null) { receiver.damageInParent(); } } // Damage the entire IsolatedZVolume receiving this view's shadow. if (isHardwareAccelerated() && getZ() != 0) { damageShadowReceiver(); } } }
3. 配合的实现方式
需要先设想滑动的效果,需要移动到哪里等,然后塞入工具类 scroll种,
真正调用view 的方法实现时,再取出调用
例:
1.//需要创建工具类 Scroller mScroller = new Scroller(mContext); 2. private void myScrollTo(int destX, int destY) { //先获取现在的view的偏移量 x int scrollX = getScrollX(); //计算出在现在的偏移量 x 的基础上, 我还要继续便宜多少 int delta = destX - scrollX; //这里看着是 startScroll 其实并没有真正的滑动,只是工具类Scroller记录了位置,执行时间等参数 mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 3); // mScroller.startScroll(scrollX, 0, delta, 0, 5000); //当调用了 invalidate 会立即调用computeScroll这个空方法,所以我们要在这个空方法实现我们的真正的效果 invalidate(); } 3. 会一直调用这个方法 //父view 要求他内部的子view的mScrollX和mScrollY发生变化时,通常情况下子view使用Scroller对象进行动画滚动. @Override public void computeScroll() { //true 说明滚动未完成 false 说明滚动完成 boolean a = mScroller.computeScrollOffset(); System.out.println("==========a======"+ a); if (a) { //调用 View的 scrollTo方法开始滚动或滑动效果,参数则是从 Scroll这个工具类种取出来的 scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
4. 总结:整个流程
View - (预设工具类) scroll.startScroll() - (刷新)invalidate() - (view重新绘制调用 ,一直重复调用哦)computeScroll - (判断是否执行完,是上面重复调用的关键)mScroller.computeScrollOffset()
网上的一个代码提供学习
public class SlideView extends LinearLayout { private static final String TAG = "SlideView"; private Context mContext; private LinearLayout mViewContent; private RelativeLayout mHolder; private Scroller mScroller; private OnSlideListener mOnSlideListener; private int mHolderWidth = 120; private int mLastX = 0; private int mLastY = 0; private static final int TAN = 2; public interface OnSlideListener { public static final int SLIDE_STATUS_OFF = 0; //初始的状态 public static final int SLIDE_STATUS_START_SCROLL = 1; // 正在滑动 public static final int SLIDE_STATUS_ON = 2; // 显示出删除字段 public void onSlide(View view, int status); } public SlideView(Context context) { super(context); initView(); } public SlideView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } private void initView() { mContext = getContext(); //创建滑动的 scroller mScroller = new Scroller(mContext); setOrientation(LinearLayout.HORIZONTAL); //加载自定义view的界面布局 View.inflate(mContext, R.layout.rygslide_view_merge, this); //总的linearlayout mViewContent = (LinearLayout) findViewById(R.id.view_content); //这个方法是转变为标准尺寸的一个函数,例如 // int size = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics()); // 这里COMPLEX_UNIT_SP是单位,20是数值,也就是20sp。 mHolderWidth = Math.round(TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, mHolderWidth, getResources() .getDisplayMetrics())); } //设置移动后显示的字体 public void setButtonText(CharSequence text) { ((TextView)findViewById(R.id.delete)).setText(text); } //添加自己本身 ListView 要显示的 xml界面 public void setContentView(View view) { mViewContent.addView(view); } public View getContentView(){ return mViewContent; } public void setOnSlideListener(OnSlideListener onSlideListener) { mOnSlideListener = onSlideListener; } public void shrink() { if (getScrollX() != 0) { this.smoothScrollTo(0, 0); } } // 从listview端接收到 event事件,用来执行滑动等动画 public void onRequireTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); int clickx = 0; int clicky = 0; int scrollX = getScrollX(); Log.d(TAG, "x=" + x + " y=" + y); switch (event.getAction()) { //当 case MotionEvent.ACTION_DOWN: { clickx = (int) event.getX(); clicky = (int) event.getY(); if (!mScroller.isFinished()) { mScroller.abortAnimation(); } if (mOnSlideListener != null) { mOnSlideListener.onSlide(this, OnSlideListener.SLIDE_STATUS_START_SCROLL); } break; } case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastX; int deltaY = y - mLastY; if (Math.abs(deltaX) < Math.abs(deltaY) * TAN) { break; } int newScrollX = scrollX - deltaX; if (deltaX != 0) { if (newScrollX < 0) { newScrollX = 0; } else if (newScrollX > mHolderWidth) { newScrollX = mHolderWidth; } this.scrollTo(newScrollX, 0); } break; } case MotionEvent.ACTION_UP: { int newScrollX = 0; if((int)event.getX() == clickx && (int)event.getY() == clicky ) System.out.println("====================onclick================"); //手指滑动多少会判定, 这样进行撤回 if (scrollX - mHolderWidth * 0.75 > 0) { newScrollX = mHolderWidth; } System.out.println("====ACTION_UP==="+ newScrollX ); //手指抬起,滑动到指定 this.smoothScrollTo(newScrollX, 0); if (mOnSlideListener != null) { mOnSlideListener.onSlide(this, newScrollX == 0 ? OnSlideListener.SLIDE_STATUS_OFF : OnSlideListener.SLIDE_STATUS_ON); } break; } default: break; } mLastX = x; mLastY = y; } // 调用这里进行的刷新哦哦哦 // 1.滑动到界面外面,显示 删除 // 2.滑动回原来的位置 private void smoothScrollTo(int destX, int destY) { int scrollX = getScrollX(); int delta = destX - scrollX; mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 3); // mScroller.startScroll(scrollX, 0, delta, 0, 5000); invalidate(); } //父view 要求他内部的子view的mScrollX和mScrollY发生变化时,通常情况下子view使用Scroller对象进行动画滚动. @Override public void computeScroll() { //true 说明滚动未完成 false 说明滚动完成 boolean a = mScroller.computeScrollOffset(); System.out.println("==========a======"+ a); if (a) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories