主要记录学习《Android开发艺术探索》
1.View的位置参数
left,top,right,bottom;
表示View的原始信息,view在移动过程不会发生变化。
x,y,translationX,translationY;
x,y表示View左上角顶点坐标,translationX,translationY表示View向x轴,y轴移动的距离。若View的位置未发生移动translationX,translationY都为0;
x=left+translationX;
y=top+translationY;
以上大小都是View相对于父控件的坐标。Android坐标系原点在左上角,x轴水平向右为正,y轴竖直向下为正。
2.MotionEvent和TouchSlop
2.1 MotionEvent
当手指接触屏幕后会产生一系列的事件。典型的有以下几种
1.ACTION_DOWN 手指刚接触屏幕
2.ACTION_MOVE 手指在屏幕上移动
3.ACTION_UP 手指离开屏幕
MotionEvent 可通过event.getX(); event.getY();获取相对父控件坐标。
event.getRawX(); event.getRawY();获取相对屏幕的绝对坐标。
2.2 TouchSlop
系统所能识别出被认为是滑动的最小距离,用于过滤一些微小的滑动
可通过如下方式获取到
ViewConfiguration.get(getContext()).getScaledTouchSlop();
3.VelocityTracker,GestureDetector和Scroller
3.1 VelocityTracker
手在屏幕的滑动速度
VelocityTracker velocityTracker=VelocityTracker.obtain();
velocityTracker.addMovement(event);
//设置时间间隔为1000毫秒
velocityTracker.computeCurrentVelocity(1000);
//表示水平方向1000毫秒内�从左往右滑过的像素点(向右滑为正,左滑为负)
int xVelocity= (int) velocityTracker.getXVelocity();
//表示竖直方向1000毫秒内�从上往下滑过的像素点(向下滑为正,上滑为负)
int yVelocity= (int) velocityTracker.getYVelocity();
velocityTracker.clear();
velocityTracker.recycle();
3.2 GestureDetector 和ScaleGestureDetector
手势检测和缩放手势检测
GestureDetector gestureDetector=new GestureDetector(getContext(),new GestureDetector.OnGestureListener() {
//手指触碰一瞬间触发
@Override
public boolean onDown(MotionEvent e) {
return false;
}
//手指触碰未松开或者拖动
@Override
public void onShowPress(MotionEvent e) {
}
//单击
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
//拖动
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
//长按
@Override
public void onLongPress(MotionEvent e) {
}
//快速滑动
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});
gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
//严格单击行为(不可能为双击中的某一次)
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
//双击行为
@Override
public boolean onDoubleTap(MotionEvent e) {
return false;
}
//双击行为,但是ACTION_DOWN ACTION_MOVE ACTION_UP 都会触发
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
});
//解决长按后无法拖动的现象
gestureDetector.setIsLongpressEnabled(false);
boolean consume = gestureDetector.onTouchEvent(event);
return consume;
ScaleGestureDetector g=new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
return false;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return false;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
});
boolean consume = g.onTouchEvent(event);
return consume;
3.3 Scroller
可以实现弹性滑动,但和View的computeScroll搭配使用典型代码如下
通过调用View的smoothScrollTo方法即可实现弹性滑动
private Scroller mscoller;
mscoller=new Scroller(getContext());
public void smoothScrollTo(int destX,int destY)
{
int scrollX=getScrollX();
int scorllY=getScrollY();
int deltax=destX-scrollX;
int deltay=destY-scorllY;
mscoller.startScroll(scrollX,scorllY,deltax,deltay,3000);
invalidate();
}
@Override
public void computeScroll() {
if (mscoller.computeScrollOffset())
{
scrollTo(mscoller.getCurrX(),mscoller.getCurrY());
postInvalidate();
}
}
4.View的滑动
4.1使用scrollTo和scrollBy
通过改变View的mScrollX,mScrollY参数来改变View内容的位置
若View内容在初始位置的的左边 mScrollX为正,反正为负。
若View的内容在初始位置的上面 mScrollY为正,反正为负。
可通过getScrollX();getScrollY();来获取mScrollX,mScrollY。
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
4.2使用动画
View动画只能改变View的影像无法改变View的位置参数,无法在新位置接收点击事件,有严重的缺陷。
属性动画可以改变View的位置参数translationX,translationY。
4.3改变布局参数
ViewGroup.MarginLayoutParams params=(ViewGroup.MarginLayoutParams)view.getLayoutParams();
params.width+=100;
params.leftMargin+=100;
view.requestLayout();
//或者
view.setLayoutParams(params);
5.弹性滑动
5.1Scoller
工作原理:通过startScroll()把要滑动的位置信息和时间等传入Scoller,在调用 invalidate()方法进行重绘,View的重绘过程会调用自身的computeScroll()方法,
这个方法在View中是空实现,我们要重写这个方法,若时间还没有到(computeScrollOffset()),就调用View的scollTo方法,通过scoller的getCurrX(),getCurrY()获取到按时间流逝百分比计算出来的mCurrX,mCurrY,然后在调用postInvalidate()方法继续重绘。
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;
}
public boolean computeScrollOffset() {
if (mFinished) {
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);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);
float distanceCoef = 1.f;
float velocityCoef = 0.f;
if (index < NB_SAMPLES) {
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + 1) / NB_SAMPLES;
final float d_inf = SPLINE_POSITION[index];
final float d_sup = SPLINE_POSITION[index + 1];
velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
distanceCoef = d_inf + (t - t_inf) * velocityCoef;
}
mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
5.2通过动画
动画本身就是一种渐进的过程,它天然就有弹性效果.
ObjectAnimator.ofFloat(view,"translationX",0,100).setDuration(1000).start();
可用动画特性来实现动画无法实现一些特殊效果
final int startX=0;
final int deltaX=100;
final int startY=0;
final int deltaY=100;
ValueAnimator animator=ValueAnimator.ofInt(0,1).setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction=animation.getAnimatedFraction();
view.scrollTo(startX+(int)(fraction * deltaX),startY+(int)(fraction * deltaY));
}
});
animator.start();