編輯:關於Android編程
原創博客地址:點擊傳送
AndroidResideMenu
先看看如何使用:
把項目源碼下載下來導入工程,可以看到
ResideMenu為引用工程,再看看如何使用這個引用工程來構建出ResideMenu,
1.先new一個ResideMenu對象
resideMenu = new ResideMenu(this);2.設置它的背景圖片
resideMenu.setBackground(R.drawable.menu_background);3.綁定當前Activity
resideMenu.attachToActivity(this);4.設置監聽
resideMenu.setMenuListener(menuListener);可以監聽菜單打開和關閉狀態
private ResideMenu.OnMenuListener menuListener = new ResideMenu.OnMenuListener() { @Override public void openMenu() { Toast.makeText(mContext, Menu is opened!, Toast.LENGTH_SHORT).show(); } @Override public void closeMenu() { Toast.makeText(mContext, Menu is closed!, Toast.LENGTH_SHORT).show(); } };5.設置內容縮放比例(0.1~1f)
//valid scale factor is between 0.0f and 1.0f. leftmenu'width is 150dip. resideMenu.setScaleValue(0.6f);6.創建子菜單
// create menu items; itemHome = new ResideMenuItem(this, R.drawable.icon_home, Home); itemProfile = new ResideMenuItem(this, R.drawable.icon_profile, Profile); itemCalendar = new ResideMenuItem(this, R.drawable.icon_calendar, Calendar); itemSettings = new ResideMenuItem(this, R.drawable.icon_settings, Settings);7.設置點擊事件及將剛創建的子菜單添加到側換菜單中(可以看到它是通過常量來控制子菜單的添加位置)
itemHome.setOnClickListener(this); itemProfile.setOnClickListener(this); itemCalendar.setOnClickListener(this); itemSettings.setOnClickListener(this); resideMenu.addMenuItem(itemHome, ResideMenu.DIRECTION_LEFT); resideMenu.addMenuItem(itemProfile, ResideMenu.DIRECTION_LEFT); resideMenu.addMenuItem(itemCalendar, ResideMenu.DIRECTION_RIGHT); resideMenu.addMenuItem(itemSettings, ResideMenu.DIRECTION_RIGHT);8.設置title按鈕的點擊事件,設置左右菜單的開關
// You can disable a direction by setting -> // resideMenu.setSwipeDirectionDisable(ResideMenu.DIRECTION_RIGHT); findViewById(R.id.title_bar_left_menu).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { resideMenu.openMenu(ResideMenu.DIRECTION_LEFT); } }); findViewById(R.id.title_bar_right_menu).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { resideMenu.openMenu(ResideMenu.DIRECTION_RIGHT); } });9.還重寫了dispatchTouchEvent
@Override public boolean dispatchTouchEvent(MotionEvent ev) { return resideMenu.dispatchTouchEvent(ev); }10.菜單關閉方法
resideMenu.closeMenu();
11.屏蔽菜單方法
// You can disable a direction by setting -> // resideMenu.setSwipeDirectionDisable(ResideMenu.DIRECTION_RIGHT);
使用方法已經說完了,接下來,看看它的源碼,先看看源碼的項目結構。
很多人初學者都曾糾結,看源碼,如何從何看起,我個人建議從上面使用的順序看起,並且在看的時候要帶個問題去看去思考,這樣更容易理解。
上面的第一步是,創建ResideMenu對象,我們就看看ResideMenu的構造。
public ResideMenu(Context context) { super(context); initViews(context); }從上面代碼,看到構造裡面就一個初始化view,思考問題:如何初始化view及初始化了什麼view。
private void initViews(Context context){ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.residemenu, this); scrollViewLeftMenu = (ScrollView) findViewById(R.id.sv_left_menu); scrollViewRightMenu = (ScrollView) findViewById(R.id.sv_right_menu); imageViewShadow = (ImageView) findViewById(R.id.iv_shadow); layoutLeftMenu = (LinearLayout) findViewById(R.id.layout_left_menu); layoutRightMenu = (LinearLayout) findViewById(R.id.layout_right_menu); imageViewBackground = (ImageView) findViewById(R.id.iv_background); }原理分析:從上面的代碼可以看到,加載了一個residemenu的布局,先看布局
<framelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">布局顯示效果</framelayout>
從布局文件,以及顯示效果我們可以看到,它是一個幀布局,第一個ImageView是背景,第二個ImageView是.9的陰影效果的圖片(看下面的圖),
兩個(ScrollView包裹著一個LinerLayout),可以從上面圖看到結構分別是左菜單和右菜單
1.初始化布局以及布局文件分析完畢,2.接下來是設置背景圖,初始化view的時候就已經拿到了背景控件,所以設置背景圖也是非常好實現的事情了。
public void setBackground(int imageResrouce){ imageViewBackground.setImageResource(imageResrouce); }3.綁定activity,思考問題:它做了什麼?
/** * use the method to set up the activity which residemenu need to show; * * @param activity */ public void attachToActivity(Activity activity){ initValue(activity); setShadowAdjustScaleXByOrientation(); viewDecor.addView(this, 0); setViewPadding(); }原理分析:綁定activity做了4件事情,分別是:
1.初始化參數:
private void initValue(Activity activity){ this.activity = activity; leftMenuItems = new ArrayList(); rightMenuItems = new ArrayList (); ignoredViews = new ArrayList (); viewDecor = (ViewGroup) activity.getWindow().getDecorView(); viewActivity = new TouchDisableView(this.activity); View mContent = viewDecor.getChildAt(0); viewDecor.removeViewAt(0); viewActivity.setContent(mContent); addView(viewActivity); ViewGroup parent = (ViewGroup) scrollViewLeftMenu.getParent(); parent.removeView(scrollViewLeftMenu); parent.removeView(scrollViewRightMenu); }
2.正對橫豎屏縮放比例進行調整
private void setShadowAdjustScaleXByOrientation(){ int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { shadowAdjustScaleX = 0.034f; shadowAdjustScaleY = 0.12f; } else if (orientation == Configuration.ORIENTATION_PORTRAIT) { shadowAdjustScaleX = 0.06f; shadowAdjustScaleY = 0.07f; } }
3.添加當前view
viewDecor.addView(this, 0);
4.設置view邊距
/** * we need the call the method before the menu show, because the * padding of activity can't get at the moment of onCreateView(); */ private void setViewPadding(){ this.setPadding(viewActivity.getPaddingLeft(), viewActivity.getPaddingTop(), viewActivity.getPaddingRight(), viewActivity.getPaddingBottom()); }4.設置監聽,思考問題:它什麼時候調用監聽,原理分析:動畫監聽開始執行動畫掉哦那個openMenu動畫結束調用closeMenu,從此我們可以想到,但它調用openMenu(int direction)和closeMenu()都會設置這個監聽。
private Animator.AnimatorListener animationListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { if (isOpened()){ showScrollViewMenu(); if (menuListener != null) menuListener.openMenu(); } } @Override public void onAnimationEnd(Animator animation) { // reset the view; if(isOpened()){ viewActivity.setTouchDisable(true); viewActivity.setOnClickListener(viewActivityOnClickListener); }else{ viewActivity.setTouchDisable(false); viewActivity.setOnClickListener(null); hideScrollViewMenu(); if (menuListener != null) menuListener.closeMenu(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } };5.設置內容縮放比例(0.1~1f),細心的同學會發現在當縮完成後還可以在往裡面拉到更小,有種彈性的感覺,挺有趣的。但是有些人的需求不想要有這種彈性效果,我們可以通過修改源碼修改這個彈性效果,找到getTargetScale這個方法,修改下面0.5這個數值。使用時設置了0.6的縮放比例,默認下面的彈性參數是0.5所以我們當縮完成後還可以在往裡面拉0.1的比例。
private float getTargetScale(float currentRawX){ float scaleFloatX = ((currentRawX - lastRawX) / getScreenWidth()) * 0.75f; scaleFloatX = scaleDirection == DIRECTION_RIGHT ? - scaleFloatX : scaleFloatX; float targetScale = ViewHelper.getScaleX(viewActivity) - scaleFloatX; targetScale = targetScale > 1.0f ? 1.0f : targetScale; targetScale = targetScale < 0.5f ? 0.5f : targetScale; return targetScale; }
默認縮放比例:
//valid scale factor is between 0.0f and 1.0f. private float mScaleValue = 0.5f;
AnimatorSet scaleDown_activity = buildScaleDownAnimation(viewActivity, mScaleValue, mScaleValue);
/** * 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; }6.創建子菜單,看下子菜單的構造,我們通過上面的學習,原理分析:我們可以猜測到,無非就是加載布局設置內容
public ResideMenuItem(Context context, int icon, String title) { super(context); initViews(context); iv_icon.setImageResource(icon); tv_title.setText(title); } private void initViews(Context context){ LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.residemenu_item, this); iv_icon = (ImageView) findViewById(R.id.iv_icon); tv_title = (TextView) findViewById(R.id.tv_title); }布局文件:
顯示效果圖:
7.子菜單添加到側換菜單中(可以看到它是通過常量來控制子菜單的添加位置)原理分析:根據不同的常量來區分添加不同菜單的子菜單
/** * add a single items; * * @param menuItem * @param direction */ public void addMenuItem(ResideMenuItem menuItem, int direction){ if (direction == DIRECTION_LEFT){ this.leftMenuItems.add(menuItem); layoutLeftMenu.addView(menuItem); }else{ this.rightMenuItems.add(menuItem); layoutRightMenu.addView(menuItem); } }8.設置title按鈕的點擊事件,設置左右菜單的開關,原理分析:先設置了縮放方向然後在設置動畫,正如我們上面想的一樣還設置了動畫監聽。
/** * show the reside menu; */ public void openMenu(int direction){ setScaleDirection(direction); isOpened = true; AnimatorSet scaleDown_activity = buildScaleDownAnimation(viewActivity, mScaleValue, mScaleValue); AnimatorSet scaleDown_shadow = buildScaleDownAnimation(imageViewShadow, mScaleValue + shadowAdjustScaleX, mScaleValue + shadowAdjustScaleY); AnimatorSet alpha_menu = buildMenuAnimation(scrollViewMenu, 1.0f); scaleDown_shadow.addListener(animationListener); scaleDown_activity.playTogether(scaleDown_shadow); scaleDown_activity.playTogether(alpha_menu); scaleDown_activity.start(); }設置縮放方向及計算x,y軸位置。
private void setScaleDirection(int direction){ int screenWidth = getScreenWidth(); float pivotX; float pivotY = getScreenHeight() * 0.5f; if (direction == DIRECTION_LEFT){ scrollViewMenu = scrollViewLeftMenu; pivotX = screenWidth * 1.5f; }else{ scrollViewMenu = scrollViewRightMenu; pivotX = screenWidth * -0.5f; } ViewHelper.setPivotX(viewActivity, pivotX); ViewHelper.setPivotY(viewActivity, pivotY); ViewHelper.setPivotX(imageViewShadow, pivotX); ViewHelper.setPivotY(imageViewShadow, pivotY); scaleDirection = direction; }9.重寫dispatchTouchEvent,問題思考:如何到根據手指滑動自動縮放
如果還不了解,dispatchTouchEvent這個函數如何調用?什麼時候調用?請先看看http://blog.csdn.net/cym492224103/article/details/39179311
@Override public boolean dispatchTouchEvent(MotionEvent ev) { float currentActivityScaleX = ViewHelper.getScaleX(viewActivity); if (currentActivityScaleX == 1.0f) setScaleDirectionByRawX(ev.getRawX()); switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: lastActionDownX = ev.getX(); lastActionDownY = ev.getY(); isInIgnoredView = isInIgnoredView(ev) && !isOpened(); pressedState = PRESSED_DOWN; break; case MotionEvent.ACTION_MOVE: if (isInIgnoredView || isInDisableDirection(scaleDirection)) break; if(pressedState != PRESSED_DOWN && pressedState != PRESSED_MOVE_HORIZANTAL) break; int xOffset = (int) (ev.getX() - lastActionDownX); int yOffset = (int) (ev.getY() - lastActionDownY); if(pressedState == PRESSED_DOWN) { if(yOffset > 25 || yOffset < -25) { pressedState = PRESSED_MOVE_VERTICAL; break; } if(xOffset < -50 || xOffset > 50) { pressedState = PRESSED_MOVE_HORIZANTAL; ev.setAction(MotionEvent.ACTION_CANCEL); } } else if(pressedState == PRESSED_MOVE_HORIZANTAL) { if (currentActivityScaleX < 0.95) showScrollViewMenu(); float targetScale = getTargetScale(ev.getRawX()); ViewHelper.setScaleX(viewActivity, targetScale); ViewHelper.setScaleY(viewActivity, targetScale); ViewHelper.setScaleX(imageViewShadow, targetScale + shadowAdjustScaleX); ViewHelper.setScaleY(imageViewShadow, targetScale + shadowAdjustScaleY); ViewHelper.setAlpha(scrollViewMenu, (1 - targetScale) * 2.0f); lastRawX = ev.getRawX(); return true; } break; case MotionEvent.ACTION_UP: if (isInIgnoredView) break; if (pressedState != PRESSED_MOVE_HORIZANTAL) break; pressedState = PRESSED_DONE; if (isOpened()){ if (currentActivityScaleX > 0.56f) closeMenu(); else openMenu(scaleDirection); }else{ if (currentActivityScaleX < 0.94f){ openMenu(scaleDirection); }else{ closeMenu(); } } break; } lastRawX = ev.getRawX(); return super.dispatchTouchEvent(ev); }上面代碼量有點多,看上去有點暈,接下來我們來分別從按下、移動、放開、來原理分析:
MotionEvent.ACTION_DOWN:
記錄了X,Y軸的坐標點,判斷是否打開,設置了按下的狀態為PRESSED_DOWN
MotionEvent.ACTION_MOVE:
拿到當前X,Y減去DOWN下記錄下來的X,Y,這樣得到了移動的X,Y,然後判斷如果如果移動的X,Y大於25或者小於-25就改變按下狀態為PRESSED_MOVE_VERTICAL
如果移動的X,Y大於50或者小於-50就改變狀態為PRESSED_MOVE_HORIZANTAL
狀態為PRESSED_MOVE_HORIZANTAL就改變菜單主視圖內容以及陰影圖片大小,在改變的同時還設置了當前菜單的透明度。
MotionEvent.ACTION_UP:
判斷是否菜單是否打開狀態,在獲取當前縮放的X比例,
判斷比例小於0.56f,則關閉菜單,反正開啟菜單。
看完後,我們在回去看看代碼,就會發現其實也不過如此~!
10.菜單關閉方法,同樣也設置了動畫監聽之前的想法也是成立的。
/** * close the reslide menu; */ public void closeMenu(){ isOpened = false; AnimatorSet scaleUp_activity = buildScaleUpAnimation(viewActivity, 1.0f, 1.0f); AnimatorSet scaleUp_shadow = buildScaleUpAnimation(imageViewShadow, 1.0f, 1.0f); AnimatorSet alpha_menu = buildMenuAnimation(scrollViewMenu, 0.0f); scaleUp_activity.addListener(animationListener); scaleUp_activity.playTogether(scaleUp_shadow); scaleUp_activity.playTogether(alpha_menu); scaleUp_activity.start(); }11.屏蔽菜單方法
public void setSwipeDirectionDisable(int direction){ disabledSwipeDirection.add(direction); }
private boolean isInDisableDirection(int direction){ return disabledSwipeDirection.contains(direction); }原理分析:在重寫dispatchTouchEvent的時候,細心的同學應該會看到,ACTION_MOVE下面有個判斷
if (isInIgnoredView || isInDisableDirection(scaleDirection))如果這個方向的菜單被屏蔽了,就滑不出來了。
最後我們會發現我們一直都沒說到TouchDisableView,其實initValue的時候就初始化了,它就是viewActivity,是我們的內容視圖。
我們來看看它做了什麼?
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width); final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height); mContent.measure(contentWidth, contentHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = r - l; final int height = b - t; mContent.layout(0, 0, width, height); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mTouchDisabled; } void setTouchDisable(boolean disableTouch) { mTouchDisabled = disableTouch; } boolean isTouchDisabled() { return mTouchDisabled; }動態設置寬高,設置事件是否傳遞下去的flag。
好了,源碼分析已完畢,喜歡這篇文章的就請關注我吧~!
簡介: 一般我們在自定義ViewGroup 的時候會通常都會用到onInterceptTouchEvent ,onTouchEvent 這些方法去進行距離的判斷然後利用s
如果是自己開發的板子,需要用GPIO引腳控制3G模塊開機/關機時,下面的文章會對你有所幫助,是以處理器IMX6和中興MG3732模塊為例介紹。 一、引腳連接
剛剛買回了手機,手機裡面或多或少都會帶有一些本身的系統軟件,有些軟件很少用,但是卻很占內存,想必很多人選擇ROOT的直接原因就是系統自帶軟件太多了,不僅占用
微信開放平台和公眾平台的區別1.公眾平台面向的時普通的用戶,比如自媒體和媒體,企業官方微信公眾賬號運營人員使用,當然你所在的團隊或者公司有實力去開發一些內容,也可以調用公