package cn.scau.scautreasure.widget; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.*; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ScrollView; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.AnimatorSet; import com.nineoldandroids.animation.ObjectAnimator; import com.nineoldandroids.view.ViewHelper; import java.util.ArrayList; import java.util.List; import cn.scau.scautreasure.R; /** * User: special * Date: 13-12-10 * Time: 下午10:44 * Mail: specialcyci@gmail.com */ public class ResideMenu extends FrameLayout implements GestureDetector.OnGestureListener{ public static final int DIRECTION_LEFT = 0; public static final int DIRECTION_RIGHT = 1; private ImageView iv_shadow; private ImageView iv_background; private LinearLayout layout_left_menu; private LinearLayout layout_right_menu; private ScrollView sv_left_menu; private ScrollView sv_right_menu; private ScrollView sv_menu; /** the activity that view attach to */ private Activity activity; /** the decorview of the activity */ private ViewGroup view_decor; /** the viewgroup of the activity */ private ViewGroup view_activity; /** the flag of main open status */ private boolean isOpened; private GestureDetector gestureDetector; private float shadow_AdjustScaleX; private float shadow_AdjustScaleY; /** the view which don't want to intercept touch event */ private List<View> ignoredViews; private List<ResideMenuItem> leftMenuItems; private List<ResideMenuItem> rightMenuItems; private DisplayMetrics displayMetrics = new DisplayMetrics(); private OnMenuListener menuListener; private float lastRawX; private boolean canScale = false; private int scaleDirection = DIRECTION_LEFT; private List<Integer> disabledDirection = new ArrayList<Integer>(); public ResideMenu(Context context) { super(context); initViews(context); } private void initViews(Context context){ LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.residemenu, this); sv_left_menu = (ScrollView) findViewById(R.id.sv_left_menu); sv_right_menu = (ScrollView) findViewById(R.id.sv_right_menu); iv_shadow = (ImageView) findViewById(R.id.iv_shadow); layout_left_menu = (LinearLayout) findViewById(R.id.layout_left_menu); layout_right_menu = (LinearLayout) findViewById(R.id.layout_right_menu); iv_background = (ImageView) findViewById(R.id.iv_background); } /** * use the method to set up the activity which residemenu need to show; * * @param activity */ public void attachToActivity(Activity activity){ initValue(activity); setShadowAdjustScaleXByOrientation(); view_decor.addView(this, 0); setViewPadding(); } private void initValue(Activity activity){ this.activity = activity; leftMenuItems = new ArrayList<ResideMenuItem>(); rightMenuItems = new ArrayList<ResideMenuItem>(); gestureDetector = new GestureDetector(this); ignoredViews = new ArrayList<View>(); view_decor = (ViewGroup) activity.getWindow().getDecorView(); view_activity = (ViewGroup) view_decor.getChildAt(0); } private void setShadowAdjustScaleXByOrientation(){ int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { shadow_AdjustScaleX = 0.034f; shadow_AdjustScaleY = 0.12f; } else if (orientation == Configuration.ORIENTATION_PORTRAIT) { shadow_AdjustScaleX = 0.06f; shadow_AdjustScaleY = 0.07f; } } /** * set the main background picture; * * @param imageResrouce */ public void setBackground(int imageResrouce){ iv_background.setImageResource(imageResrouce); } /** * the visiblity of shadow under the activity view; * * @param isVisible */ public void setShadowVisible(boolean isVisible){ if (isVisible) iv_shadow.setImageResource(R.drawable.shadow); else iv_shadow.setImageBitmap(null); } /** * add a single items to left main; * * @param menuItem */ @Deprecated public void addMenuItem(ResideMenuItem menuItem){ this.leftMenuItems.add(menuItem); layout_left_menu.addView(menuItem); } /** * add a single items; * * @param menuItem * @param direction */ public void addMenuItem(ResideMenuItem menuItem, int direction){ if (direction == DIRECTION_LEFT){ this.leftMenuItems.add(menuItem); layout_left_menu.addView(menuItem); }else{ this.rightMenuItems.add(menuItem); layout_right_menu.addView(menuItem); } } /** * set the main items by array list to left main; * * @param menuItems */ @Deprecated public void setMenuItems(List<ResideMenuItem> menuItems){ this.leftMenuItems = menuItems; rebuildMenu(); } /** * set the main items by array list; * * @param menuItems * @param direction */ public void setMenuItems(List<ResideMenuItem> menuItems, int direction){ if (direction == DIRECTION_LEFT) this.leftMenuItems = menuItems; else this.rightMenuItems = menuItems; rebuildMenu(); } private void rebuildMenu(){ layout_left_menu.removeAllViews(); layout_right_menu.removeAllViews(); for(int i = 0; i < leftMenuItems.size() ; i ++) layout_left_menu.addView(leftMenuItems.get(i), i); for(int i = 0; i < rightMenuItems.size() ; i ++) layout_right_menu.addView(rightMenuItems.get(i), i); } /** * get the left main items; * * @return */ @Deprecated public List<ResideMenuItem> getMenuItems() { return leftMenuItems; } /** * get the main items; * * @return */ public List<ResideMenuItem> getMenuItems(int direction) { if ( direction == DIRECTION_LEFT) return leftMenuItems; else return rightMenuItems; } /** * if you need to do something on the action of closing or opening * main, set the listener here. * * @return */ public void setMenuListener(OnMenuListener menuListener) { this.menuListener = menuListener; } public OnMenuListener getMenuListener() { return menuListener; } /** * we need the call the method before the main show, because the * padding of activity can't get at the moment of onCreateView(); */ private void setViewPadding(){ this.setPadding(view_activity.getPaddingLeft(), view_activity.getPaddingTop(), view_activity.getPaddingRight(), view_activity.getPaddingBottom()); } /** * show the reside main; */ public void openMenu(int direction){ if (isInDisableDirection(direction)) throw new IllegalArgumentException("You have set this direction disable, but now you want to open main in this direction."); setScaleDirection(direction); isOpened = true; AnimatorSet scaleDown_activity = buildScaleDownAnimation(view_activity, 0.5f, 0.5f); AnimatorSet scaleDown_shadow = buildScaleDownAnimation(iv_shadow, 0.5f + shadow_AdjustScaleX, 0.5f + shadow_AdjustScaleY); AnimatorSet alpha_menu = buildMenuAnimation(sv_menu, 1.0f); scaleDown_shadow.addListener(animationListener); scaleDown_activity.playTogether(scaleDown_shadow); scaleDown_activity.playTogether(alpha_menu); scaleDown_activity.start(); } /** * close the reslide main; */ public void closeMenu(){ isOpened = false; AnimatorSet scaleUp_activity = buildScaleUpAnimation(view_activity, 1.0f, 1.0f); AnimatorSet scaleUp_shadow = buildScaleUpAnimation(iv_shadow, 1.0f, 1.0f); AnimatorSet alpha_menu = buildMenuAnimation(sv_menu, 0.0f); scaleUp_activity.addListener(animationListener); scaleUp_activity.playTogether(scaleUp_shadow); scaleUp_activity.playTogether(alpha_menu); scaleUp_activity.start(); } public void setDirectionDisable(int direction){ disabledDirection.add(direction); } private boolean isInDisableDirection(int direction){ return disabledDirection.contains(direction); } private void setScaleDirection(int direction){ int screenWidth = getScreenWidth(); float pivotX; float pivotY = getScreenHeight() * 0.5f; if (direction == DIRECTION_LEFT){ sv_menu = sv_left_menu; pivotX = screenWidth * 1.5f; }else{ sv_menu = sv_right_menu; pivotX = screenWidth * -0.5f; } ViewHelper.setPivotX(view_activity, pivotX); ViewHelper.setPivotY(view_activity, pivotY); ViewHelper.setPivotX(iv_shadow, pivotX); ViewHelper.setPivotY(iv_shadow, pivotY); scaleDirection = direction; } /** * return the flag of main status; * * @return */ public boolean isOpened() { return isOpened; } private Animator.AnimatorListener animationListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { if (isOpened){ sv_menu.setVisibility(VISIBLE); if (menuListener != null) menuListener.openMenu(); } } @Override public void onAnimationEnd(Animator animation) { // reset the view; if(!isOpened){ sv_menu.setVisibility(GONE); if (menuListener != null) menuListener.closeMenu(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }; /** * a helper method to build scale down animation; * * @param target * @param targetScaleX * @param targetScaleY * @return */ private AnimatorSet buildScaleDownAnimation(View target,float targetScaleX,float targetScaleY){ AnimatorSet scaleDown = new AnimatorSet(); scaleDown.playTogether( ObjectAnimator.ofFloat(target, "scaleX", targetScaleX), ObjectAnimator.ofFloat(target, "scaleY", targetScaleY) ); scaleDown.setInterpolator(AnimationUtils.loadInterpolator(activity, android.R.anim.decelerate_interpolator)); scaleDown.setDuration(250); return scaleDown; } /** * a helper method to build scale up animation; * * @param target * @param targetScaleX * @param targetScaleY * @return */ private AnimatorSet buildScaleUpAnimation(View target,float targetScaleX,float targetScaleY){ AnimatorSet scaleUp = new AnimatorSet(); scaleUp.playTogether( ObjectAnimator.ofFloat(target, "scaleX", targetScaleX), ObjectAnimator.ofFloat(target, "scaleY", targetScaleY) ); scaleUp.setDuration(250); return scaleUp; } private AnimatorSet buildMenuAnimation(View target, float alpha){ AnimatorSet alphaAnimation = new AnimatorSet(); alphaAnimation.playTogether( ObjectAnimator.ofFloat(target, "alpha", alpha) ); alphaAnimation.setDuration(250); return alphaAnimation; } /** * if there ware some view you don't want reside main * to intercept their touch event,you can use the method * to set. * * @param v */ public void addIgnoredView(View v){ ignoredViews.add(v); } /** * remove the view from ignored view list; * @param v */ public void removeIgnoredView(View v){ ignoredViews.remove(v); } /** * clear the ignored view list; */ public void clearIgnoredViewList(){ ignoredViews.clear(); } /** * if the motion evnent was relative to the view * which in ignored view list,return true; * * @param ev * @return */ private boolean isInIgnoredView(MotionEvent ev) { Rect rect = new Rect(); for (View v : ignoredViews) { v.getGlobalVisibleRect(rect); if (rect.contains((int) ev.getX(), (int) ev.getY())) return true; } return false; } //-------------------------------------------------------------------------- // // GestureListener // //-------------------------------------------------------------------------- @Override public boolean onTouchEvent(MotionEvent event) { return gestureDetector.onTouchEvent(event); } private void setScaleDirectionByRawX(float currentRawX){ if (currentRawX < lastRawX) setScaleDirection(DIRECTION_RIGHT); else setScaleDirection(DIRECTION_LEFT); } private float getTargetScale(float currentRawX){ float scaleFloatX = ((currentRawX - lastRawX) / getScreenWidth()) * 0.75f; scaleFloatX = scaleDirection == DIRECTION_RIGHT ? - scaleFloatX : scaleFloatX; float targetScale = ViewHelper.getScaleX(view_activity) - scaleFloatX; targetScale = targetScale > 1.0f ? 1.0f : targetScale; targetScale = targetScale < 0.5f ? 0.5f : targetScale; return targetScale; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { float currentActivityScaleX = ViewHelper.getScaleX(view_activity); if (currentActivityScaleX == 1.0f) setScaleDirectionByRawX(ev.getRawX()); switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: canScale = !isInIgnoredView(ev); break; case MotionEvent.ACTION_MOVE: if (!canScale || isInDisableDirection(scaleDirection)) break; if (currentActivityScaleX < 0.95) sv_menu.setVisibility(VISIBLE); float targetScale = getTargetScale(ev.getRawX()); ViewHelper.setScaleX(view_activity, targetScale); ViewHelper.setScaleY(view_activity, targetScale); ViewHelper.setScaleX(iv_shadow, targetScale + shadow_AdjustScaleX); ViewHelper.setScaleY(iv_shadow, targetScale + shadow_AdjustScaleY); ViewHelper.setAlpha(sv_menu, (1 - targetScale) * 2.0f); break; case MotionEvent.ACTION_UP: if (!canScale) break; if (isOpened()){ if (currentActivityScaleX > 0.55f) closeMenu(); else openMenu(scaleDirection); }else if (!isOpened()){ if (currentActivityScaleX < 0.95f) openMenu(scaleDirection); else closeMenu(); } break; } lastRawX = ev.getRawX(); return super.onInterceptTouchEvent(ev); } @Override public boolean onDown(MotionEvent motionEvent) { return false; } @Override public void onShowPress(MotionEvent motionEvent) { } @Override public boolean onSingleTapUp(MotionEvent motionEvent) { return false; } @Override public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent2, float v, float v2) { return true; } @Override public void onLongPress(MotionEvent motionEvent) { } @Override public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent2, float v, float v2) { return false; } public int getScreenHeight(){ activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); return displayMetrics.heightPixels; } public int getScreenWidth(){ activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); return displayMetrics.widthPixels; } public interface OnMenuListener{ /** * the method will call on the finished time of opening main's animation. */ public void openMenu(); /** * the method will call on the finished time of closing main's animation . */ public void closeMenu(); } }