过程分析
当下很多应用实现了这样一个效果,可以从窗口侧边划出隐藏的部分视图,划出过程中背景色逐渐改变,官方的BottomSheet就实现了这样一个效果。但毕竟官方代码出于健壮性和安全性等的考虑,
api
设计的较为复杂,今天我们就尝试自己实现一下。为了贯彻”熔岩流”精神,我们先上效果图:
废话不多说了,来看下实现吧。
代码实现
SideMenu
类中包含了主要的逻辑,其他的代码较为简单:
package com.ajay.sidemenu.view;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.res.Resources;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
/**
* desc: The menu support above and below.
* author:AjayNiu
* date:2016/11/12
*/
public class SideMenu {
public static final int DIRECTION_TOP = 1;
public static final int DIRECTION_BOTTOM = 2;
private static final int ALPHA_SPAN = 200;
private static final int ANIM_DURATION = 250;
private int screenHeight;
private int mStartY;
private int mEndY;
private View mMenu;
private int mDirection;
private View overlapView;
private boolean isMenuVisible;
private Runnable runnable = new Runnable() {
@Override
public void run() {
alignPosition(mDirection);
}
};
public void init(Activity activity, View menu, int direction) {
mMenu = menu;
mDirection = direction;
screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
overlapView = new View(activity);
overlapView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, ViewGroup.LayoutParams.MATCH_PARENT));
FrameLayout rootLayout = (FrameLayout) activity.getWindow().getDecorView();
if (direction == DIRECTION_TOP) {
rootLayout = (FrameLayout) activity.getWindow().getDecorView().findViewById(android.R.id.content);
} else if (direction == DIRECTION_BOTTOM) {
rootLayout = (FrameLayout) activity.getWindow().getDecorView();
}
rootLayout.addView(overlapView);
rootLayout.addView(menu);
menu.post(runnable);
// touch event
overlapView.setOnTouchListener(new MenuTouchEventListener());
}
public void toggle() {
if (isMenuVisible) {
hideMenu();
} else {
showMenu();
}
isMenuVisible = !isMenuVisible;
}
public boolean isMenuVisible() {
return isMenuVisible;
}
private void alignPosition(int direction) {
if (direction == DIRECTION_BOTTOM) {
mMenu.setTranslationY(screenHeight);
mStartY = screenHeight;
mEndY = screenHeight - mMenu.getHeight();
} else if (direction == DIRECTION_TOP) {
mMenu.setTranslationY(-mMenu.getHeight());
mStartY = -mMenu.getHeight();
mEndY = 0;
}
}
private void showMenu() {
ObjectAnimator translationY = ObjectAnimator.ofFloat(mMenu, View.TRANSLATION_Y, mStartY, mEndY);
translationY.setDuration(ANIM_DURATION);
translationY.setInterpolator(new FastOutSlowInInterpolator());
translationY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
int color = (int) ((fraction) * ALPHA_SPAN) * 0x01000000;
overlapView.setBackgroundColor(color);
}
});
translationY.start();
}
private void hideMenu() {
ObjectAnimator translationY = ObjectAnimator.ofFloat(mMenu, View.TRANSLATION_Y, mEndY, mStartY);
translationY.setDuration(ANIM_DURATION);
translationY.setInterpolator(new FastOutSlowInInterpolator());
translationY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
int color = (int) ((1 - fraction) * ALPHA_SPAN) * 0x01000000;
overlapView.setBackgroundColor(color);
}
});
translationY.start();
}
private class MenuTouchEventListener implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isMenuVisible) {
hideMenu();
isMenuVisible = false;
}
return false;
}
}
}