Skip to content

Commit 5854d3a

Browse files
committed
Add Scroller类简介.md adn SlidingMenu.md
1 parent ce8fd48 commit 5854d3a

File tree

5 files changed

+264
-11
lines changed

5 files changed

+264
-11
lines changed

.DS_Store

-6 KB
Binary file not shown.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/.project
2+
/.DS_Store
3+
/.DS_Store

.project

Lines changed: 0 additions & 11 deletions
This file was deleted.

Scroller类简介.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
Scroller
2+
===
3+
`SlidingMenu`项目中为了实现控件的滑动,需要用到`Scroller`类来实现缓慢的滑动过程,至于有人说View类可以直接调用`scrollTo()`方法,这里`scrollTo()`方法也能实现移动,但是它的移动是很快一下子就移过去了,就像穿越一样,直接从现实回到了过去,而`Scroller`类能够实现过程的移动。可以理解为一步步的走。
4+
5+
1. 查看Scroller源码
6+
```java
7+
public class Scroller {
8+
//...
9+
}
10+
```
11+
发现`Scroller`类并不是`View`的子类,只是一个普通的类,这个类中封装了滚动的操作,记录了滚动的位置以及时间等。
12+
该类有两个重要的方法:
13+
- `computeScrollOffset()`:
14+
文档的说明为`Call this when you want to know the new location.`查看源码可以发现,如果在移动到指定位置后就会返回false.正在移动的过程中返回true。
15+
- `startScroll()`:
16+
该方法的内部实现,并没有具体的移动方法,而是设置了一些移动所需的数据,包括移动持续的时间、开始位置、结束位置等。从而我们可以知道调用`Scroller.startScroll()`方法并没有真正的移动,而是设置了一些数据。
17+
2. `Scroller.startScoll()`是如何与`View`的移动相关联呢?在`View`的源码中:
18+
```java
19+
/**
20+
* Called by a parent to request that a child update its values for mScrollX
21+
* and mScrollY if necessary. This will typically be done if the child is
22+
* animating a scroll using a {@link android.widget.Scroller Scroller}
23+
* object.
24+
*/
25+
public void computeScroll() {
26+
}
27+
```
28+
通过注释我们可以看到该方法又父类调用根据滚动的值去更新View,在使用`Scroller`的时候通常都要实现该方法。来达到子View的滚动效果。
29+
继续往下跟发现在`draw()`方法中回去调用`computeScroll()`,而`draw()`方法会在父布局调用`drawChild()`的时候使用。
30+
31+
3. 具体关联
32+
通过上面两步大体能得到`Scroller``View`的移动要通过`computeScroll()`来完成,但是在究竟如何进行代码实现。
33+
`Scroller.startScroll()`方法被调用后会储存要滚动的 起始位置、结束位置、持续时间。所以我们可以在`computeScroll()`方法中去判断一下当前是否已经滚动完成,如果没有滚动完成,我们就去不断的获取当前`Scroller的位置`,根据这个位置,来把相应的`View`移动到这里。
34+
```java
35+
public void computeScroll() {
36+
if (mScroller.computeScrollOffset()) {
37+
//如果还没有滚动完成,我们就去让当前的View移动到指定位置去
38+
mCenterView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
39+
//移动完后,我们应该继续调用computeScoll方法去获取并且移动当前View。所以我们调用invalidate方法去请求重绘,这样父类就会调用computeScroll
40+
postInvalidate();
41+
}
42+
}
43+
```
44+
45+
------------------------------------------
46+
47+
- 邮箱 :charon.chui@gmail.com
48+
- Good Luck!

SlidingMenu.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
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

Comments
 (0)