您的位置:首页 > 移动开发 > Android开发

Android View - Scroll

2015-11-23 18:03 393 查看
1.Scroller类是为了实现 View 平滑滚动的一个Helper类

自定义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();
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  scroll view android