Skip to content

Commit b6e4f99

Browse files
author
liyong
committed
嵌套滑动demo
0 parents  commit b6e4f99

35 files changed

+1137
-0
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea
5+
.DS_Store
6+
/build
7+
/captures
8+
.externalNativeBuild

app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

app/build.gradle

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
apply plugin: 'com.android.application'
2+
3+
android {
4+
compileSdkVersion 28
5+
defaultConfig {
6+
applicationId "com.allen.android.nestedscrolldemo"
7+
minSdkVersion 19
8+
targetSdkVersion 26
9+
versionCode 1
10+
versionName "1.0"
11+
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12+
}
13+
buildTypes {
14+
release {
15+
minifyEnabled false
16+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17+
}
18+
}
19+
}
20+
21+
dependencies {
22+
implementation fileTree(include: ['*.jar'], dir: 'libs')
23+
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
24+
testImplementation 'junit:junit:4.12'
25+
androidTestImplementation 'com.android.support.test:runner:1.0.2'
26+
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
27+
implementation 'com.android.support:support-v4:28.0.0'
28+
}

app/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.allen.android.nestedscrolldemo;
2+
3+
import android.content.Context;
4+
import android.support.test.InstrumentationRegistry;
5+
import android.support.test.runner.AndroidJUnit4;
6+
7+
import org.junit.Test;
8+
import org.junit.runner.RunWith;
9+
10+
import static org.junit.Assert.*;
11+
12+
/**
13+
* Instrumented test, which will execute on an Android device.
14+
*
15+
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
16+
*/
17+
@RunWith(AndroidJUnit4.class)
18+
public class ExampleInstrumentedTest {
19+
@Test
20+
public void useAppContext() {
21+
// Context of the app under test.
22+
Context appContext = InstrumentationRegistry.getTargetContext();
23+
24+
assertEquals("com.allen.android.nestedscrolldemo", appContext.getPackageName());
25+
}
26+
}

app/src/main/AndroidManifest.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.allen.android.nestedscrolldemo">
4+
5+
<application
6+
android:allowBackup="true"
7+
android:icon="@mipmap/ic_launcher"
8+
android:label="@string/app_name"
9+
android:roundIcon="@mipmap/ic_launcher_round"
10+
android:supportsRtl="true"
11+
android:theme="@style/AppTheme">
12+
<activity android:name=".MainActivity">
13+
<intent-filter>
14+
<action android:name="android.intent.action.MAIN" />
15+
16+
<category android:name="android.intent.category.LAUNCHER" />
17+
</intent-filter>
18+
</activity>
19+
</application>
20+
21+
</manifest>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.allen.android.nestedscrolldemo;
2+
3+
import android.app.Activity;
4+
import android.os.Bundle;
5+
6+
public class MainActivity extends Activity {
7+
8+
@Override
9+
protected void onCreate(Bundle savedInstanceState) {
10+
super.onCreate(savedInstanceState);
11+
setContentView(R.layout.activity_main);
12+
}
13+
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package com.allen.android.nestedscrolldemo;
2+
3+
import android.content.Context;
4+
import android.support.annotation.Nullable;
5+
import android.support.v4.view.NestedScrollingChild;
6+
import android.support.v4.view.NestedScrollingChildHelper;
7+
import android.support.v4.view.ViewCompat;
8+
import android.util.AttributeSet;
9+
import android.util.Log;
10+
import android.view.MotionEvent;
11+
import android.view.View;
12+
13+
import java.util.Arrays;
14+
15+
/**
16+
* 嵌套滑动的发起者:子View
17+
* Created by liyong on 2018/10/11.
18+
*/
19+
public class NestedChildView extends View implements NestedScrollingChild
20+
21+
{
22+
23+
private final static String TAG = "NestedChildView";
24+
25+
private float mLastX;//手指在屏幕上最后的x位置
26+
private float mLastY;//手指在屏幕上最后的y位置
27+
28+
private float mDownX;//手指第一次落下时的x位置(忽略)
29+
private float mDownY;//手指第一次落下时的y位置
30+
31+
private int[] consumed = new int[2];//消耗的距离
32+
private int[] offsetInWindow = new int[2];//窗口偏移
33+
34+
private NestedScrollingChildHelper mScrollingChildHelper;
35+
36+
public NestedChildView(Context context, @Nullable AttributeSet attrs) {
37+
super(context, attrs);
38+
init();
39+
}
40+
41+
private void init() {
42+
mScrollingChildHelper = new NestedScrollingChildHelper(this);
43+
setNestedScrollingEnabled(true);
44+
}
45+
46+
@Override
47+
public boolean onTouchEvent(MotionEvent ev) {
48+
float x = ev.getX();
49+
float y = ev.getY();
50+
51+
int action = ev.getAction();
52+
53+
switch (action) {
54+
case MotionEvent.ACTION_DOWN: {
55+
56+
mDownX = x;
57+
mDownY = y;
58+
mLastX = x;
59+
mLastY = y;
60+
//当开始滑动的时候,告诉父view
61+
startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL);
62+
break;
63+
}
64+
65+
case MotionEvent.ACTION_MOVE: {
66+
/*
67+
mDownY:293.0
68+
mDownX:215.0
69+
*/
70+
71+
int dy = (int) (y - mDownY);
72+
int dx = (int) (x - mDownX);
73+
74+
//分发触屏事件给父类处理
75+
if (dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)) {
76+
//减掉父类消耗的距离
77+
dx -= consumed[0];
78+
dy -= consumed[1];
79+
Log.d(TAG, Arrays.toString(offsetInWindow));
80+
}
81+
82+
offsetTopAndBottom(dy);
83+
offsetLeftAndRight(dx);
84+
85+
dispatchNestedScroll(dx,dy,0,0,offsetInWindow);
86+
break;
87+
}
88+
89+
case MotionEvent.ACTION_UP: {
90+
stopNestedScroll();
91+
break;
92+
}
93+
}
94+
mLastX = x;
95+
mLastY = y;
96+
return true;
97+
}
98+
99+
/**
100+
* 设置是否允许嵌套滑动
101+
*
102+
* @param enabled
103+
*/
104+
@Override
105+
public void setNestedScrollingEnabled(boolean enabled) {
106+
mScrollingChildHelper.setNestedScrollingEnabled(enabled);
107+
}
108+
109+
/**
110+
* 是否允许嵌套滑动
111+
*
112+
* @return
113+
*/
114+
@Override
115+
public boolean isNestedScrollingEnabled() {
116+
return mScrollingChildHelper.isNestedScrollingEnabled();
117+
}
118+
119+
/**
120+
* 告诉开始嵌套滑动流程,调用这个函数的时候会去找嵌套滑动的父控件。如果找到了父控件并且父控件说可以滑动就返回true,否则返回false
121+
* (一般ACTION_DOWN里面调用)
122+
*
123+
* @param axes:支持嵌套滚动轴。水平方向,垂直方向,或者不指定
124+
* @return true 父控件说可以滑动,false 父控件说不可以滑动
125+
*/
126+
@Override
127+
public boolean startNestedScroll(int axes) {
128+
return mScrollingChildHelper.startNestedScroll(axes);
129+
}
130+
131+
/**
132+
* 停止嵌套滑动流程(一般ACTION_UP里面调用)
133+
*/
134+
@Override
135+
public void stopNestedScroll() {
136+
mScrollingChildHelper.stopNestedScroll();
137+
}
138+
139+
/**
140+
* 是否有嵌套滑动对应的父控件
141+
*
142+
* @return
143+
*/
144+
@Override
145+
public boolean hasNestedScrollingParent() {
146+
return mScrollingChildHelper.hasNestedScrollingParent();
147+
}
148+
149+
/**
150+
* 在嵌套滑动的子View滑动之后再调用该函数向父View汇报滑动情况。
151+
*
152+
* @param dxConsumed 子View水平方向滑动的距离
153+
* @param dyConsumed 子View垂直方向滑动的距离
154+
* @param dxUnconsumed 子View水平方向没有滑动的距离
155+
* @param dyUnconsumed 子View垂直方向没有滑动的距离
156+
* @param offsetInWindow 出参 如果父View滑动导致子View的窗口发生了变化(子View的位置发生了变化)
157+
* 该参数返回x(offsetInWindow[0]) y(offsetInWindow[1])方向的变化
158+
* 如果你记录了手指最后的位置,需要根据参数offsetInWindow计算偏移量,才能保证下一次的touch事件的计算是正确的。
159+
* @return true 如果父View有滑动做了相应的处理, false 父View没有滑动.
160+
*/
161+
@Override
162+
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
163+
int dyUnconsumed, int[] offsetInWindow) {
164+
return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
165+
}
166+
167+
/**
168+
* 在嵌套滑动的子View滑动之前,告诉父View滑动的距离,让父View做相应的处理。
169+
*
170+
* @param dx 告诉父View水平方向需要滑动的距离
171+
* @param dy 告诉父View垂直方向需要滑动的距离
172+
* @param consumed 出参. 如果不是null, 则告诉子View父View滑动的情况, consumed[0]父View告诉子View水平方向滑动的距离(dx)
173+
* consumed[1]父View告诉子View垂直方向滑动的距离(dy).
174+
* @param offsetInWindow 可选 length=2的数组,如果父View滑动导致子View的窗口发生了变化(子View的位置发生了变化)
175+
* 该参数返回x(offsetInWindow[0]) y(offsetInWindow[1])方向的变化
176+
* 如果你记录了手指最后的位置,需要根据参数offsetInWindow计算偏移量,才能保证下一次的touch事件的计算是正确的。
177+
* @return true 父View滑动了,false 父View没有滑动。
178+
*/
179+
@Override
180+
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
181+
return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
182+
}
183+
184+
/**
185+
* 在嵌套滑动的子View fling之后再调用该函数向父View汇报fling情况。
186+
*
187+
* @param velocityX 水平方向的速度
188+
* @param velocityY 垂直方向的速度
189+
* @param consumed true 如果子View fling了, false 如果子View没有fling
190+
* @return true 如果父View fling了
191+
*/
192+
@Override
193+
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
194+
return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
195+
}
196+
197+
/**
198+
* 在嵌套滑动的子View fling之前告诉父View fling的情况。
199+
*
200+
* @param velocityX 水平方向的速度
201+
* @param velocityY 垂直方向的速度
202+
* @return 如果父View fling了
203+
*/
204+
@Override
205+
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
206+
return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
207+
}
208+
209+
@Override
210+
protected void onDetachedFromWindow() {
211+
super.onDetachedFromWindow();
212+
mScrollingChildHelper.onDetachedFromWindow();
213+
}
214+
}
215+

0 commit comments

Comments
 (0)