|
| 1 | +SlidingMenu |
| 2 | +=== |
| 3 | + |
| 4 | +先看一下图片 |
| 5 | +![image]{https://github.com/CharonChui/AndroidNote/blob/master/Pic/slidingmenu_1.png?raw=true} |
| 6 | +![image]{https://github.com/CharonChui/AndroidNote/blob/master/Pic/slidingmenu_2.png?raw=true} |
| 7 | +![image]{https://github.com/CharonChui/AndroidNote/blob/master/Pic/slidingmenu_3.png?raw=true} |
| 8 | + |
| 9 | +原理 |
| 10 | +--- |
| 11 | + |
| 12 | +`SlidingMenu`无非就是一个包含三个`View`的控件,**左边View**、**中间View(默认时全屏)**、**右边View**,默认的情况下中间`View`会把两边的`View`覆盖住,在手指滑动的时候,会根据手指的滑动方向以及滑动距离去移动中间的那个`View`,从而能让两边`View`完全可见。 |
| 13 | +在定义该View的时候,首先会想到继承`RelativeLayout`,能简单的实现这种左、中、右三个View的布局。 |
| 14 | + |
| 15 | +1. 继承RelativeLayout |
| 16 | +```java |
| 17 | + public class SlidingMenu extends RelativeLayout { |
| 18 | + |
| 19 | + public SlidingMenu(Context context, AttributeSet attrs, int defStyle) { |
| 20 | + super(context, attrs, defStyle); |
| 21 | + init(context); |
| 22 | + } |
| 23 | + |
| 24 | + public SlidingMenu(Context context, AttributeSet attrs) { |
| 25 | + super(context, attrs); |
| 26 | + init(context); |
| 27 | + } |
| 28 | + |
| 29 | + public SlidingMenu(Context context) { |
| 30 | + super(context); |
| 31 | + init(context); |
| 32 | + } |
| 33 | + |
| 34 | + private void init(Context context) { |
| 35 | + mContext = context; |
| 36 | + mScroller = new Scroller(context); |
| 37 | + mWindowWidth = getWindowWidth(context); |
| 38 | + } |
| 39 | + } |
| 40 | +``` |
| 41 | + |
| 42 | +2. 具体的三个View需要暴露给外界调用,所以我们要提供一个setView()的方法。 |
| 43 | +```java |
| 44 | + public void setView(View leftView, View rightView, View centerView, |
| 45 | + int leftViewWidth, int rightViewWidth) { |
| 46 | + //添加左边View |
| 47 | + RelativeLayout.LayoutParams leftParams = new LayoutParams( |
| 48 | + (int) convertDpToPixel(leftViewWidth, mContext), |
| 49 | + LayoutParams.MATCH_PARENT); |
| 50 | + leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); |
| 51 | + addView(leftView, leftParams); |
| 52 | + |
| 53 | + //右边的View |
| 54 | + RelativeLayout.LayoutParams rightParams = new LayoutParams( |
| 55 | + (int) convertDpToPixel(rightViewWidth, mContext), |
| 56 | + LayoutParams.MATCH_PARENT); |
| 57 | + rightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); |
| 58 | + addView(rightView, rightParams); |
| 59 | + |
| 60 | + //添加中间的View |
| 61 | + RelativeLayout.LayoutParams centerParams = new LayoutParams( |
| 62 | + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); |
| 63 | + addView(centerView, centerParams); |
| 64 | + |
| 65 | + mLeftView = leftView; |
| 66 | + mRightView = rightView; |
| 67 | + mCenterView = centerView; |
| 68 | + } |
| 69 | +``` |
| 70 | +外界使用`SlidingMenu`类的时候需要首先调用该方法去设置相应的View,一旦调用该方法后,我们就将布局设置完了,下一步就是对`touch`事件进行处理,然后去移动中间的View。 |
| 71 | + |
| 72 | +3. 处理Touch事件 |
| 73 | +在手指按下的时候,我们去控制两边View的显示与隐藏 |
| 74 | +```java |
| 75 | + public boolean onInterceptTouchEvent(MotionEvent ev) { |
| 76 | + int x = (int) ev.getRawX(); |
| 77 | + int y = (int) ev.getRawY(); |
| 78 | + |
| 79 | + int action = ev.getAction(); |
| 80 | + |
| 81 | + switch (action) { |
| 82 | + case MotionEvent.ACTION_DOWN: |
| 83 | + mLastPostionX = x; |
| 84 | + mLastPostionY = y; |
| 85 | + //通过变量记录当前可以显示左边的View还是可以显示右边的View |
| 86 | + if (mCanLeftViewShow) { |
| 87 | + //如果当前,中间的View往右滑,那么这时候左边的View就要能显示了 |
| 88 | + mLeftView.setVisibility(View.VISIBLE); |
| 89 | + mRightView.setVisibility(View.GONE); |
| 90 | + } else if (mCanRightViewShow) { |
| 91 | + mLeftView.setVisibility(View.GONE); |
| 92 | + mRightView.setVisibility(View.VISIBLE); |
| 93 | + } |
| 94 | + |
| 95 | + break; |
| 96 | + case MotionEvent.ACTION_MOVE: |
| 97 | + |
| 98 | + break; |
| 99 | + case MotionEvent.ACTION_UP: |
| 100 | + |
| 101 | + break; |
| 102 | + |
| 103 | + default: |
| 104 | + break; |
| 105 | + } |
| 106 | + |
| 107 | + return false; |
| 108 | + } |
| 109 | +``` |
| 110 | +在`onTouch()`中,我们去获取手指移动的距离 |
| 111 | +```java |
| 112 | + public boolean onTouchEvent(MotionEvent event) { |
| 113 | + int x = (int) event.getRawX(); |
| 114 | + int y = (int) event.getRawY(); |
| 115 | + |
| 116 | + int action = event.getAction(); |
| 117 | + switch (action) { |
| 118 | + case MotionEvent.ACTION_DOWN: |
| 119 | + mLastPostionX = x; |
| 120 | + mLastPostionY = y; |
| 121 | + |
| 122 | + if (!mScroller.isFinished()) { |
| 123 | + mScroller.abortAnimation(); |
| 124 | + } |
| 125 | + |
| 126 | + break; |
| 127 | + case MotionEvent.ACTION_MOVE: |
| 128 | + int distance = x - mLastPostionX; |
| 129 | + int targetPositon = mCenterView.getScrollX() - distance; |
| 130 | + mLastPostionX = x; |
| 131 | + |
| 132 | + if (mCanLeftViewShow) { |
| 133 | + if (targetPositon > 0) { |
| 134 | + targetPositon = 0; |
| 135 | + } |
| 136 | + |
| 137 | + if (targetPositon < -mLeftViewWidth) { |
| 138 | + targetPositon = -mLeftViewWidth; |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + if (mCanRightViewShow) { |
| 143 | + if (targetPositon < 0) { |
| 144 | + targetPositon = 0; |
| 145 | + } |
| 146 | + |
| 147 | + if (targetPositon > mRightViewWidth) { |
| 148 | + targetPositon = mRightViewWidth; |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + mClicked = false; |
| 153 | + //让中间的View随着手指的移动而移动 |
| 154 | + mCenterView.scrollTo(targetPositon, 0); |
| 155 | + |
| 156 | + break; |
| 157 | + case MotionEvent.ACTION_UP: |
| 158 | + //你手指移动后抬起的时候需要注意,如果现在左边的View已经超过一半可见了,这时候就算你抬起手指了,SlidingMenu也要滑动到右边让左边View完全可见。当然还有就是你滑动的飞快,然后突然抬起了手指,这时候就要进行速率的计算了,我们先不说速率 |
| 159 | + int dx = 0; |
| 160 | + if (mCanLeftViewShow) { |
| 161 | + if (mCenterView.getScrollX() <= -mLeftViewWidth / 2) { |
| 162 | + //已经超过左边View的一般了,应该让中间View继续移动,移动到左边View完全可见 |
| 163 | + dx = -mLeftViewWidth - mCenterView.getScrollX(); |
| 164 | + } else { |
| 165 | + // 滚回原来的位置 |
| 166 | + dx = -mCenterView.getScrollX(); |
| 167 | + resumeLeftViewClickState(); |
| 168 | + } |
| 169 | + |
| 170 | + } else if (mCanRightViewShow) { |
| 171 | + if (mCenterView.getScrollX() >= mRightViewWidth / 2) { |
| 172 | + dx = mRightViewWidth - mCenterView.getScrollX(); |
| 173 | + } else { |
| 174 | + dx = -mCenterView.getScrollX(); |
| 175 | + resumeRightViewClickState(); |
| 176 | + } |
| 177 | + } |
| 178 | + //手指抬起后,要让中间View有过程的滑过去,所以要用到Scroller类 |
| 179 | + smoothScrollTo(dx); |
| 180 | + break; |
| 181 | + |
| 182 | + default: |
| 183 | + break; |
| 184 | + } |
| 185 | + |
| 186 | + return true; |
| 187 | + } |
| 188 | +``` |
| 189 | +`Scroller`的实现 |
| 190 | +```java |
| 191 | + |
| 192 | + private void smoothScrollTo(int distance) { |
| 193 | + mScroller.startScroll(mCenterView.getScrollX(), 0, distance, 0, |
| 194 | + sDuration); |
| 195 | + invalidate(); |
| 196 | + } |
| 197 | + |
| 198 | + @Override |
| 199 | + public void computeScroll() { |
| 200 | + if (mScroller.computeScrollOffset()) { |
| 201 | + mCenterView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); |
| 202 | + postInvalidate(); |
| 203 | + } |
| 204 | + } |
| 205 | +``` |
| 206 | + |
| 207 | +4. 到这里SlidingMenu的大体实现已经完成了 |
| 208 | +剩下的就是对速率的计算,已经添加显示左边与显示右边的View的按钮。当左边View完全显示的时候,点击中间View可见部分时需要让中间View全屏。至于这些细节的东西就不再仔细说了,大家自己看源码吧。 |
| 209 | + |
| 210 | +------------------------------------------ |
| 211 | + |
| 212 | +- 邮箱 :charon.chui@gmail.com |
| 213 | +- Good Luck! |
0 commit comments