这个源码十分简单,没啥可说的.只有一点提一下,看到有些控件达到背景色的效果,是动态填加一个带背景色父布局实现的.
我这里是在画布上动态绘制的.
GitHub 地址: TranslationView
源码如下:
public class TranslationView extends FrameLayout {
private static final String TAG = "TranslationView";
private static final int DEFAULT_COLOR = 0x50000000;
private int mShadowColor = DEFAULT_COLOR;
private boolean mIsShow = false;
private View mTranslationView;
private ObjectAnimator mShowAni;
private ObjectAnimator mHideAni;
public TranslationView(Context context) {
super(context, null);
}
public TranslationView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public TranslationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() != 2) {
throw new IllegalStateException("only and should contain two child view");
}
mTranslationView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d(TAG, "onMeasure");
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mTranslationView.layout(0, -mTranslationView.getHeight(), mTranslationView.getWidth(), 0);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (mIsShow && child == mTranslationView) {
canvas.drawColor(mShadowColor);
}
return super.drawChild(canvas, child, drawingTime);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
if (mIsShow && inShadow(ev)) {
hide();
return true;
}
}
}
return super.onInterceptTouchEvent(ev);
}
private boolean inShadow(MotionEvent ev) {
float x = ev.getX();
float y = ev.getY();
final float leftEdge = mTranslationView.getX();
final float rightEdge = leftEdge + mTranslationView.getWidth();
final float topEdge = mTranslationView.getHeight();
final float bottomEdge = getHeight() + topEdge;
return x > leftEdge && x < rightEdge && y > topEdge && y < bottomEdge;
}
public void show() {
if (!mIsShow) {
mIsShow = true;
if (mShowAni == null) {
mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), mTranslationView.getHeight());
mShowAni.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
invalidate();
}
});
}
mShowAni.start();
}
}
public void hide() {
if (mIsShow) {
mIsShow = false;
if (mHideAni == null) {
mHideAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), -mTranslationView.getHeight());
mHideAni.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
invalidate();
}
});
}
mHideAni.start();
}
}
public void setShadowColor(@ColorInt int color) {
mShadowColor = color;
}
}
勘误:之前写这个小控件的时候,代码是有些问题的,虽然有点歪打正着。当初写这个控件的时候控件的坐标计算都是按照绝对坐标去思考的,但其实 translationY 是相对原坐标移动了多少的一个相对概念,虽然之前也已经了解了相关概念,没想到写的时候还是犯了错误。这次因为别的需要,扩展功能的时候才发现错误。
2017/6/1更新:新增其他方向的滑动效果。
public class TranslationView extends FrameLayout {
public static final String START = "start";
public static final String END = "end";
public static final String TOP = "top";
public static final String BOTTOM = "bottom";
private static final String TAG = "TranslationView";
private static final int DEFAULT_COLOR = 0x50000000;
private String mDirection = TOP;
private int mShadowColor = DEFAULT_COLOR;
private int mW;
private int mH;
private boolean mIsShow = false;
private View mTranslationView;
private ObjectAnimator mShowAni;
private ObjectAnimator mHideAni;
public TranslationView(Context context) {
this(context, null);
}
public TranslationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TranslationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (attrs == null) {
return;
}
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TranslationView);
mDirection = a.getString(R.styleable.TranslationView_direction);
if (TextUtils.isEmpty(mDirection)) {
mDirection = TOP;
}
a.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() != 2) {
throw new IllegalStateException("only and should contain two child view");
}
mTranslationView = getChildAt(1);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mW = w;
mH = h;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
switch (mDirection) {
case TOP:
mTranslationView.layout(0, -mTranslationView.getHeight(), mTranslationView.getWidth(), 0);
break;
case BOTTOM:
mTranslationView.layout(0, mH, mTranslationView.getWidth(), mH + mTranslationView.getHeight());
break;
case START:
mTranslationView.layout(-mTranslationView.getWidth(), 0, 0, mTranslationView.getHeight());
break;
case END:
mTranslationView.layout(mW, 0, mW + mTranslationView.getWidth(), mTranslationView.getHeight());
break;
default:
break;
}
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (mIsShow && child == mTranslationView) {
canvas.drawColor(mShadowColor);
}
return super.drawChild(canvas, child, drawingTime);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
if (mIsShow && !inTranslationView(ev)) {
hide();
return true;
}
}
}
return super.onInterceptTouchEvent(ev);
}
/**
* 在内容区域中
*
* @param ev
* @return
*/
private boolean inTranslationView(MotionEvent ev) {
float x = ev.getX();
float y = ev.getY();
final float leftEdge = mTranslationView.getX();
final float rightEdge = leftEdge + mTranslationView.getWidth();
final float topEdge = mTranslationView.getY();
final float bottomEdge = mTranslationView.getHeight() + topEdge;
return x > leftEdge && x < rightEdge && y > topEdge && y < bottomEdge;
}
public void show() {
if (!mIsShow) {
mIsShow = true;
if (mShowAni == null) {
switch (mDirection) {
case TOP:
mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", 0, mTranslationView.getHeight());
break;
case BOTTOM:
mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", 0, -mTranslationView.getHeight());
break;
case START:
mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationX", 0, mTranslationView.getWidth());
break;
case END:
mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationX", 0, -mTranslationView.getWidth());
break;
default:
break;
}
mShowAni.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
invalidate();
}
});
}
mShowAni.start();
}
}
public void hide() {
if (mIsShow) {
mIsShow = false;
if (mHideAni == null) {
switch (mDirection) {
case TOP:
case BOTTOM:
mHideAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), 0);
break;
case START:
case END:
mHideAni = ObjectAnimator.ofFloat(mTranslationView, "translationX", mTranslationView.getTranslationX(), 0);
break;
}
mHideAni.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
invalidate();
}
});
}
mHideAni.start();
}
}
public void setShadowColor(@ColorInt int color) {
mShadowColor = color;
}
}
效果图如下:
2017.7.3 更新 v1.0.1