Created
June 25, 2013 03:20
-
-
Save arakuma/5855678 to your computer and use it in GitHub Desktop.
A simple dynamic layout tool.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.view.MotionEvent; | |
import android.view.View; | |
import android.view.ViewTreeObserver.OnGlobalLayoutListener; | |
import android.view.animation.Animation; | |
import android.view.animation.LinearInterpolator; | |
import android.view.animation.Animation.AnimationListener; | |
/** | |
* 对View进行动态layout的工具类 | |
* @author siwei1986@gmail.com | |
* | |
*/ | |
public class DynamicLayouter { | |
/** | |
* View隐藏时的移动方向 | |
* @author siwei1986@gmail.com | |
* | |
*/ | |
public enum Orientation{ | |
TOP, | |
BOTTOM, | |
LEFT, | |
RIGHT | |
} | |
/** | |
* 目标View | |
*/ | |
private View mView; | |
/** | |
* 移动方向 | |
*/ | |
private Orientation mOrientation; | |
/** | |
* 上次记录的touch位置 | |
*/ | |
float mLastPos = 0.0f; | |
/** | |
* 显示范围低侧 | |
*/ | |
private int mBoundLow; | |
/** | |
* 显示范围高侧 | |
*/ | |
private int mBoundHigh; | |
/** | |
* 是否手动设置的范围 | |
*/ | |
private boolean mIsManualBound; | |
/** | |
* 构造函数 | |
* @param view | |
* @param orientation | |
*/ | |
public DynamicLayouter(View view, Orientation orientation, int boundLow, int boundHigh){ | |
mView = view; | |
mBoundLow = boundLow; | |
mBoundHigh = boundHigh; | |
mIsManualBound = true; | |
} | |
/** | |
* 构造函数 | |
* @param view | |
* @param orientation | |
* @param extraOffset | |
*/ | |
public DynamicLayouter(View view, Orientation orientation){ | |
mView = view; | |
mOrientation = orientation; | |
mView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { | |
@Override | |
public void onGlobalLayout() { | |
mView.getViewTreeObserver().removeGlobalOnLayoutListener(this); | |
int screenWidth = DeviceInfo.getScreenWidth(); | |
int screenHeight = DeviceInfo.getScreenHeight(); | |
// 这本来想用嵌套三目运算符写的,虽然代码会少些但看起来实在痛苦,改成if-else了 | |
if(isHorizontal()){ | |
if(followsTouchDirection()){ | |
// 左 | |
mBoundLow = -mView.getWidth(); | |
mBoundHigh = 0; | |
} | |
else { | |
// 右 | |
mBoundLow = screenWidth - mView.getWidth(); | |
mBoundHigh = screenWidth; | |
} | |
} | |
else { | |
if(followsTouchDirection()){ | |
// 上 | |
mBoundLow = -mView.getHeight(); | |
mBoundHigh = 0; | |
} | |
else { | |
// 下 | |
mBoundLow = screenHeight - mView.getHeight(); | |
mBoundHigh = screenHeight; | |
} | |
} | |
} | |
}); | |
} | |
/** | |
* 发出一个touch event进行处理 | |
* @param event | |
*/ | |
public void feedMotionEvent(MotionEvent event){ | |
int action = event.getAction() & MotionEvent.ACTION_MASK; | |
switch (action) { | |
case MotionEvent.ACTION_DOWN: | |
mLastPos = isHorizontal() ? event.getRawX() : event.getRawY(); | |
break; | |
case MotionEvent.ACTION_MOVE: | |
float currentPos = isHorizontal() ? event.getRawX() : event.getRawY(); | |
int movedDistance = (int) (currentPos - mLastPos); | |
if(!followsTouchDirection()){ | |
// 逆动方向需要将移动距离修正为反向位移 | |
movedDistance = -movedDistance; | |
} | |
mLastPos = currentPos; | |
int newPos = movedDistance + (isHorizontal() ? mView.getLeft() : mView.getTop()); | |
if(newPos >= mBoundLow && newPos <= mBoundHigh){ | |
// 最多只能将控件侧边对齐到屏幕边缘,无论控件是否可见 | |
shiftView(movedDistance); | |
} | |
break; | |
case MotionEvent.ACTION_CANCEL: | |
case MotionEvent.ACTION_UP: | |
int shownPartSize = isHorizontal() | |
? followsTouchDirection() ? mView.getRight() : mBoundHigh - mView.getLeft() | |
: followsTouchDirection() ? mView.getBottom() : mBoundHigh - mView.getTop(); | |
// 显示的部分是否小于一半 | |
int rangeSize = mIsManualBound | |
? mBoundHigh - mBoundLow | |
: isHorizontal() ? mView.getWidth() : mView.getHeight(); | |
boolean isShownLessThanHalf = shownPartSize < rangeSize / 2; | |
// 需要移动的距离,显示的话要移动隐藏部分的尺寸,隐藏则是显示部分的尺寸 | |
int translateX, translateY; | |
if(isHorizontal()){ | |
translateX = isShownLessThanHalf ? -shownPartSize : rangeSize - shownPartSize; | |
translateY = 0; | |
if(!followsTouchDirection()){ | |
translateX = -translateX; | |
} | |
} | |
else { | |
translateX = 0; | |
translateY = isShownLessThanHalf ? -shownPartSize : rangeSize - shownPartSize; | |
if(!followsTouchDirection()){ | |
translateY = -translateY; | |
} | |
} | |
// 和最终目的地之间的距离就是移动的距离 | |
final int destinationPosOffset = isHorizontal() ? translateX : translateY; | |
// 采用动画方式显隐 | |
TranslateAnimation translateAnimation = new TranslateAnimation(translateX, 0, 0, translateY); | |
translateAnimation.setDuration(500); | |
translateAnimation.setFillAfter(true); | |
translateAnimation.setInterpolator(new LinearInterpolator()); | |
translateAnimation.setAnimationListener(new AnimationListener() { | |
@Override | |
public void onAnimationStart(Animation animation) {} | |
@Override | |
public void onAnimationRepeat(Animation animation) {} | |
@Override | |
public void onAnimationEnd(Animation animation) { | |
mView.clearAnimation(); | |
shiftView(destinationPosOffset); | |
} | |
}); | |
mView.startAnimation(shownAnimation); | |
default: | |
break; | |
} | |
} | |
/** | |
* 移动View | |
* @param offset 指定的位移 | |
*/ | |
private void shiftView(int offset){ | |
if(isHorizontal()){ | |
// 左右 | |
shiftXBasedOnLeftOffset(mView, offset); | |
} | |
else { | |
// 上下 | |
shiftYBasedOnTopOffset(mView, offset); | |
} | |
} | |
/** | |
* 判断是否水平方向移动 | |
* @return 返回True,如果是左右方向显隐 | |
*/ | |
private boolean isHorizontal(){ | |
return mOrientation == Orientation.LEFT || mOrientation == Orientation.RIGHT; | |
} | |
/** | |
* 判断View是否顺着touch移动方向而动 | |
* @return 返回True,如果是上方或者左方显隐 | |
*/ | |
private boolean followsTouchDirection(){ | |
return mOrientation == Orientation.TOP || mOrientation == Orientation.LEFT; | |
} | |
/** | |
* 移动view到指定的电,基于横纵轴位置 | |
* @param view | |
* @param toX | |
* @param toY | |
*/ | |
public static void shiftXYBasedOnLeftTop(View view, int toX, int toY){ | |
view.layout(toX, toY, toX + view.getWidth(), toY + view.getHeight()); | |
} | |
/** | |
* 移动view到指定的点,基于纵轴位移 | |
* @param view | |
* @param toX | |
*/ | |
public static void shiftYBasedOnTopOffset(View view, int offsetY) { | |
view.layout(view.getLeft(), view.getTop() + offsetY, view.getRight(), view.getTop() + view.getHeight() + offsetY); | |
} | |
/** | |
* 移动view到指定的点,基于纵轴上侧位置 | |
* @param view | |
* @param toX | |
*/ | |
public static void shiftYBasedOnTop(View view, int toY) { | |
view.layout(view.getLeft(), toY, view.getRight(), view.getHeight() + toY); | |
} | |
/** | |
* 移动view到指定的点,基于横轴位移 | |
* @param view | |
* @param toX | |
*/ | |
public static void shiftXBasedOnLeftOffset(View view, int offsetX) { | |
view.layout(view.getLeft() + offsetX, view.getTop(), view.getLeft() + view.getWidth() + offsetX, view.getBottom()); | |
} | |
/** | |
* 移动view到指定的点,基于横轴左侧位置 | |
* @param view | |
* @param toX | |
*/ | |
public static void shiftXBasedOnLeft(View view, int toX) { | |
view.layout(toX, view.getTop(), view.getWidth() + toX, view.getBottom()); | |
} | |
/** | |
* 移动view到指定的点,基于横轴右侧位置 | |
* | |
* @param view | |
* @param toX | |
*/ | |
public static void shiftXBasedOnRight(View view, int toX) { | |
view.layout(toX - view.getWidth(), view.getTop(), toX, view.getBottom()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment