├── .gitignore
├── .idea
├── caches
│ └── build_file_checksums.ser
├── codeStyles
│ └── Project.xml
├── dictionaries
│ └── andy.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jennifer
│ │ └── andy
│ │ └── nestedscrollingdemo
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── jennifer
│ │ │ └── andy
│ │ │ └── nestedscrollingdemo
│ │ │ ├── MainActivity.java
│ │ │ ├── adapter
│ │ │ ├── BaseFragmentItemAdapter.java
│ │ │ ├── BaseRecyclerViewAdapter.java
│ │ │ ├── RecyclerViewHolder.java
│ │ │ └── SimpleStringAdapter.java
│ │ │ ├── ui
│ │ │ ├── TabFragment.java
│ │ │ ├── abl
│ │ │ │ ├── CdlWithAppBarActivity.java
│ │ │ │ └── CdlWithAppBarWithCollActivity.java
│ │ │ ├── cdl
│ │ │ │ ├── CoordinatorLayoutActivity.java
│ │ │ │ ├── CoordinatorLayoutDemo1Activity.java
│ │ │ │ ├── CoordinatorLayoutDemo2Activity.java
│ │ │ │ ├── CoordinatorLayoutDemo3Activity.java
│ │ │ │ ├── CoordinatorLayoutDemo4Activity.java
│ │ │ │ └── behavior
│ │ │ │ │ ├── BrotherChameleonBehavior.java
│ │ │ │ │ ├── BrotherFollowBehavior.java
│ │ │ │ │ ├── BrotherFollowWithoutDependsBehavior.java
│ │ │ │ │ ├── HeaderScrollingViewBehavior.java
│ │ │ │ │ ├── MeasureLayoutBehavior.java
│ │ │ │ │ ├── NestedHeaderBehavior.java
│ │ │ │ │ └── ScrollingViewBehavior.java
│ │ │ └── nested
│ │ │ │ ├── NestedScrolling2DemoActivity.java
│ │ │ │ ├── NestedScrollingParent2Activity.java
│ │ │ │ ├── NestedScrollingParentActivity.java
│ │ │ │ ├── NestedTraditionActivity.java
│ │ │ │ └── normal_form
│ │ │ │ ├── NestedScrollingChild2View.java
│ │ │ │ ├── NestedScrollingChildView.java
│ │ │ │ ├── NestedScrollingParent2View.java
│ │ │ │ └── NestedScrollingParentView.java
│ │ │ └── view
│ │ │ ├── DependedView.java
│ │ │ ├── NestedScrollingParent2Layout.java
│ │ │ ├── NestedScrollingParentLayout.java
│ │ │ ├── NestedTraditionLayout.java
│ │ │ └── StickyNavLayout.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_action_back_black.png
│ │ ├── ic_action_back_white.png
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_cdl_abl_ctl.xml
│ │ ├── activity_cdl_demo1.xml
│ │ ├── activity_cdl_demo2.xml
│ │ ├── activity_cdl_demo3.xml
│ │ ├── activity_cdl_demo4.xml
│ │ ├── activity_cdl_with_appbar.xml
│ │ ├── activity_coord_main.xml
│ │ ├── activity_main.xml
│ │ ├── activity_nested_scrolling2_demo.xml
│ │ ├── activity_nested_srolling_parent.xml
│ │ ├── activity_nested_srolling_parent2.xml
│ │ ├── activity_nested_tradition.xml
│ │ ├── fragment_tab.xml
│ │ ├── item_single_text.xml
│ │ └── layout_common_toolbar.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── jennifer
│ └── andy
│ └── nestedscrollingdemo
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/dictionaries/andy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NestedScrollingDemo
2 |
3 | NestedScrollingDemo 是一款帮助理解 Android NestedScrolling 机制的最佳实战项目。通过学习该项目你可以了解:
4 |
5 | - 传统事件分发机制实现嵌套滑动的局限性。
6 | - 谷歌 NestedScrolling 与 NesetdScrolling2 机制的原理实现。
7 | - NestedScrollingChild 与 NestedScrollingParent 实战。
8 | - NestedScrollingChild2 与 NestedScrollingParent2 实战。
9 | - CoordinatorLayout 与 Behavior 实战。
10 | - CoordinatorLayout 与 AppBarLayout 配合使用例子。
11 | - CoordinatorLayout 与 AppBarLayout 、CollapsingToolbarLayout 三者配合使用例子。
12 |
13 | ## 项目中展示的例子
14 |
15 | ### 传统事件分发机制处理嵌套滑动 🐣
16 |
17 |
18 |
19 | 相关类:
20 |
21 | - [NestedTraditionActivity](https://github.com/AndyJennifer/NestedScrollingDemo/blob/master/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/NestedTraditionActivity.java)
22 | - [NestedTraditionLayout](https://github.com/AndyJennifer/NestedScrollingDemo/blob/master/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/view/NestedTraditionLayout.java)
23 |
24 | ### NestedScrolling 与 NestedScrolling2 机制 🐘
25 |
26 |
27 |
28 |
29 | NestedScrolling 机制 |
30 | NestedScrolling2 机制 |
31 | NestedScrolling2 机制实战例子 |
32 |
33 |
34 |
35 |
36 |
37 | |
38 |
39 |
40 | |
41 |
42 |
43 | |
44 |
45 |
46 |
47 |
48 |
59 | |
60 |
61 |
72 | |
73 |
74 |
82 | |
83 |
84 |
85 |
86 | ### CoordinatorLayout 与 Behavior、AppBarLayout、CollapsingToolbarLayout 🐠
87 |
88 |
89 |
90 | 自定义 Behavior 事件拦截与处理 |
91 | 自定义 Behavior 测量与布局 |
92 | Behavior 嵌套滑动交互效果 |
93 |
94 |
95 |
96 |
97 | |
98 |
99 |
100 | |
101 |
102 |
103 | |
104 |
105 |
106 |
107 | |
122 |
123 |
131 | |
132 |
133 |
144 | |
145 |
146 |
147 |
148 | ### CoordinatorLayout 与 AppBarLayout、CollapsingToolbarLayout 🎉
149 |
150 |
151 |
152 | CoordinatorLayout 与 AppBarLayout 结合使用 |
153 | CoordinatorLayout 与 AppBarLayout、CollapsingToolbarLayout 结合使用 |
154 |
155 |
156 |
157 |
158 | |
159 |
160 |
161 | |
162 |
163 |
164 |
165 |
170 | |
171 |
172 |
177 | |
178 |
179 |
180 |
181 | ## 了解更多
182 |
183 | 对项目中的代码有疑惑,可以查看文章:
184 |
185 | - [自定义View事件之进阶篇(一)-NestedScrolling(嵌套滑动)机制](https://juejin.im/post/5d3e639e51882508dc163606)
186 | - [自定义View事件篇进阶篇(二)-自定义NestedScrolling实战](https://juejin.im/post/5d3e860be51d454f6f16ecf0)
187 | - [自定义View事件篇进阶篇(三)-CoordinatorLayout与Behavior](https://juejin.im/post/5d430c5a6fb9a06aeb109d56)
188 | - [自定义View事件之进阶篇(四)-自定义Behavior实战](https://juejin.im/post/5d43be5af265da03c81501a1)
189 |
190 | ## 最后
191 |
192 | 如果你觉得项目不错,欢迎点击 star ❤️或 follow,也可以帮忙分享给你更多的朋友。你的支持与鼓励是给我继续做好该项目的最大动力。
193 |
194 | ## 联系我
195 |
196 | - QQ:443696320
197 | - 简书:[AndyandJennifer](https://www.jianshu.com/users/921c778fb5e1/timeline)
198 | - 掘金:[AndyandJennifer](https://juejin.im/user/5acc1ea06fb9a028bc2e0fc1)
199 | - Email: [andyjennifer@126.com](andyjennifer@126.com)
200 |
201 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | defaultConfig {
6 | applicationId "com.jennifer.andy.nestedscrollingdemo"
7 | minSdkVersion 19
8 | targetSdkVersion 27
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:appcompat-v7:27.1.1'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.2'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | implementation 'com.android.support:design:27.1.1'
29 | }
30 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
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
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/jennifer/andy/nestedscrollingdemo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo;
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 Testing documentation
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.jennifer.andy.nestedscrollingdemo", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
27 |
28 |
30 |
31 |
33 |
34 |
36 |
37 |
38 |
39 |
42 |
43 |
45 |
46 |
48 |
49 |
51 |
52 |
54 |
55 |
56 |
58 |
59 |
60 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | import com.jennifer.andy.nestedscrollingdemo.ui.abl.CdlWithAppBarActivity;
9 | import com.jennifer.andy.nestedscrollingdemo.ui.abl.CdlWithAppBarWithCollActivity;
10 | import com.jennifer.andy.nestedscrollingdemo.ui.cdl.CoordinatorLayoutActivity;
11 | import com.jennifer.andy.nestedscrollingdemo.ui.nested.NestedScrolling2DemoActivity;
12 | import com.jennifer.andy.nestedscrollingdemo.ui.nested.NestedScrollingParent2Activity;
13 | import com.jennifer.andy.nestedscrollingdemo.ui.nested.NestedScrollingParentActivity;
14 | import com.jennifer.andy.nestedscrollingdemo.ui.nested.NestedTraditionActivity;
15 |
16 | public class MainActivity extends AppCompatActivity implements View.OnClickListener {
17 |
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_main);
23 | findViewAndSetListener();
24 | }
25 |
26 | private void findViewAndSetListener() {
27 | findViewById(R.id.btn_nested_scrolling_tradition).setOnClickListener(this);
28 | findViewById(R.id.btn_nested_scrolling).setOnClickListener(this);
29 | findViewById(R.id.btn_nested_scrolling2).setOnClickListener(this);
30 | findViewById(R.id.btn_nested_scrolling2Demo).setOnClickListener(this);
31 | findViewById(R.id.btn_coordinator_layout).setOnClickListener(this);
32 | findViewById(R.id.btn_coor_with_appbar).setOnClickListener(this);
33 | findViewById(R.id.btn_coor_with_appbar_with_coll).setOnClickListener(this);
34 |
35 | }
36 |
37 | @Override
38 | public void onClick(View view) {
39 | switch (view.getId()) {
40 | case R.id.btn_nested_scrolling_tradition://传统嵌套滑动
41 | startActivity(new Intent(this, NestedTraditionActivity.class));
42 | break;
43 | case R.id.btn_nested_scrolling://实现NestedScrollingParent机制的嵌套滑动
44 | startActivity(new Intent(this, NestedScrollingParentActivity.class));
45 | break;
46 | case R.id.btn_nested_scrolling2://实现NestedScrollingParent2机制的嵌套滑动
47 | startActivity(new Intent(this, NestedScrollingParent2Activity.class));
48 | break;
49 | case R.id.btn_nested_scrolling2Demo://嵌套滑动实际使用例子
50 | startActivity(new Intent(this, NestedScrolling2DemoActivity.class));
51 | break;
52 | case R.id.btn_coordinator_layout://CoordinatorLayout效果展示
53 | startActivity(new Intent(this, CoordinatorLayoutActivity.class));
54 | break;
55 | case R.id.btn_coor_with_appbar://CoordinatorLayout与AppBarLayout结合使用
56 | startActivity(new Intent(this, CdlWithAppBarActivity.class));
57 | break;
58 | case R.id.btn_coor_with_appbar_with_coll://CoordinatorLayout与AppBarLayout与CollapsingToolbarLayout 三者结合使用
59 | startActivity(new Intent(this, CdlWithAppBarWithCollActivity.class));
60 | break;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/adapter/BaseFragmentItemAdapter.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.adapter;
2 |
3 | import android.support.annotation.Nullable;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v4.app.FragmentManager;
6 | import android.support.v4.app.FragmentPagerAdapter;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * Author: andy.xwt
12 | * Date: 2018/8/8 15:26
13 | * Description:
14 | */
15 |
16 | public class BaseFragmentItemAdapter extends FragmentPagerAdapter {
17 |
18 | private List mFragments;
19 | private List mTitles;
20 |
21 | public BaseFragmentItemAdapter(FragmentManager fm, List fragments, List titles) {
22 | super(fm);
23 | this.mFragments = fragments;
24 | this.mTitles = titles;
25 | }
26 |
27 | @Override
28 | public Fragment getItem(int position) {
29 | return mFragments.get(position);
30 | }
31 |
32 | @Override
33 | public int getCount() {
34 | return mFragments.size();
35 | }
36 |
37 | @Nullable
38 | @Override
39 | public CharSequence getPageTitle(int position) {
40 | return mTitles.get(position);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/adapter/BaseRecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.IdRes;
5 | import android.support.annotation.IntRange;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | /**
15 | * Author: andy.xwt
16 | * Date: 2017/5/17 17:14
17 | * Description: 基础 RecyclerView适配器
18 | */
19 |
20 | public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter {
21 |
22 | protected int mLayoutId;
23 | protected List mData;
24 | protected Context mContext;
25 | protected View mRootView;//
26 | protected List mHolders = new ArrayList<>();
27 | private RecyclerView mRecyclerView;
28 | private onItemClickListener mOnItemClickListener;
29 | private onItemChildClickListener mOnItemChildClickListener;
30 |
31 | public BaseRecyclerViewAdapter(int layoutId, List data, Context context) {
32 | mLayoutId = layoutId;
33 | mData = data;
34 | mContext = context;
35 | }
36 |
37 | public BaseRecyclerViewAdapter(int layoutId, Context context) {
38 | mLayoutId = layoutId;
39 | mContext = context;
40 | }
41 |
42 | @Override
43 | public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
44 | mRootView = LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false);
45 | final RecyclerViewHolder holder = new RecyclerViewHolder(mRootView);
46 | mHolders.add(holder);
47 | holder.itemView.setOnClickListener(new View.OnClickListener() {
48 | @Override
49 | public void onClick(View v) {
50 | if (mOnItemClickListener != null) {
51 | mOnItemClickListener.onClick(v, holder.getLayoutPosition());
52 | }
53 | }
54 | });
55 | holder.setAdapter(this);
56 | return holder;
57 | }
58 |
59 | @Override
60 | public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
61 | bindData(holder, mData.get(position), position);
62 |
63 |
64 | }
65 |
66 | @Override
67 | public int getItemCount() {
68 | return mData == null ? 0 : mData.size();
69 | }
70 |
71 | /**
72 | * 绑定数据
73 | *
74 | * @param holder
75 | * @param t 数据源
76 | * @param position 位置
77 | */
78 | protected abstract void bindData(RecyclerViewHolder holder, T t, int position);
79 |
80 |
81 | public List getData() {
82 | return mData;
83 | }
84 |
85 | public void setData(List data) {
86 | mData = data;
87 | }
88 |
89 | /**
90 | * 添加新数据(集合)
91 | */
92 | public void addData(List data) {
93 | if (data != null && data.size() > 0) {
94 | mData.addAll(data);
95 | notifyItemRangeInserted(mData.size(), data.size());
96 | }
97 | }
98 |
99 | /**
100 | * 添加一个数据
101 | */
102 | public void addData(T data) {
103 | if (data != null) {
104 | mData.add(data);
105 | notifyItemInserted(mData.size());
106 | }
107 | }
108 |
109 |
110 | /**
111 | * 设置新数据
112 | */
113 | public void setNewData(List data) {
114 | if (data != null && data.size() > 0) {
115 | mData = data;
116 | notifyDataSetChanged();
117 | }
118 | }
119 |
120 | /**
121 | * 清空数据
122 | */
123 | public void clearData() {
124 | mData.clear();
125 | notifyDataSetChanged();
126 | }
127 |
128 | /**
129 | * 根据位置删除数据
130 | */
131 | public void remove(@IntRange(from = 0) int position) {
132 | mData.remove(position);
133 | notifyItemRemoved(position);
134 | }
135 |
136 | private void checkNotNull() {
137 | if (getRecyclerView() == null) {
138 | throw new RuntimeException("please bind recyclerView first!");
139 | }
140 | }
141 |
142 | /**
143 | * 根据当前位置获取相应item中的view
144 | */
145 | public View getViewByPosition(int position, @IdRes int viewId) {
146 | checkNotNull();
147 | return getViewByPosition(getRecyclerView(), position, viewId);
148 | }
149 |
150 | /**
151 | * 获取当前
152 | */
153 | public V getViewByPosition(RecyclerView recyclerView, int position, @IdRes int viewId) {
154 | if (recyclerView == null) {
155 | return null;
156 | }
157 | RecyclerViewHolder viewHolder = (RecyclerViewHolder) recyclerView.findViewHolderForLayoutPosition(position);
158 | if (viewHolder == null) {
159 | return null;
160 | }
161 | return viewHolder.getView(viewId);
162 | }
163 |
164 | public View getRootView() {
165 | return mRootView;
166 | }
167 |
168 |
169 | protected RecyclerView getRecyclerView() {
170 | return mRecyclerView;
171 | }
172 |
173 | private void setRecyclerView(RecyclerView recyclerView) {
174 | mRecyclerView = recyclerView;
175 | }
176 |
177 | /**
178 | * item 点击事件
179 | */
180 | public interface onItemClickListener {
181 | void onClick(View view, int position);
182 | }
183 |
184 | public void setOnItemClickListener(onItemClickListener onItemClickListener) {
185 | mOnItemClickListener = onItemClickListener;
186 | }
187 |
188 |
189 | /**
190 | * item 中子布局点击事件
191 | */
192 | public interface onItemChildClickListener {
193 | void onItemChildClick(View view, int position);
194 | }
195 |
196 |
197 | public onItemChildClickListener getOnItemChildClickListener() {
198 | return mOnItemChildClickListener;
199 | }
200 |
201 | public void setOnItemChildClickListener(onItemChildClickListener onItemChildClickListener) {
202 | mOnItemChildClickListener = onItemChildClickListener;
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/adapter/RecyclerViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.adapter;
2 |
3 | import android.graphics.drawable.Drawable;
4 | import android.support.annotation.ColorInt;
5 | import android.support.annotation.DrawableRes;
6 | import android.support.annotation.IdRes;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.text.SpannableStringBuilder;
10 | import android.util.SparseArray;
11 | import android.view.View;
12 | import android.widget.CheckBox;
13 | import android.widget.ImageView;
14 | import android.widget.TextView;
15 |
16 | import java.util.LinkedHashSet;
17 |
18 | /**
19 | * Author: andy.xwt
20 | * Date: 2017/8/8 11:55
21 | * Description:
22 | */
23 |
24 | public class RecyclerViewHolder extends RecyclerView.ViewHolder {
25 |
26 | private SparseArray mViews;
27 | private final LinkedHashSet childClickViewIds = new LinkedHashSet();
28 | private BaseRecyclerViewAdapter adapter;
29 |
30 | public RecyclerViewHolder(View itemView) {
31 | super(itemView);
32 | this.mViews = new SparseArray<>();
33 | }
34 |
35 |
36 | public V getView(@IdRes int viewId) {
37 | View view = mViews.get(viewId);
38 | if (view == null) {
39 | view = itemView.findViewById(viewId);
40 | mViews.put(viewId, view);
41 | }
42 | return (V) view;
43 | }
44 |
45 | /**
46 | * 设置textView值
47 | *
48 | * @param viewId
49 | * @param text 内容
50 | * @return
51 | */
52 | public RecyclerViewHolder setText(@IdRes int viewId, String text) {
53 | TextView tv = getView(viewId);
54 | tv.setText(text);
55 | return this;
56 | }
57 |
58 |
59 | /**
60 | * 设置textView值
61 | */
62 | public RecyclerViewHolder setText(@IdRes int viewId, SpannableStringBuilder text) {
63 | TextView tv = getView(viewId);
64 | tv.setText(text);
65 | return this;
66 | }
67 |
68 | /**
69 | * 设置textVie颜色
70 | */
71 | public RecyclerViewHolder setTextColor(@IdRes int viewId, @ColorInt int color) {
72 | TextView tv = getView(viewId);
73 | tv.setTextColor(color);
74 | return this;
75 | }
76 |
77 |
78 |
79 | public RecyclerViewHolder setImage(@IdRes int viewId, Drawable drawable) {
80 | ImageView iv = getView(viewId);
81 | iv.setImageDrawable(drawable);
82 | return this;
83 | }
84 |
85 |
86 | public RecyclerViewHolder setImage(@IdRes int viewId, @DrawableRes int resId) {
87 | ImageView iv = getView(viewId);
88 | iv.setImageResource(resId);
89 | return this;
90 | }
91 |
92 |
93 | public RecyclerViewHolder setCompoundDrawables(@IdRes int viewId,
94 | @Nullable Drawable left,
95 | @Nullable Drawable top,
96 | @Nullable Drawable right,
97 | @Nullable Drawable bottom) {
98 | TextView tv = getView(viewId);
99 | tv.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
100 | return this;
101 | }
102 |
103 | /**
104 | * 设置背景
105 | *
106 | * @param viewId
107 | * @param resId
108 | * @return
109 | */
110 | public RecyclerViewHolder setBackgroundResource(@IdRes int viewId, @DrawableRes int resId) {
111 | View view = getView(viewId);
112 | view.setBackgroundResource(resId);
113 | return this;
114 | }
115 |
116 | /**
117 | * 设置image 为null,解决viewHolder复用问题
118 | *
119 | * @param viewId
120 | */
121 | public RecyclerViewHolder setImageBitMapNull(@IdRes int viewId) {
122 | ImageView iv = getView(viewId);
123 | iv.setImageBitmap(null);
124 | return this;
125 | }
126 |
127 | /**
128 | * 设置是否可见
129 | *
130 | * @param viewId
131 | * @param isVisible 是否可见
132 | * @return
133 | */
134 | public RecyclerViewHolder setVisible(@IdRes int viewId, boolean isVisible) {
135 | View tv = getView(viewId);
136 | tv.setVisibility(isVisible ? View.VISIBLE : View.GONE);
137 | return this;
138 | }
139 |
140 |
141 | /**
142 | * 设置是否选中
143 | *
144 | * @param viewId
145 | * @param isChecked 是否选中
146 | * @return
147 | */
148 | public RecyclerViewHolder setChecked(@IdRes int viewId, boolean isChecked) {
149 | CheckBox tv = getView(viewId);
150 | if (tv != null) {
151 | tv.setChecked(isChecked);
152 | }
153 | return this;
154 | }
155 |
156 |
157 | /**
158 | * 设置是否可用
159 | */
160 | public RecyclerViewHolder setEnabled(@IdRes int viewId, boolean isEnabled) {
161 | View view = getView(viewId);
162 | if (view != null) {
163 | view.setEnabled(isEnabled);
164 | }
165 | return this;
166 | }
167 |
168 | /**
169 | * 设置点击事件
170 | */
171 | public RecyclerViewHolder setOnClickListener(@IdRes int viewId, View.OnClickListener listener) {
172 | View view = getView(viewId);
173 | view.setOnClickListener(listener);
174 | return this;
175 | }
176 |
177 | /**
178 | * 添加点击事件 注意添加点击事件的时候必须要设置adapter
179 | *
180 | * @see #setAdapter(BaseRecyclerViewAdapter)
181 | */
182 | public RecyclerViewHolder addOnClickListener(@IdRes int viewId) {
183 | this.childClickViewIds.add(viewId);
184 | View view = this.getView(viewId);
185 | if (view != null) {
186 | if (!view.isClickable()) {
187 | view.setClickable(true);
188 | }
189 | view.setOnClickListener(new View.OnClickListener() {
190 | public void onClick(View v) {
191 | if (RecyclerViewHolder.this.adapter.getOnItemChildClickListener() != null) {
192 | RecyclerViewHolder.this.adapter.getOnItemChildClickListener().onItemChildClick(v, RecyclerViewHolder.this.getLayoutPosition());
193 | }
194 | }
195 | });
196 | }
197 | return this;
198 | }
199 |
200 | /**
201 | * 设置适配器
202 | */
203 | public BaseRecyclerViewAdapter getAdapter() {
204 | return adapter;
205 | }
206 |
207 | public void setAdapter(BaseRecyclerViewAdapter adapter) {
208 | this.adapter = adapter;
209 | }
210 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/adapter/SimpleStringAdapter.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.adapter;
2 |
3 | import android.content.Context;
4 |
5 | import com.jennifer.andy.nestedscrollingdemo.R;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * Author: andy.xwt
11 | * Date: 2018/8/8 15:55
12 | * Description:简单的一行文本适配器
13 | */
14 |
15 | public class SimpleStringAdapter extends BaseRecyclerViewAdapter {
16 |
17 | public SimpleStringAdapter(List strs, Context context) {
18 | super(R.layout.item_single_text, strs, context);
19 | }
20 |
21 | @Override
22 | protected void bindData(RecyclerViewHolder holder, String text, int position) {
23 | holder.setText(R.id.tv_text, text);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/TabFragment.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.app.Fragment;
7 | import android.support.v7.widget.DividerItemDecoration;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 |
14 | import com.jennifer.andy.nestedscrollingdemo.R;
15 | import com.jennifer.andy.nestedscrollingdemo.adapter.SimpleStringAdapter;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | /**
21 | * Author: andy.xwt
22 | * Date: 2018/8/8 15:29
23 | * Description:
24 | */
25 |
26 | public class TabFragment extends Fragment {
27 |
28 |
29 | private RecyclerView mRecyclerView;
30 | private String mText;
31 |
32 | public static TabFragment newInstance(String text) {
33 | Bundle args = new Bundle();
34 | TabFragment fragment = new TabFragment();
35 | args.putString("text", text);
36 | fragment.setArguments(args);
37 | return fragment;
38 | }
39 |
40 | @Nullable
41 | @Override
42 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
43 | return inflater.inflate(R.layout.fragment_tab, container, false);
44 | }
45 |
46 | @Override
47 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
48 | super.onViewCreated(view, savedInstanceState);
49 | mText = getArguments().getString("text");
50 | initView(view);
51 | }
52 |
53 | private void initView(View view) {
54 | mRecyclerView = view.findViewById(R.id.recycler_view);
55 | mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
56 | mRecyclerView.setAdapter(new SimpleStringAdapter(initStrings(), getContext()));
57 | mRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
58 | }
59 |
60 | private List initStrings() {
61 | List list = new ArrayList<>();
62 | for (int i = 0; i < 100; i++) {
63 | list.add(mText);
64 | }
65 | return list;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/abl/CdlWithAppBarActivity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.abl;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.DividerItemDecoration;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.R;
10 | import com.jennifer.andy.nestedscrollingdemo.adapter.SimpleStringAdapter;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Author: andy.xwt
17 | * Date: 2018/8/8 13:56
18 | * Description:coordinatorLayout与AppBarLayout的使用
19 | */
20 |
21 | public class CdlWithAppBarActivity extends AppCompatActivity {
22 |
23 | private RecyclerView mRecyclerView;
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | setContentView(R.layout.activity_cdl_with_appbar);
29 | initView();
30 | }
31 |
32 | private void initView() {
33 | mRecyclerView = findViewById(R.id.recycler_view);
34 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
35 | mRecyclerView.setAdapter(new SimpleStringAdapter(initStrings(), this));
36 | mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
37 | }
38 |
39 | private List initStrings() {
40 | List list = new ArrayList<>();
41 | for (int i = 0; i < 100; i++) {
42 | list.add("简单文本" + i);
43 | }
44 | return list;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/abl/CdlWithAppBarWithCollActivity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.abl;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.DividerItemDecoration;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.R;
10 | import com.jennifer.andy.nestedscrollingdemo.adapter.SimpleStringAdapter;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Author: andy.xwt
17 | * Date: 2018/8/8 13:56
18 | * Description:
19 | */
20 |
21 | public class CdlWithAppBarWithCollActivity extends AppCompatActivity {
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_cdl_abl_ctl);
27 | initView();
28 | }
29 |
30 | private void initView() {
31 | final RecyclerView recyclerView = findViewById(R.id.recycler_view);
32 | recyclerView.setLayoutManager(new LinearLayoutManager(this));
33 | recyclerView.setAdapter(new SimpleStringAdapter(initStrings(), this));
34 | recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
35 | }
36 |
37 | private List initStrings() {
38 | List list = new ArrayList<>();
39 | for (int i = 0; i < 100; i++) {
40 | list.add(i + "--->CollapsingToolbarLayout与AppBarLayout");
41 | }
42 | return list;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/CoordinatorLayoutActivity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | import com.jennifer.andy.nestedscrollingdemo.R;
9 |
10 | /**
11 | * Author: andy.xwt
12 | * Date: 2018/8/8 13:56
13 | * Description:CoordinatorLayout的效果展示。
14 | */
15 |
16 | public class CoordinatorLayoutActivity extends AppCompatActivity implements View.OnClickListener {
17 |
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_coord_main);
23 | initView();
24 | }
25 |
26 | private void initView() {
27 | findViewById(R.id.btn_demo1).setOnClickListener(this);
28 | findViewById(R.id.btn_demo2).setOnClickListener(this);
29 | findViewById(R.id.btn_demo3).setOnClickListener(this);
30 | findViewById(R.id.btn_demo4).setOnClickListener(this);
31 | findViewById(R.id.btn_demo5).setOnClickListener(this);
32 |
33 | }
34 |
35 |
36 | @Override
37 | public void onClick(View v) {
38 | switch (v.getId()) {
39 | case R.id.btn_demo1://多个view的协同交互效果
40 | startActivity(new Intent(this, CoordinatorLayoutDemo1Activity.class));
41 | break;
42 | case R.id.btn_demo2://不重写layoutDependsOn方法,而是在布局使用xml中使用layout_anchor来确定依赖关系
43 | startActivity(new Intent(this, CoordinatorLayoutDemo2Activity.class));
44 | break;
45 | case R.id.btn_demo3://自定义Behavior测量与布局
46 | startActivity(new Intent(this, CoordinatorLayoutDemo3Activity.class));
47 | break;
48 | case R.id.btn_demo4://Behavior嵌套滑动交互效果
49 | startActivity(new Intent(this, CoordinatorLayoutDemo4Activity.class));
50 | break;
51 | case R.id.btn_demo5://自定义Behavior事件拦截与处理
52 | startActivity(new Intent(this, CoordinatorLayoutDemo2Activity.class));
53 | break;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/CoordinatorLayoutDemo1Activity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import com.jennifer.andy.nestedscrollingdemo.R;
7 |
8 | /**
9 | * Author: andy.xwt
10 | * Date: 2018/8/8 13:56
11 | * Description:
12 | * 没有实现NestedScrollingChild接口下,多个view的交互效果
13 | */
14 |
15 | public class CoordinatorLayoutDemo1Activity extends AppCompatActivity {
16 |
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_cdl_demo1);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/CoordinatorLayoutDemo2Activity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import com.jennifer.andy.nestedscrollingdemo.R;
7 |
8 | /**
9 | * Author: andy.xwt
10 | * Date: 2018/8/8 13:56
11 | * Description:
12 | * 不重写layoutDependsOn方法,而是在布局使用xml中使用layout_anchor来确定依赖关系
13 | * 具体为什么可以这样使用,请查看{@link android.support.design.widget.CoordinatorLayout#onChildViewsChanged(int)}方法中
14 | *
15 | * for (int j = 0; j < i; j++) {
16 | * final View checkChild = mDependencySortedChildren.get(j);
17 | *
18 | * if (lp.mAnchorDirectChild == checkChild) {//这里
19 | * offsetChildToAnchor(child, layoutDirection);//与这里
20 | * }
21 | * }
22 | */
23 |
24 | public class CoordinatorLayoutDemo2Activity extends AppCompatActivity {
25 |
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | setContentView(R.layout.activity_cdl_demo2);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/CoordinatorLayoutDemo3Activity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.DividerItemDecoration;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.R;
10 | import com.jennifer.andy.nestedscrollingdemo.adapter.SimpleStringAdapter;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Author: andy.xwt
17 | * Date: 2018/8/8 13:56
18 | * Description:
19 | * 自定义Behavior测量与布局
20 | */
21 |
22 | public class CoordinatorLayoutDemo3Activity extends AppCompatActivity {
23 |
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | setContentView(R.layout.activity_cdl_demo3);
29 | initView();
30 | }
31 |
32 | private void initView() {
33 | final RecyclerView recyclerView = findViewById(R.id.recycler_view);
34 | recyclerView.setLayoutManager(new LinearLayoutManager(this));
35 | recyclerView.setAdapter(new SimpleStringAdapter(initStrings(), this));
36 | recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
37 | }
38 |
39 | private List initStrings() {
40 | List list = new ArrayList<>();
41 | for (int i = 0; i < 100; i++) {
42 | list.add(i + "--->定义Behavior测量与布局");
43 | }
44 | return list;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/CoordinatorLayoutDemo4Activity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.DividerItemDecoration;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.R;
10 | import com.jennifer.andy.nestedscrollingdemo.adapter.SimpleStringAdapter;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Author: andy.xwt
17 | * Date: 2018/8/8 13:56
18 | * Description:Behavior嵌套滑动效果。
19 | * 因为要重新设置Recycler的位置,所以说需要自定义Behavior{@link com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior.ScrollingViewBehavior}
20 | */
21 |
22 | public class CoordinatorLayoutDemo4Activity extends AppCompatActivity {
23 |
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | setContentView(R.layout.activity_cdl_demo4);
29 | initView();
30 | }
31 |
32 |
33 | private void initView() {
34 | final RecyclerView recyclerView = findViewById(R.id.recycler_view);
35 | recyclerView.setLayoutManager(new LinearLayoutManager(this));
36 | recyclerView.setAdapter(new SimpleStringAdapter(initStrings(), this));
37 | recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
38 |
39 | /**
40 | * 如果你在CoordinatorLayout结合RecyclerView使用了嵌套滑动效果。那么你会发现,当我们使用recyclerView.smoothScrollToPosition(0)时,
41 | * 之前设置的嵌套滑动效果会失效。也就是RecyclerView只会滚动到顶部。而RecyclerView
42 | * 所依赖的控件,它的嵌套滑动是没有出来的。如果你想看一下我的解决方法。可以将R.layout.activity_cdl_demo4布局中的注释解开。
43 | * 并查看NestedHeaderBehavior类中的onStopNestedScroll方法的处理。
44 | */
45 | // findViewById(R.id.btn_ok).setOnClickListener(new View.OnClickListener() {
46 | // @Override
47 | // public void onClick(View v) {
48 | // recyclerView.startNestedScroll(View.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
49 | // recyclerView.smoothScrollToPosition(0);
50 | // }
51 | // });
52 | }
53 |
54 | private List initStrings() {
55 | List list = new ArrayList<>();
56 | for (int i = 0; i < 100; i++) {
57 | list.add(i + "--->Behavior的嵌套滑动");
58 | }
59 | return list;
60 | }
61 |
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/behavior/BrotherChameleonBehavior.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
2 |
3 | import android.animation.ArgbEvaluator;
4 | import android.content.Context;
5 | import android.graphics.Color;
6 | import android.support.design.widget.CoordinatorLayout;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 |
10 | import com.jennifer.andy.nestedscrollingdemo.view.DependedView;
11 |
12 | /**
13 | * Author: andy.xwt
14 | * Date: 2019-07-11 10:53
15 | * Description:变色行为
16 | */
17 |
18 | public class BrotherChameleonBehavior extends CoordinatorLayout.Behavior {
19 |
20 | private ArgbEvaluator mArgbEvaluator = new ArgbEvaluator();
21 |
22 | public BrotherChameleonBehavior(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | @Override
27 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
28 | return dependency instanceof DependedView;
29 | }
30 |
31 | @Override
32 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
33 | int color = (int) mArgbEvaluator.evaluate(dependency.getY() / parent.getHeight(), Color.WHITE, Color.BLACK);
34 | child.setBackgroundColor(color);
35 | return false;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/behavior/BrotherFollowBehavior.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 |
8 | import com.jennifer.andy.nestedscrollingdemo.view.DependedView;
9 |
10 | /**
11 | * Author: andy.xwt
12 | * Date: 2019-07-11 10:53
13 | * Description:跟随行为
14 | */
15 |
16 | public class BrotherFollowBehavior extends CoordinatorLayout.Behavior {
17 |
18 | public BrotherFollowBehavior(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | @Override
23 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
24 | return dependency instanceof DependedView;
25 | }
26 |
27 | @Override
28 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
29 | child.setY(dependency.getBottom() + 50);
30 | child.setX(dependency.getX());
31 | return true;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/behavior/BrotherFollowWithoutDependsBehavior.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 |
8 | /**
9 | * Author: andy.xwt
10 | * Date: 2019-07-11 10:53
11 | * Description: 不重写layoutDependsOn方法,而是在布局使用xml中使用layout_anchor来确定依赖关系
12 | */
13 |
14 | public class BrotherFollowWithoutDependsBehavior extends CoordinatorLayout.Behavior {
15 |
16 | public BrotherFollowWithoutDependsBehavior(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | @Override
21 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
22 | child.setY(dependency.getTop() - 50);//始终在依赖控件上面50个像素
23 | child.setX(dependency.getX());
24 | return true;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/behavior/HeaderScrollingViewBehavior.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.support.design.widget.CoordinatorLayout;
6 | import android.support.v4.view.GravityCompat;
7 | import android.support.v4.view.ViewCompat;
8 | import android.util.AttributeSet;
9 | import android.util.Log;
10 | import android.view.Gravity;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Author: andy.xwt
18 | * Date: 2019-07-12 15:39
19 | * Description:
20 | * 用于测量对应控件的高度和布局 需要配合{@link NestedHeaderBehavior}使用
21 | */
22 | public class HeaderScrollingViewBehavior extends CoordinatorLayout.Behavior {
23 |
24 | final Rect mTempRect1 = new Rect();
25 | final Rect mTempRect2 = new Rect();
26 |
27 | public static final String TAG = "HeaderScrolling";
28 |
29 | public HeaderScrollingViewBehavior() {
30 | }
31 |
32 | public HeaderScrollingViewBehavior(Context context, AttributeSet attrs) {
33 | super(context, attrs);
34 | }
35 |
36 |
37 | @Override
38 | public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
39 |
40 | //获取当前滚动控件的测量模式
41 | final int childLpHeight = child.getLayoutParams().height;
42 |
43 | //只有当前滚动控件为match_parent/wrap_content时才重新测量其高度,因为固定高度不会出现底部空白的情况
44 | if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
45 | || childLpHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
46 |
47 | //获取当前child依赖的对象集合
48 | final List dependencies = parent.getDependencies(child);
49 |
50 | final View header = findFirstDependency(dependencies);
51 | if (header != null) {
52 | if (ViewCompat.getFitsSystemWindows(header)
53 | && !ViewCompat.getFitsSystemWindows(child)) {
54 | // If the header is fitting system windows then we need to also,
55 | // otherwise we'll get CoL's compatible measuring
56 | ViewCompat.setFitsSystemWindows(child, true);
57 |
58 | if (ViewCompat.getFitsSystemWindows(child)) {
59 | // If the set succeeded, trigger a new layout and return true
60 | child.requestLayout();
61 | return true;
62 | }
63 | }
64 | //获取当前父控件中可用的距离,
65 | int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
66 | if (availableHeight == 0) {
67 |
68 | // If the measure spec doesn't specify a size, use the current height
69 | availableHeight = parent.getHeight();
70 | }
71 | //计算当前滚动控件的高度。
72 | final int height = availableHeight - header.getMeasuredHeight() + getScrollRange(header);
73 | final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height,
74 | childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
75 | ? View.MeasureSpec.EXACTLY
76 | : View.MeasureSpec.AT_MOST);
77 |
78 | //测量当前滚动的View的正确高度
79 | parent.onMeasureChild(child, parentWidthMeasureSpec,
80 | widthUsed, heightMeasureSpec, heightUsed);
81 |
82 | return true;
83 | }
84 | }
85 | return false;
86 | }
87 |
88 | @Override
89 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
90 | final List dependencies = parent.getDependencies(child);
91 | final View header = findFirstDependency(dependencies);
92 |
93 | if (header != null) {
94 | final CoordinatorLayout.LayoutParams lp =
95 | (CoordinatorLayout.LayoutParams) child.getLayoutParams();
96 | final Rect available = mTempRect1;
97 |
98 | //得到依赖控件下方的坐标。
99 | available.set(parent.getPaddingLeft() + lp.leftMargin,
100 | header.getBottom() + lp.topMargin,
101 | parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,
102 | parent.getHeight() + header.getBottom()
103 | - parent.getPaddingBottom() - lp.bottomMargin);
104 |
105 | //拿到上面计算的坐标后,根据当前控件在父控件中设置的gravity,重新计算并得到控件在父控件中的坐标
106 | final Rect out = mTempRect2;
107 | GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
108 | child.getMeasuredHeight(), available, out, layoutDirection);
109 |
110 | //拿到坐标后重新布局
111 | child.layout(out.left, out.top, out.right, out.bottom);
112 |
113 | } else {
114 | //如果没有依赖,则调用父控件来处理布局
115 | parent.onLayoutChild(child, layoutDirection);
116 | }
117 | return true;
118 | }
119 |
120 | @Override
121 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
122 | offsetChildAsNeeded(parent, child, dependency);
123 | return true;
124 | }
125 |
126 | private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) {
127 | final CoordinatorLayout.Behavior behavior =
128 | ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
129 | if (behavior instanceof NestedHeaderBehavior) {
130 | Log.i(TAG, "offsetChildAsNeeded: " + dependency.getBottom() + "--->" + child.getTop() + "---->" + ((NestedHeaderBehavior) behavior).getOffset());
131 | ViewCompat.offsetTopAndBottom(child, dependency.getBottom() - child.getTop() + ((NestedHeaderBehavior) behavior).getOffset());
132 | }
133 | }
134 |
135 | /**
136 | * 从依赖集合中获取第一个
137 | */
138 | View findFirstDependency(List views) {
139 | if (views != null && !views.isEmpty()) {
140 | return views.get(0);
141 | }
142 | return null;
143 | }
144 |
145 | /**
146 | * 矫正当前Gravity
147 | */
148 | private static int resolveGravity(int gravity) {
149 | return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
150 | }
151 |
152 |
153 | /**
154 | * 获取当前View的滑动范围,一般情况下,为view的高度。
155 | * 特殊情况下,滚动范围会小于View的高度。这种一般都是折叠布局
156 | *
157 | * @param v
158 | * @return
159 | */
160 | int getScrollRange(View v) {
161 | return v.getMeasuredHeight();
162 | }
163 |
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/behavior/MeasureLayoutBehavior.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.support.design.widget.CoordinatorLayout;
6 | import android.support.v4.view.GravityCompat;
7 | import android.support.v4.view.ViewCompat;
8 | import android.util.AttributeSet;
9 | import android.view.Gravity;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.TextView;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Author: andy.xwt
18 | * Date: 2019-07-27 20:06
19 | * Description:该Behavior只负责测量与布局基本与{@link HeaderScrollingViewBehavior}非常类似。
20 | * 这里为了帮助大家理解如何理解在如何在CoordinatorLayout下,通过Behavior来控制控件的位置与高度
21 | */
22 |
23 | public class MeasureLayoutBehavior extends CoordinatorLayout.Behavior {
24 |
25 |
26 | final Rect mTempRect1 = new Rect();
27 | final Rect mTempRect2 = new Rect();
28 |
29 | public static final String TAG = "MeasureLayoutBehavior";
30 |
31 | public MeasureLayoutBehavior() {
32 | }
33 |
34 | public MeasureLayoutBehavior(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | }
37 |
38 |
39 | /**
40 | * 依赖TextView
41 | */
42 | @Override
43 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
44 | return dependency instanceof TextView;
45 | }
46 |
47 |
48 | @Override
49 | public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
50 |
51 | //获取当前滚动控件的测量模式
52 | final int childLpHeight = child.getLayoutParams().height;
53 |
54 | //只有当前滚动控件为match_parent/wrap_content时才重新测量其高度,因为固定高度不会出现底部空白的情况
55 | if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
56 | || childLpHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
57 |
58 | //获取当前child依赖的对象集合
59 | final List dependencies = parent.getDependencies(child);
60 |
61 | final View header = findFirstDependency(dependencies);
62 | if (header != null) {
63 | if (ViewCompat.getFitsSystemWindows(header)
64 | && !ViewCompat.getFitsSystemWindows(child)) {
65 | // If the header is fitting system windows then we need to also,
66 | // otherwise we'll get CoL's compatible measuring
67 | ViewCompat.setFitsSystemWindows(child, true);
68 |
69 | if (ViewCompat.getFitsSystemWindows(child)) {
70 | // If the set succeeded, trigger a new layout and return true
71 | child.requestLayout();
72 | return true;
73 | }
74 | }
75 | //获取当前父控件中可用的距离,
76 | int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
77 | if (availableHeight == 0) {
78 |
79 | // If the measure spec doesn't specify a size, use the current height
80 | availableHeight = parent.getHeight();
81 | }
82 | //计算当前滚动控件的高度。
83 | final int height = availableHeight - header.getMeasuredHeight() + getScrollRange(header);
84 | final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height,
85 | childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
86 | ? View.MeasureSpec.EXACTLY
87 | : View.MeasureSpec.AT_MOST);
88 |
89 | //测量当前滚动的View的正确高度
90 | parent.onMeasureChild(child, parentWidthMeasureSpec,
91 | widthUsed, heightMeasureSpec, heightUsed);
92 |
93 | return true;
94 | }
95 | }
96 | return false;
97 | }
98 |
99 | @Override
100 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
101 | final List dependencies = parent.getDependencies(child);
102 | final View header = findFirstDependency(dependencies);
103 |
104 | if (header != null) {
105 | final CoordinatorLayout.LayoutParams lp =
106 | (CoordinatorLayout.LayoutParams) child.getLayoutParams();
107 | final Rect available = mTempRect1;
108 |
109 | //设置当前的宽高 为当前header的下方
110 | available.set(parent.getPaddingLeft() + lp.leftMargin,
111 | header.getBottom() + lp.topMargin,
112 | parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,
113 | parent.getHeight() + header.getBottom()
114 | - parent.getPaddingBottom() - lp.bottomMargin);
115 |
116 | //根据gravity重新计算坐标
117 | final Rect out = mTempRect2;
118 | GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
119 | child.getMeasuredHeight(), available, out, layoutDirection);
120 |
121 | //拿到坐标后重新布局
122 | child.layout(out.left, out.top, out.right, out.bottom);
123 |
124 | } else {
125 | //如果没有依赖,则调用父控件来处理布局
126 | parent.onLayoutChild(child, layoutDirection);
127 | }
128 | return true;
129 | }
130 |
131 |
132 | /**
133 | * 从依赖集合中获取第一个
134 | */
135 | View findFirstDependency(List views) {
136 | if (views != null && !views.isEmpty()) {
137 | return views.get(0);
138 | }
139 | return null;
140 | }
141 |
142 | /**
143 | * 矫正当前Gravity
144 | */
145 | private static int resolveGravity(int gravity) {
146 | return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
147 | }
148 |
149 |
150 | /**
151 | * 获取当前View的滑动范围,一般情况下,为view的高度
152 | *
153 | * @param v
154 | * @return
155 | */
156 | int getScrollRange(View v) {
157 | return v.getMeasuredHeight();
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/behavior/NestedHeaderBehavior.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.design.widget.CoordinatorLayout;
6 | import android.support.v4.view.ViewCompat;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import java.lang.ref.WeakReference;
13 |
14 | /**
15 | * Author: andy.xwt
16 | * Date: 2019-07-22 23:24
17 | * Description: 处理嵌套滑动的Behavior,仿照{@link android.support.design.widget.BottomSheetBehavior}
18 | * 对嵌套滑动相关方法不熟悉的的小伙伴可以查看{@link com.jennifer.andy.nestedscrollingdemo.ui.nested.normal_form.NestedScrollingParent2View}
19 | * 其实这里可以使用android.support.design.widget.ViewOffsetHelper,熟悉的小伙伴可以自己改造。
20 | */
21 |
22 | public class NestedHeaderBehavior extends CoordinatorLayout.Behavior {
23 |
24 |
25 | private WeakReference mNestedScrollingChildRef;
26 |
27 | public static final String TAG = "NestedHeaderBehavior";
28 |
29 | private int mOffset;//记录当前布局的偏移量
30 |
31 | public NestedHeaderBehavior(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 | }
34 |
35 | @Override
36 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
37 | mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(parent));
38 | return super.onLayoutChild(parent, child, layoutDirection);
39 | }
40 |
41 |
42 | @Override
43 | public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
44 | //只要竖直方向上就拦截
45 | return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
46 | }
47 |
48 |
49 | @Override
50 | public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
51 | View scrollingChild = mNestedScrollingChildRef.get();
52 | if (target != scrollingChild) {
53 | return;
54 | }
55 | int currentTop = child.getTop();
56 | int newTop = currentTop - dy;
57 | if (dy > 0) {//向上滑动
58 | //处理在范围内的滚动与fling
59 | if (newTop >= -child.getHeight()) {
60 | Log.i(TAG, "onNestedPreScroll:向上移动" + "currentTop--->" + currentTop + " newTop--->" + newTop);
61 | consumed[1] = dy;
62 | mOffset = -dy;
63 | ViewCompat.offsetTopAndBottom(child, -dy);
64 | coordinatorLayout.dispatchDependentViewsChanged(child);
65 | } else { //当超过后,单独处理
66 | consumed[1] = child.getHeight() + currentTop;
67 | mOffset = -consumed[1];
68 | ViewCompat.offsetTopAndBottom(child, -consumed[1]);
69 | coordinatorLayout.dispatchDependentViewsChanged(child);
70 | }
71 | }
72 | if (dy < 0) {//向下滑动
73 | if (newTop <= 0 && !target.canScrollVertically(-1)) {
74 | Log.i(TAG, "onNestedPreScroll:向下移动" + "currentTop--->" + currentTop + " newTop--->" + newTop);
75 | consumed[1] = dy;
76 | mOffset = -dy;
77 | ViewCompat.offsetTopAndBottom(child, -dy);
78 | coordinatorLayout.dispatchDependentViewsChanged(child);
79 | }
80 | }
81 |
82 | }
83 |
84 |
85 | @Override
86 | public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
87 | if (dyUnconsumed < 0) {//表示已经向下滑动到头。
88 | int currentTop = child.getTop();
89 | int newTop = currentTop - dyUnconsumed;
90 | if (newTop <= 0) {
91 | Log.i(TAG, "onNestedScroll: " + "dyUnconsumed--> " + dyUnconsumed + " currentTop--->" + currentTop + " newTop--->" + newTop);
92 | ViewCompat.offsetTopAndBottom(child, -dyUnconsumed);
93 | mOffset = -dyUnconsumed;
94 | } else {//如果当前的值大于最大的偏移量,那么就直接滚动到-currentTop就行了
95 | ViewCompat.offsetTopAndBottom(child, -currentTop);
96 | mOffset = -currentTop;
97 | }
98 | coordinatorLayout.dispatchDependentViewsChanged(child);
99 | }
100 |
101 | }
102 |
103 | /**
104 | * 这里是为了解决在CoordinatorLayout下,RecyclerView调用smoothScrollToPosition导致嵌套滑动效果失效的问题
105 | * 如有需要可将注释打开
106 | */
107 |
108 | // @Override
109 | // public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) {
110 | // super.onStopNestedScroll(coordinatorLayout, child, target, type);
111 | // String message = type == ViewCompat.TYPE_NON_TOUCH ? "TYPE_NON_TOUCH" : "TYPE_TOUCH";
112 | // Log.i(TAG, "onStopNestedScroll: " + message);
113 | // if (type == ViewCompat.TYPE_NON_TOUCH) {
114 | // if (!target.canScrollVertically(-1)) {
115 | // ViewCompat.offsetTopAndBottom(child, -child.getTop());
116 | // coordinatorLayout.dispatchDependentViewsChanged(child);
117 | // }
118 | // }
119 | //
120 | // }
121 |
122 | /**
123 | * 获取实现了NestedScrollingChild或NestedScrollingChild2接口的View。
124 | */
125 | private View findScrollingChild(View view) {
126 | if (ViewCompat.isNestedScrollingEnabled(view)) {
127 | return view;
128 | }
129 | if (view instanceof ViewGroup) {
130 | ViewGroup group = (ViewGroup) view;
131 | for (int i = 0, count = group.getChildCount(); i < count; i++) {
132 | View scrollingChild = findScrollingChild(group.getChildAt(i));
133 | if (scrollingChild != null) {
134 | return scrollingChild;
135 | }
136 | }
137 | }
138 | return null;
139 | }
140 |
141 | /**
142 | * 获取当前控件的偏移量
143 | */
144 | public int getOffset() {
145 | return mOffset;
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/cdl/behavior/ScrollingViewBehavior.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 | import android.widget.TextView;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.ui.cdl.CoordinatorLayoutDemo4Activity;
10 |
11 | /**
12 | * Author: andy.xwt
13 | * Date: 2019-07-24 22:22
14 | * Description: 在{@link CoordinatorLayoutDemo4Activity}中RecyclerView重新布局中需要的Behavior
15 | */
16 |
17 | public class ScrollingViewBehavior extends HeaderScrollingViewBehavior {
18 |
19 |
20 | public ScrollingViewBehavior(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | }
23 |
24 | /**
25 | * 依赖TextView
26 | */
27 | @Override
28 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
29 | return dependency instanceof TextView;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/NestedScrolling2DemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested;
2 |
3 | import android.animation.ArgbEvaluator;
4 | import android.graphics.Color;
5 | import android.graphics.drawable.Drawable;
6 | import android.os.Bundle;
7 | import android.support.annotation.DrawableRes;
8 | import android.support.design.widget.TabLayout;
9 | import android.support.v4.app.Fragment;
10 | import android.support.v4.graphics.drawable.DrawableCompat;
11 | import android.support.v4.view.ViewPager;
12 | import android.support.v7.app.AppCompatActivity;
13 | import android.view.View;
14 | import android.widget.ImageView;
15 | import android.widget.TextView;
16 |
17 | import com.jennifer.andy.nestedscrollingdemo.R;
18 | import com.jennifer.andy.nestedscrollingdemo.adapter.BaseFragmentItemAdapter;
19 | import com.jennifer.andy.nestedscrollingdemo.ui.TabFragment;
20 | import com.jennifer.andy.nestedscrollingdemo.view.StickyNavLayout;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | /**
26 | * Author: andy.xwt
27 | * Date: 2019-07-08 22:11
28 | * Description:
29 | */
30 |
31 | public class NestedScrolling2DemoActivity extends AppCompatActivity {
32 |
33 |
34 | private TabLayout mTabLayout;
35 | private ViewPager mViewPager;
36 | private StickyNavLayout mStickyNavLayout;
37 | private ImageView mBackImageView;
38 | private TextView mTitleView;
39 |
40 | public static final int FRAGMENT_COUNT = 4;
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_nested_scrolling2_demo);
46 | findView();
47 | initData();
48 | }
49 |
50 | private void findView() {
51 | mTabLayout = findViewById(R.id.sl_tab);
52 | mViewPager = findViewById(R.id.sl_viewpager);
53 | mStickyNavLayout = findViewById(R.id.sick_layout);
54 | mBackImageView = findViewById(R.id.iv_back);
55 | mTitleView = findViewById(R.id.tv_title);
56 |
57 | initToolBar(R.drawable.ic_action_back_black, 0);
58 | }
59 |
60 | private void initData() {
61 | mViewPager.setAdapter(new BaseFragmentItemAdapter(getSupportFragmentManager(), initFragments(), initTitles()));
62 | mTabLayout.setupWithViewPager(mViewPager);
63 | mStickyNavLayout.setScrollChangeListener(new StickyNavLayout.ScrollChangeListener() {
64 | @Override
65 | public void onScroll(float moveRatio) {
66 | initToolBar(R.drawable.ic_action_back_white, moveRatio);
67 | }
68 | });
69 | }
70 |
71 | private void initToolBar(@DrawableRes int backResId, float moveRatio) {
72 | mBackImageView.setOnClickListener(new View.OnClickListener() {
73 | @Override
74 | public void onClick(View v) {
75 | finish();
76 | }
77 | });
78 |
79 | ArgbEvaluator argbEvaluator = new ArgbEvaluator();
80 | int color = (int) argbEvaluator.evaluate(moveRatio, Color.WHITE, Color.BLACK);
81 | Drawable wrapDrawable = DrawableCompat.wrap(getResources().getDrawable(backResId));
82 | DrawableCompat.setTint(wrapDrawable, color);
83 |
84 | mBackImageView.setImageDrawable(wrapDrawable);
85 | mTitleView.setAlpha(moveRatio);
86 |
87 |
88 | }
89 |
90 | private List initFragments() {
91 | List fragments = new ArrayList<>();
92 | for (int i = 0; i < FRAGMENT_COUNT; i++) {
93 | fragments.add(TabFragment.newInstance("NestedScrolling2Demo"));
94 | }
95 | return fragments;
96 | }
97 |
98 | private List initTitles() {
99 | List titles = new ArrayList<>();
100 | titles.add("首页");
101 | titles.add("全部");
102 | titles.add("作者");
103 | titles.add("专辑");
104 | return titles;
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/NestedScrollingParent2Activity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.view.ViewPager;
7 | import android.support.v7.app.AppCompatActivity;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.R;
10 | import com.jennifer.andy.nestedscrollingdemo.adapter.BaseFragmentItemAdapter;
11 | import com.jennifer.andy.nestedscrollingdemo.ui.TabFragment;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * Author: andy.xwt
18 | * Date: 2018/8/8 13:56
19 | * Description:使用NestedScrollingParent2的实现嵌套滑动
20 | */
21 |
22 | public class NestedScrollingParent2Activity extends AppCompatActivity {
23 |
24 | private TabLayout mTabLayout;
25 | private ViewPager mViewPager;
26 |
27 | public static final int FRAGMENT_COUNT = 4;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_nested_srolling_parent2);
33 | findView();
34 | initData();
35 | }
36 |
37 | private void findView() {
38 | mTabLayout = findViewById(R.id.tab_layout);
39 | mViewPager = findViewById(R.id.view_pager);
40 | }
41 |
42 | private void initData() {
43 | mViewPager.setAdapter(new BaseFragmentItemAdapter(getSupportFragmentManager(), initFragments(), initTitles()));
44 | mViewPager.setOffscreenPageLimit(FRAGMENT_COUNT);
45 | mTabLayout.setupWithViewPager(mViewPager);
46 | }
47 |
48 | private List initFragments() {
49 | List fragments = new ArrayList<>();
50 | for (int i = 0; i < FRAGMENT_COUNT; i++) {
51 | fragments.add(TabFragment.newInstance("实现NestedScrollingParent2接口"));
52 | }
53 | return fragments;
54 | }
55 |
56 | private List initTitles() {
57 | List titles = new ArrayList<>();
58 | titles.add("首页");
59 | titles.add("全部");
60 | titles.add("作者");
61 | titles.add("专辑");
62 | return titles;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/NestedScrollingParentActivity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.view.ViewPager;
7 | import android.support.v7.app.AppCompatActivity;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.R;
10 | import com.jennifer.andy.nestedscrollingdemo.adapter.BaseFragmentItemAdapter;
11 | import com.jennifer.andy.nestedscrollingdemo.ui.TabFragment;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * Author: andy.xwt
18 | * Date: 2018/8/8 13:56
19 | * Description:使用NestedScrollingParent接口的嵌套滑动
20 | */
21 |
22 | public class NestedScrollingParentActivity extends AppCompatActivity {
23 |
24 | private TabLayout mTabLayout;
25 | private ViewPager mViewPager;
26 |
27 | public static final int FRAGMENT_COUNT = 4;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_nested_srolling_parent);
33 | findView();
34 | initData();
35 | }
36 |
37 | private void findView() {
38 | mTabLayout = findViewById(R.id.tab_layout);
39 | mViewPager = findViewById(R.id.view_pager);
40 | }
41 |
42 | private void initData() {
43 | mViewPager.setAdapter(new BaseFragmentItemAdapter(getSupportFragmentManager(), initFragments(), initTitles()));
44 | mViewPager.setOffscreenPageLimit(FRAGMENT_COUNT);
45 | mTabLayout.setupWithViewPager(mViewPager);
46 | }
47 |
48 | private List initFragments() {
49 | List fragments = new ArrayList<>();
50 | for (int i = 0; i < FRAGMENT_COUNT; i++) {
51 | fragments.add(TabFragment.newInstance("实现NestedScrollingParent接口"));
52 | }
53 | return fragments;
54 | }
55 |
56 | private List initTitles() {
57 | List titles = new ArrayList<>();
58 | titles.add("首页");
59 | titles.add("全部");
60 | titles.add("作者");
61 | titles.add("专辑");
62 | return titles;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/NestedTraditionActivity.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.view.ViewPager;
7 | import android.support.v7.app.AppCompatActivity;
8 |
9 | import com.jennifer.andy.nestedscrollingdemo.R;
10 | import com.jennifer.andy.nestedscrollingdemo.adapter.BaseFragmentItemAdapter;
11 | import com.jennifer.andy.nestedscrollingdemo.ui.TabFragment;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * Author: andy.xwt
18 | * Date: 2018/8/8 13:56
19 | * Description:使用传统机制来实现嵌套滑动
20 | */
21 |
22 | public class NestedTraditionActivity extends AppCompatActivity {
23 |
24 |
25 | private TabLayout mTabLayout;
26 | private ViewPager mViewPager;
27 |
28 | public static final int FRAGMENT_COUNT = 4;
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_nested_tradition);
34 | findView();
35 | initData();
36 | }
37 |
38 | private void findView() {
39 | mTabLayout = findViewById(R.id.tab_layout);
40 | mViewPager = findViewById(R.id.view_pager);
41 | }
42 |
43 | private void initData() {
44 | mViewPager.setAdapter(new BaseFragmentItemAdapter(getSupportFragmentManager(), initFragments(), initTitles()));
45 | mViewPager.setOffscreenPageLimit(FRAGMENT_COUNT);
46 | mTabLayout.setupWithViewPager(mViewPager);
47 | }
48 |
49 | private List initFragments() {
50 | List fragments = new ArrayList<>();
51 | for (int i = 0; i < FRAGMENT_COUNT; i++) {
52 | fragments.add(TabFragment.newInstance("传统事件分发机制嵌套滑动"));
53 | }
54 | return fragments;
55 | }
56 |
57 | private List initTitles() {
58 | List titles = new ArrayList<>();
59 | titles.add("首页");
60 | titles.add("全部");
61 | titles.add("作者");
62 | titles.add("专辑");
63 | return titles;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/normal_form/NestedScrollingChild2View.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested.normal_form;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.view.NestedScrollingChild2;
6 | import android.support.v4.view.NestedScrollingChildHelper;
7 | import android.support.v4.view.ViewCompat;
8 | import android.view.MotionEvent;
9 | import android.view.VelocityTracker;
10 | import android.view.View;
11 | import android.view.ViewConfiguration;
12 | import android.widget.OverScroller;
13 |
14 | import static android.support.v4.view.ViewCompat.TYPE_NON_TOUCH;
15 |
16 | /**
17 | * Author: andy.xwt
18 | * Date: 2019-06-28 00:12
19 | * Description: NestedScrollingChild2与NestedScrollingChild接口的最大差异就是处理fling,
20 | * 所以我们直接查看fling效果的处理
21 | */
22 |
23 | public class NestedScrollingChild2View extends View implements NestedScrollingChild2 {
24 |
25 | private NestedScrollingChildHelper mScrollingChildHelper = new NestedScrollingChildHelper(this);
26 | private final int mMinFlingVelocity;
27 | private final int mMaxFlingVelocity;
28 | private OverScroller mScroller;
29 |
30 | public NestedScrollingChild2View(Context context) {
31 | super(context);
32 | ViewConfiguration vc = ViewConfiguration.get(context);
33 | mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
34 | mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
35 | mScroller = new OverScroller(context);
36 | }
37 |
38 |
39 | @Override
40 | public boolean startNestedScroll(int axes, int type) {
41 | return mScrollingChildHelper.startNestedScroll(axes);
42 | }
43 |
44 |
45 | @Override
46 | public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow, int type) {
47 | return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
48 | }
49 |
50 |
51 | @Override
52 | public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type) {
53 | return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow, type);
54 | }
55 |
56 | @Override
57 | public void stopNestedScroll(int type) {
58 | mScrollingChildHelper.stopNestedScroll(type);
59 | }
60 |
61 | @Override
62 | public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
63 | return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
64 | }
65 |
66 | @Override
67 | public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
68 | return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
69 | }
70 |
71 |
72 | @Override
73 | public void setNestedScrollingEnabled(boolean enabled) {
74 | mScrollingChildHelper.setNestedScrollingEnabled(enabled);
75 | }
76 |
77 | @Override
78 | public boolean isNestedScrollingEnabled() {
79 | return mScrollingChildHelper.isNestedScrollingEnabled();
80 | }
81 |
82 | @Override
83 | public boolean hasNestedScrollingParent(int type) {
84 | return mScrollingChildHelper.hasNestedScrollingParent(type);
85 | }
86 |
87 |
88 | private VelocityTracker mVelocityTracker;
89 |
90 | @Override
91 | public boolean onTouchEvent(MotionEvent event) {
92 | int action = event.getActionMasked();
93 |
94 | int y = (int) event.getY();
95 | int x = (int) event.getX();
96 |
97 | //添加速度检测器,用于处理fling效果
98 | if (mVelocityTracker == null) {
99 | mVelocityTracker = VelocityTracker.obtain();
100 | }
101 | mVelocityTracker.addMovement(event);
102 |
103 | switch (action) {
104 | case MotionEvent.ACTION_UP: {//当手指抬起的时,结束嵌套滑动传递,并判断是否产生了fling效果
105 | mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
106 | int xvel = (int) mVelocityTracker.getXVelocity();
107 | int yvel = (int) mVelocityTracker.getYVelocity();
108 | fling(xvel, yvel);
109 | mVelocityTracker.clear();
110 | stopNestedScroll(ViewCompat.TYPE_TOUCH);
111 | break;
112 | }
113 |
114 | }
115 |
116 | return super.onTouchEvent(event);
117 | }
118 |
119 | private boolean fling(int velocityX, int velocityY) {
120 | //判断速度是否足够大。如果够大才执行fling
121 | if (Math.abs(velocityX) < mMinFlingVelocity) {
122 | velocityX = 0;
123 | }
124 | if (Math.abs(velocityY) < mMinFlingVelocity) {
125 | velocityY = 0;
126 | }
127 | if (velocityX == 0 && velocityY == 0) {
128 | return false;
129 | }
130 | if (dispatchNestedPreFling(velocityX, velocityY)) {
131 | boolean canScroll = canScroll();
132 | //将fling效果传递给父控件
133 | dispatchNestedFling(velocityX, velocityY, canScroll);
134 |
135 | //子控件在处理fling效果
136 | if (canScroll) {
137 | //通知父控件开始fling事件,注意这里默认传递的是竖直方向,具体方向由子控件决定
138 | startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
139 | velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
140 | velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
141 | doFling(velocityX, velocityY);
142 | return true;
143 | }
144 |
145 | }
146 | return false;
147 |
148 | }
149 |
150 | private int mLastFlingX;
151 | private int mLastFlingY;
152 | private final int[] mScrollConsumed = new int[2];
153 |
154 | /**
155 | * 实际的fling处理效果
156 | */
157 | private void doFling(int velocityX, int velocityY) {
158 | mScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
159 | postInvalidate();
160 | }
161 |
162 | @Override
163 | public void computeScroll() {
164 | if (mScroller.computeScrollOffset()) {
165 | int x = mScroller.getCurrX();
166 | int y = mScroller.getCurrY();
167 | int dx = x - mLastFlingX;
168 | int dy = y - mLastFlingY;
169 |
170 | mLastFlingX = x;
171 | mLastFlingY = y;
172 | //在子控件处理fling之前,先判断父控件是否消耗
173 | if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, null, TYPE_NON_TOUCH)) {
174 | //计算父控件消耗后,剩下的距离
175 | dx -= mScrollConsumed[0];
176 | dy -= mScrollConsumed[1];
177 |
178 | //因为之前默认向父控件传递的竖直方向,所以这里子控件也消耗剩下的竖直方向
179 | int hResult = 0;
180 | int vResult = 0;
181 | int leaveDx = 0;//子控件水平fling 消耗的距离
182 | int leaveDy = 0;//父控件竖直fling 消耗的距离
183 |
184 | if (dx != 0) {
185 | leaveDx = childFlingX(dx);
186 | hResult = dx - leaveDx;//得到子控件消耗后剩下的水平距离
187 | }
188 | if (dy != 0) {
189 | leaveDy = childFlingY(dy);//得到子控件消耗后剩下的竖直距离
190 | vResult = dy - leaveDy;
191 | }
192 |
193 | dispatchNestedScroll(leaveDx, leaveDy, hResult, vResult, null, TYPE_NON_TOUCH);
194 |
195 | }
196 | } else {
197 | stopNestedScroll(TYPE_NON_TOUCH);
198 |
199 | }
200 |
201 | }
202 |
203 | /**
204 | * 判断子子控件是否能够滑动,只有能滑动才能处理fling
205 | */
206 | private boolean canScroll() {
207 | //具体逻辑自己实现
208 | return false;
209 | }
210 |
211 | /**
212 | * 子控件消耗多少竖直方向上的fling,由子控件自己决定
213 | *
214 | * @param dy 父控件消耗部分竖直fling后,剩余的距离
215 | * @return 子控件竖直fling,消耗的距离
216 | */
217 | private int childFlingY(int dy) {
218 |
219 | return 0;
220 | }
221 |
222 | /**
223 | * 子控件消耗多少竖直方向上的fling,由子控件自己决定
224 | *
225 | * @param dx 父控件消耗部分水平fling后,剩余的距离
226 | * @return 子控件水平fling,消耗的距离
227 | */
228 | private int childFlingX(int dx) {
229 | return 0;
230 | }
231 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/normal_form/NestedScrollingChildView.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested.normal_form;
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.view.MotionEvent;
9 | import android.view.VelocityTracker;
10 | import android.view.View;
11 | import android.view.ViewConfiguration;
12 |
13 | /**
14 | * Author: andy.xwt
15 | * Date: 2019-06-28 00:12
16 | * Description: 嵌套滑动中,实现NestedScrollingChild接口的基本范式代码
17 | */
18 |
19 | public class NestedScrollingChildView extends View implements NestedScrollingChild {
20 |
21 | private NestedScrollingChildHelper mScrollingChildHelper = new NestedScrollingChildHelper(this);
22 |
23 | private final int mMinFlingVelocity;
24 | private final int mMaxFlingVelocity;
25 |
26 |
27 | public NestedScrollingChildView(Context context) {
28 | super(context);
29 | ViewConfiguration vc = ViewConfiguration.get(context);
30 | mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
31 | mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
32 | }
33 |
34 |
35 | /**
36 | * 开启一个嵌套滑动
37 | *
38 | * @param axes 支持的嵌套滑动方法,分为水平方向,竖直方向,或不指定
39 | * @return 如果返回true, 表示当前子view已经找了一起嵌套滑动的view
40 | */
41 | @Override
42 | public boolean startNestedScroll(int axes) {
43 | return mScrollingChildHelper.startNestedScroll(axes);
44 | }
45 |
46 |
47 | /**
48 | * 在子view滑动前,将事件分发给父view,由父view判断消耗多少
49 | *
50 | * @param dx 水平方向嵌套滑动的子View想要变化的距离 dx<0 向右滑动 dx>0 向左滑动
51 | * @param dy 垂直方向嵌套滑动的子View想要变化的距离 dy<0 向下滑动 dy>0 向上滑动
52 | * @param consumed 子view传给父view数组,用于存储父view水平与竖直方向上消耗的距离,consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离
53 | * @param offsetInWindow 子view在当前window的偏移量
54 | * @return 如果返回true, 表示父view已经消耗了
55 | */
56 | @Override
57 | public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow) {
58 | return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
59 | }
60 |
61 |
62 | /**
63 | * 当父view消耗事件后,子view处理后,又继续将事件分发给父view,由父view判断是否消耗剩下的距离。
64 | *
65 | * @param dxConsumed 水平方向嵌套滑动的子View滑动的距离(消耗的距离)
66 | * @param dyConsumed 垂直方向嵌套滑动的子View滑动的距离(消耗的距离)
67 | * @param dxUnconsumed 水平方向嵌套滑动的子View未滑动的距离(未消耗的距离)
68 | * @param dyUnconsumed 垂直方向嵌套滑动的子View未滑动的距离(未消耗的距离)
69 | * @param offsetInWindow 子view在当前window的偏移量
70 | * @return 如果返回true, 表示父view又继续消耗了
71 | */
72 | @Override
73 | public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) {
74 | return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
75 | }
76 |
77 | /**
78 | * 子view停止嵌套滑动
79 | */
80 | @Override
81 | public void stopNestedScroll() {
82 | mScrollingChildHelper.stopNestedScroll();
83 | }
84 |
85 |
86 | /**
87 | * 当子view产生fling滑动时,判断父view是否处拦截fling,如果父View处理了fling,那子view就没有办法处理fling了。
88 | *
89 | * @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
90 | * @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
91 | * @return 如果返回true, 表示父view拦截了fling
92 | */
93 | @Override
94 | public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
95 | return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
96 | }
97 |
98 | /**
99 | * 当父view不拦截子view的fling,那么子view会调用该方法将fling,传给父view进行处理
100 | *
101 | * @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
102 | * @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
103 | * @param consumed 子view是否可以消耗该fling,也可以说是子view是否消耗掉了该fling
104 | * @return 父view是否消耗了该fling
105 | */
106 | @Override
107 | public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
108 | return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
109 | }
110 |
111 | /**
112 | * 设置当前子view是否支持嵌套滑动,如果不支持,那么父view是不能够响应嵌套滑动的
113 | *
114 | * @param enabled true 支持
115 | */
116 | @Override
117 | public void setNestedScrollingEnabled(boolean enabled) {
118 | mScrollingChildHelper.setNestedScrollingEnabled(enabled);
119 | }
120 |
121 | /**
122 | * 当前子view是否支持嵌套滑动
123 | */
124 | @Override
125 | public boolean isNestedScrollingEnabled() {
126 | return mScrollingChildHelper.isNestedScrollingEnabled();
127 | }
128 |
129 | /**
130 | * 判断当前子view是否拥有嵌套滑动的父view
131 | */
132 | @Override
133 | public boolean hasNestedScrollingParent() {
134 | return mScrollingChildHelper.hasNestedScrollingParent();
135 | }
136 |
137 | private int mLastY;
138 | private int mLastX;
139 | private final int[] mScrollConsumed = new int[2];
140 | private final int[] mScrollOffset = new int[2];
141 | private VelocityTracker mVelocityTracker;
142 |
143 | @Override
144 | public boolean onTouchEvent(MotionEvent event) {
145 |
146 | int action = event.getActionMasked();
147 |
148 | int y = (int) event.getY();
149 | int x = (int) event.getX();
150 |
151 | //添加速度检测器,用于处理fling效果
152 | if (mVelocityTracker == null) {
153 | mVelocityTracker = VelocityTracker.obtain();
154 | }
155 | mVelocityTracker.addMovement(event);
156 |
157 | switch (action) {
158 | case MotionEvent.ACTION_DOWN: {
159 | mLastX = x;
160 | mLastY = y;
161 | //自己的处理逻辑,判断传递竖直还是水平方向,这里默认是设置的竖直方向
162 | startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
163 | break;
164 | }
165 | case MotionEvent.ACTION_MOVE: {
166 | int dy = mLastY - y;
167 | int dx = mLastX - x;
168 |
169 | //将事件传递给父控件,并记录父控件消耗的距离。
170 | if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
171 | dx -= mScrollConsumed[0];
172 | dy -= mScrollConsumed[1];
173 | //子控件处理事件,并将未处理完的事件传递给父控件
174 | scrollNested(dx, dy);
175 | }
176 | //如果找不到嵌套滑动的父控件,自己就处理事件。
177 | childScroll(dx, dy);
178 | break;
179 | }
180 | case MotionEvent.ACTION_UP: {//当手指抬起的时,结束嵌套滑动传递,并判断是否产生了fling效果
181 | mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
182 | int xvel = (int) mVelocityTracker.getXVelocity();
183 | int yvel = (int) mVelocityTracker.getYVelocity();
184 | fling(xvel, yvel);
185 | mVelocityTracker.clear();
186 | stopNestedScroll();
187 | break;
188 | }
189 | case MotionEvent.ACTION_CANCEL: {//当取消滑动的时,结束嵌套滑动传递
190 | stopNestedScroll();
191 | mVelocityTracker.clear();
192 | break;
193 | }
194 | }
195 |
196 | return super.onTouchEvent(event);
197 | }
198 |
199 | /**
200 | * 子控件处理事件,并将未处理完的事件传递给父控件
201 | *
202 | * @param x 水平方向移动距离
203 | * @param y 竖直方向移动距离
204 | */
205 | private void scrollNested(int x, int y) {
206 | int unConsumedX = 0, unConsumedY = 0;
207 | int consumedX = 0, consumedY = 0;
208 |
209 | //子控件消耗多少事件,由自己决定
210 | if (x != 0) {
211 | consumedX = childConsumeX(x);
212 | unConsumedX = x - consumedX;
213 | }
214 | if (y != 0) {
215 | consumedY = childConsumeY(y);
216 | unConsumedY = y - consumedY;
217 | }
218 |
219 | //子控件处理事件
220 | childScroll(consumedX, consumedY);
221 |
222 | //子控件处理后,又将剩下的事件传递给父控件
223 | if (!dispatchNestedScroll(consumedX, consumedY, unConsumedX, unConsumedY, mScrollOffset)) {
224 | //传给父控件处理后,剩下的逻辑自己实现
225 | }
226 | //传递给父控件,父控件不处理,那么子控件就继续处理。
227 | childScroll(unConsumedX, unConsumedY);
228 |
229 | }
230 |
231 | /**
232 | * 子控件滑动逻辑
233 | */
234 | private void childScroll(int x, int y) {
235 | //子控件怎么滑动,自己实现
236 | }
237 |
238 |
239 | /**
240 | * 子控件水平方向消耗多少距离
241 | */
242 | private int childConsumeX(int x) {
243 | //具体逻辑由自己实现
244 | return 0;
245 | }
246 |
247 | /**
248 | * 子控件竖直方向消耗距离
249 | */
250 | private int childConsumeY(int y) {
251 | //具体逻辑由自己实现
252 | return 0;
253 | }
254 |
255 | private boolean fling(int velocityX, int velocityY) {
256 | //判断速度是否足够大。如果够大才执行fling
257 | if (Math.abs(velocityX) < mMinFlingVelocity) {
258 | velocityX = 0;
259 | }
260 | if (Math.abs(velocityY) < mMinFlingVelocity) {
261 | velocityY = 0;
262 | }
263 | if (velocityX == 0 && velocityY == 0) {
264 | return false;
265 | }
266 | if (dispatchNestedPreFling(velocityX, velocityY)) {
267 | boolean consumed = canScroll();
268 | //将fling效果传递给父控件
269 | dispatchNestedFling(velocityX, velocityY, consumed);
270 | //然后子控件在处理fling效果
271 | childFling();
272 |
273 | }
274 | return false;
275 |
276 | }
277 |
278 |
279 | /**
280 | * 判断子子控件是否能够滑动,只有能滑动才能处理fling
281 | */
282 | private boolean canScroll() {
283 | //具体逻辑自己实现
284 | return false;
285 | }
286 |
287 | /**
288 | * 子控件处理fling效果
289 | */
290 | private void childFling() {
291 | //具体逻辑自己实现
292 | }
293 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/normal_form/NestedScrollingParent2View.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested.normal_form;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.v4.view.NestedScrollingParent2;
6 | import android.support.v4.view.NestedScrollingParentHelper;
7 | import android.view.View;
8 | import android.widget.LinearLayout;
9 |
10 | /**
11 | * Author: andy.xwt
12 | * Date: 2019-06-28 00:13
13 | * Description:
14 | */
15 |
16 | public class NestedScrollingParent2View extends LinearLayout implements NestedScrollingParent2 {
17 |
18 | private NestedScrollingParentHelper mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
19 |
20 | public NestedScrollingParent2View(Context context) {
21 | super(context);
22 | }
23 |
24 |
25 | /**
26 | * 有嵌套滑动到来了,判断父view是否接受嵌套滑动
27 | *
28 | * @param child 嵌套滑动对应的父类的子类(因为嵌套滑动对于的父View不一定是一级就能找到的,可能挑了两级父View的父View,child的辈分>=target)
29 | * @param target 具体嵌套滑动的那个子类
30 | * @param nestedScrollAxes 支持嵌套滚动轴。水平方向,垂直方向,或者不指定
31 | * @param type 滑动类型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动
32 | */
33 | @Override
34 | public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int nestedScrollAxes, int type) {
35 | //自己处理逻辑
36 | return true;
37 | }
38 |
39 | /**
40 | * 当父view接受嵌套滑动,当onStartNestedScroll方法返回true该方法会调用
41 | *
42 | * @param type 滑动类型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动
43 | */
44 | @Override
45 | public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
46 | mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type);
47 | }
48 |
49 | /**
50 | * 在嵌套滑动的子View未滑动之前,判断父view是否优先与子view处理(也就是父view可以先消耗,然后给子view消耗)
51 | *
52 | * @param target 具体嵌套滑动的那个子类
53 | * @param dx 水平方向嵌套滑动的子View想要变化的距离
54 | * @param dy 垂直方向嵌套滑动的子View想要变化的距离 dy<0向下滑动 dy>0 向上滑动
55 | * @param consumed 这个参数要我们在实现这个函数的时候指定,回头告诉子View当前父View消耗的距离
56 | * consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离 好让子view做出相应的调整
57 | * @param type 滑动类型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动
58 | */
59 | @Override
60 | public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
61 | //自己处理逻辑
62 | }
63 |
64 | /**
65 | * 嵌套滑动的子View在滑动之后,判断父view是否继续处理(也就是父消耗一定距离后,子再消耗,最后判断父消耗不)
66 | *
67 | * @param target 具体嵌套滑动的那个子类
68 | * @param dxConsumed 水平方向嵌套滑动的子View滑动的距离(消耗的距离)
69 | * @param dyConsumed 垂直方向嵌套滑动的子View滑动的距离(消耗的距离)
70 | * @param dxUnconsumed 水平方向嵌套滑动的子View未滑动的距离(未消耗的距离)
71 | * @param dyUnconsumed 垂直方向嵌套滑动的子View未滑动的距离(未消耗的距离)
72 | * @param type 滑动类型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动
73 | */
74 | @Override
75 | public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
76 | //自己处理逻辑
77 | }
78 |
79 | /**
80 | * 嵌套滑动结束
81 | *
82 | * @param type 滑动类型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动
83 | */
84 | @Override
85 | public void onStopNestedScroll(@NonNull View child, int type) {
86 | mNestedScrollingParentHelper.onStopNestedScroll(child, type);
87 | }
88 |
89 | @Override
90 | public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
91 | //自己判断是否处理
92 | return false;
93 | }
94 |
95 | @Override
96 | public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
97 | //自己处理逻辑
98 | return false;
99 | }
100 |
101 |
102 | @Override
103 | public int getNestedScrollAxes() {
104 | return mNestedScrollingParentHelper.getNestedScrollAxes();
105 | }
106 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/ui/nested/normal_form/NestedScrollingParentView.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.ui.nested.normal_form;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.v4.view.NestedScrollingParent;
6 | import android.support.v4.view.NestedScrollingParentHelper;
7 | import android.view.View;
8 | import android.widget.LinearLayout;
9 |
10 | /**
11 | * Author: andy.xwt
12 | * Date: 2019-06-28 00:13
13 | * Description:
14 | */
15 |
16 | public class NestedScrollingParentView extends LinearLayout implements NestedScrollingParent {
17 |
18 | private NestedScrollingParentHelper mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
19 |
20 | public NestedScrollingParentView(Context context) {
21 | super(context);
22 | }
23 |
24 | /**
25 | * 有嵌套滑动到来了,判断父view是否接受嵌套滑动
26 | *
27 | * @param child 嵌套滑动对应的父类的子类(因为嵌套滑动对于的父View不一定是一级就能找到的,可能挑了两级父View的父View,child的辈分>=target)
28 | * @param target 具体嵌套滑动的那个子类
29 | * @param nestedScrollAxes 支持嵌套滚动轴。水平方向,垂直方向,或者不指定
30 | * @return 父view是否接受嵌套滑动, 只有接受了才会执行剩下的嵌套滑动方法
31 | */
32 | @Override
33 | public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int nestedScrollAxes) {
34 | return super.onStartNestedScroll(child, target, nestedScrollAxes);
35 | }
36 |
37 | /**
38 | * 当onStartNestedScroll返回为true时,也就是父view接受嵌套滑动时,该方法才会调用
39 | */
40 | @Override
41 | public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes) {
42 | mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
43 | }
44 |
45 | /**
46 | * 在嵌套滑动的子View未滑动之前,判断父view是否优先与子view处理(也就是父view可以先消耗,然后给子view消耗)
47 | *
48 | * @param target 具体嵌套滑动的那个子类
49 | * @param dx 水平方向嵌套滑动的子View想要变化的距离
50 | * @param dy 垂直方向嵌套滑动的子View想要变化的距离 dy<0向下滑动 dy>0 向上滑动
51 | * @param consumed 这个参数要我们在实现这个函数的时候指定,回头告诉子View当前父View消耗的距离
52 | * consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离 好让子view做出相应的调整
53 | */
54 | @Override
55 | public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
56 | //自己实现
57 | }
58 |
59 | /**
60 | * 嵌套滑动的子View在滑动之后,判断父view是否继续处理(也就是父消耗一定距离后,子再消耗,最后判断父消耗不)
61 | *
62 | * @param target 具体嵌套滑动的那个子类
63 | * @param dxConsumed 水平方向嵌套滑动的子View滑动的距离(消耗的距离)
64 | * @param dyConsumed 垂直方向嵌套滑动的子View滑动的距离(消耗的距离)
65 | * @param dxUnconsumed 水平方向嵌套滑动的子View未滑动的距离(未消耗的距离)
66 | * @param dyUnconsumed 垂直方向嵌套滑动的子View未滑动的距离(未消耗的距离)
67 | */
68 | @Override
69 | public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
70 | super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
71 | }
72 |
73 | /**
74 | * 嵌套滑动结束
75 | */
76 | @Override
77 | public void onStopNestedScroll(@NonNull View child) {
78 | mNestedScrollingParentHelper.onStopNestedScroll(child);
79 | }
80 |
81 | /**
82 | * 当子view产生fling滑动时,判断父view是否处拦截fling,如果父View处理了fling,那子view就没有办法处理fling了。
83 | *
84 | * @param target 具体嵌套滑动的那个子类
85 | * @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
86 | * @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
87 | * @return 父view是否拦截该fling
88 | */
89 | @Override
90 | public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
91 | return super.onNestedPreFling(target, velocityX, velocityY);
92 | }
93 |
94 |
95 | /**
96 | * 当父view不拦截该fling,那么子view会将fling传入父view
97 | *
98 | * @param target 具体嵌套滑动的那个子类
99 | * @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
100 | * @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
101 | * @param consumed 子view是否可以消耗该fling,也可以说是子view是否消耗掉了该fling
102 | * @return 父view是否消耗了该fling
103 | */
104 | @Override
105 | public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
106 | return super.onNestedFling(target, velocityX, velocityY, consumed);
107 | }
108 |
109 | /**
110 | * 返回当前父view嵌套滑动的方向,分为水平方向与,垂直方法,或者不变
111 | */
112 | @Override
113 | public int getNestedScrollAxes() {
114 | return mNestedScrollingParentHelper.getNestedScrollAxes();
115 | }
116 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/view/DependedView.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.view;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.ViewCompat;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 | import android.view.View;
8 | import android.view.ViewConfiguration;
9 |
10 | /**
11 | * Author: andy.xwt
12 | * Date: 2019-07-11 10:42
13 | * Description: 被依赖的TextView,当该view的位置发生改变的时候,那么其他依赖DependedView都会发生改变
14 | */
15 |
16 | public class DependedView extends View {
17 |
18 | private float mLastX;
19 | private float mLastY;
20 | private final int mDragSlop;//最小的滑动距离
21 |
22 |
23 | public DependedView(Context context) {
24 | this(context, null);
25 | }
26 |
27 | public DependedView(Context context, AttributeSet attrs) {
28 | this(context, attrs, 0);
29 | }
30 |
31 | public DependedView(Context context, AttributeSet attrs, int defStyleAttr) {
32 | super(context, attrs, defStyleAttr);
33 | mDragSlop = ViewConfiguration.get(context).getScaledTouchSlop();
34 | }
35 |
36 |
37 | @Override
38 | public boolean onTouchEvent(MotionEvent event) {
39 | int action = event.getAction();
40 | switch (action) {
41 | case MotionEvent.ACTION_DOWN:
42 | mLastX = event.getX();
43 | mLastY = event.getY();
44 | break;
45 |
46 | case MotionEvent.ACTION_MOVE:
47 | int dx = (int) (event.getX() - mLastX);
48 | int dy = (int) (event.getY() - mLastY);
49 | if (Math.abs(dx) > mDragSlop || Math.abs(dy) > mDragSlop) {
50 | System.out.println("dx--->" + dx + "dy--->" + dy + "--->" + mDragSlop);
51 | ViewCompat.offsetTopAndBottom(this, dy);
52 | ViewCompat.offsetLeftAndRight(this, dx);
53 | }
54 | mLastX = event.getX();
55 | mLastY = event.getY();
56 | break;
57 |
58 | default:
59 | break;
60 |
61 | }
62 |
63 | return true;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/view/NestedScrollingParent2Layout.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.view;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.view.NestedScrollingParent2;
7 | import android.support.v4.view.NestedScrollingParentHelper;
8 | import android.support.v4.view.ViewCompat;
9 | import android.support.v4.view.ViewPager;
10 | import android.util.AttributeSet;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.LinearLayout;
14 |
15 | import com.jennifer.andy.nestedscrollingdemo.R;
16 |
17 | /**
18 | * Author: andy.xwt
19 | * Date: 2018/8/8 14:28
20 | * Description:NestedScrolling2机制下的嵌套滑动,实现NestedScrollingParent2接口下,处理fling效果的区别
21 | */
22 |
23 | public class NestedScrollingParent2Layout extends LinearLayout implements NestedScrollingParent2 {
24 |
25 | private View mTopView;
26 | private View mNavView;
27 | private View mViewPager;
28 | private int mTopViewHeight;
29 |
30 |
31 | private NestedScrollingParentHelper mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
32 |
33 | public NestedScrollingParent2Layout(Context context) {
34 | this(context, null);
35 | }
36 |
37 | public NestedScrollingParent2Layout(Context context, @Nullable AttributeSet attrs) {
38 | this(context, attrs, 0);
39 | }
40 |
41 | public NestedScrollingParent2Layout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
42 | super(context, attrs, defStyleAttr);
43 | setOrientation(VERTICAL);
44 | }
45 |
46 |
47 | @Override
48 | public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) {
49 | return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
50 | }
51 |
52 |
53 | @Override
54 | public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
55 | mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type);
56 | }
57 |
58 | /**
59 | * 在嵌套滑动的子View未滑动之前,判断父view是否优先与子view处理(也就是父view可以先消耗,然后给子view消耗)
60 | *
61 | * @param target 具体嵌套滑动的那个子类
62 | * @param dx 水平方向嵌套滑动的子View想要变化的距离
63 | * @param dy 垂直方向嵌套滑动的子View想要变化的距离 dy<0向下滑动 dy>0 向上滑动
64 | * @param consumed 这个参数要我们在实现这个函数的时候指定,回头告诉子View当前父View消耗的距离
65 | * consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离 好让子view做出相应的调整
66 | * @param type 滑动类型,ViewCompat.TYPE_NON_TOUCH fling效果,ViewCompat.TYPE_TOUCH 手势滑动
67 | */
68 | @Override
69 | public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
70 | //这里不管手势滚动还是fling都处理
71 | boolean hideTop = dy > 0 && getScrollY() < mTopViewHeight;
72 | boolean showTop = dy < 0 && getScrollY() >= 0 && !target.canScrollVertically(-1);
73 | if (hideTop || showTop) {
74 | scrollBy(0, dy);
75 | consumed[1] = dy;
76 | }
77 | }
78 |
79 |
80 | @Override
81 | public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
82 | //当子控件处理完后,交给父控件进行处理。
83 | if (dyUnconsumed < 0) {//表示已经向下滑动到头
84 | scrollBy(0, dyUnconsumed);
85 | }
86 |
87 | }
88 |
89 | @Override
90 | public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
91 | return false;
92 | }
93 |
94 | @Override
95 | public void onStopNestedScroll(@NonNull View target, int type) {
96 | if (type == ViewCompat.TYPE_NON_TOUCH) {
97 | System.out.println("onStopNestedScroll");
98 | }
99 |
100 | mNestedScrollingParentHelper.onStopNestedScroll(target, type);
101 | }
102 |
103 |
104 | @Override
105 | public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
106 | return false;
107 | }
108 |
109 | @Override
110 | public int getNestedScrollAxes() {
111 | return mNestedScrollingParentHelper.getNestedScrollAxes();
112 | }
113 |
114 | @Override
115 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
116 | //ViewPager修改后的高度= 总高度-导航栏高度
117 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
118 | ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams();
119 | layoutParams.height = getMeasuredHeight() - mNavView.getMeasuredHeight();
120 | mViewPager.setLayoutParams(layoutParams);
121 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
122 | }
123 |
124 | @Override
125 | protected void onFinishInflate() {
126 | super.onFinishInflate();
127 | mTopView = findViewById(R.id.iv_head_image);
128 | mNavView = findViewById(R.id.tab_layout);
129 | mViewPager = findViewById(R.id.view_pager);
130 | if (!(mViewPager instanceof ViewPager)) {
131 | throw new RuntimeException("id view_pager should be viewpager!");
132 | }
133 | }
134 |
135 | @Override
136 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
137 | super.onSizeChanged(w, h, oldw, oldh);
138 | mTopViewHeight = mTopView.getMeasuredHeight();
139 | }
140 |
141 | @Override
142 | public void scrollTo(int x, int y) {
143 | if (y < 0) {
144 | y = 0;
145 | }
146 | if (y > mTopViewHeight) {
147 | y = mTopViewHeight;
148 | }
149 | super.scrollTo(x, y);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/view/NestedScrollingParentLayout.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.view;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.support.v4.view.NestedScrollingParent;
8 | import android.support.v4.view.NestedScrollingParentHelper;
9 | import android.support.v4.view.ViewCompat;
10 | import android.support.v4.view.ViewPager;
11 | import android.util.AttributeSet;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.view.animation.DecelerateInterpolator;
15 | import android.widget.LinearLayout;
16 |
17 | import com.jennifer.andy.nestedscrollingdemo.R;
18 |
19 | /**
20 | * Author: andy.xwt
21 | * Date: 2018/8/8 14:28
22 | * Description:NestedScrolling机制下的嵌套滑动 实现NestedScrollingParent接口
23 | */
24 |
25 | public class NestedScrollingParentLayout extends LinearLayout implements NestedScrollingParent {
26 |
27 | private View mTopView;
28 | private View mNavView;
29 | private View mViewPager;
30 | private int mTopViewHeight;
31 | private ValueAnimator mValueAnimator;
32 |
33 | private NestedScrollingParentHelper mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
34 |
35 | public NestedScrollingParentLayout(Context context) {
36 | this(context, null);
37 | }
38 |
39 | public NestedScrollingParentLayout(Context context, @Nullable AttributeSet attrs) {
40 | this(context, attrs, 0);
41 | }
42 |
43 | public NestedScrollingParentLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
44 | super(context, attrs, defStyleAttr);
45 | setOrientation(VERTICAL);
46 | }
47 |
48 |
49 | @Override
50 | public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes) {
51 | return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
52 | }
53 |
54 |
55 | @Override
56 | public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes) {
57 | mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
58 | }
59 |
60 | /**
61 | * 在嵌套滑动的子View未滑动之前,判断父view是否优先与子view处理(也就是父view可以先消耗,然后给子view消耗)
62 | *
63 | * @param target 具体嵌套滑动的那个子类
64 | * @param dx 水平方向嵌套滑动的子View想要变化的距离
65 | * @param dy 垂直方向嵌套滑动的子View想要变化的距离 dy<0向下滑动 dy>0 向上滑动
66 | * @param consumed 这个参数要我们在实现这个函数的时候指定,回头告诉子View当前父View消耗的距离
67 | * consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离 好让子view做出相应的调整
68 | */
69 | @Override
70 | public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
71 | boolean hideTop = dy > 0 && getScrollY() < mTopViewHeight;
72 | boolean showTop = dy < 0 && getScrollY() >= 0 && !target.canScrollVertically(-1);
73 | if (hideTop || showTop) {
74 | scrollBy(0, dy);
75 | consumed[1] = dy;
76 | }
77 | }
78 |
79 |
80 | @Override
81 | public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
82 |
83 | }
84 |
85 | @Override
86 | public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
87 | return false;
88 | }
89 |
90 | @Override
91 | public void onStopNestedScroll(@NonNull View target) {
92 | mNestedScrollingParentHelper.onStopNestedScroll(target);
93 | }
94 |
95 |
96 | @Override
97 | public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
98 | final int distance = Math.abs(getScrollY());
99 | final int duration;
100 | if (velocityY > 0) {//向上滑
101 | duration = 3 * Math.round(1000 * (distance / velocityY));
102 | startAnimation(duration, getScrollY(), mTopViewHeight);
103 | } else if (velocityY < 0) {//向下滑动
104 | final float distanceRatio = (float) distance / getHeight();
105 | duration = (int) ((distanceRatio + 1) * 150);
106 | startAnimation(duration, getScrollY(), 0);
107 | }
108 |
109 | return true;
110 | }
111 |
112 | @Override
113 | public int getNestedScrollAxes() {
114 | return mNestedScrollingParentHelper.getNestedScrollAxes();
115 | }
116 |
117 | @Override
118 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
119 | //ViewPager修改后的高度= 总高度-导航栏高度
120 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
121 | ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams();
122 | layoutParams.height = getMeasuredHeight() - mNavView.getMeasuredHeight();
123 | mViewPager.setLayoutParams(layoutParams);
124 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
125 | }
126 |
127 | @Override
128 | protected void onFinishInflate() {
129 | super.onFinishInflate();
130 | mTopView = findViewById(R.id.iv_head_image);
131 | mNavView = findViewById(R.id.tab_layout);
132 | mViewPager = findViewById(R.id.view_pager);
133 | if (!(mViewPager instanceof ViewPager)) {
134 | throw new RuntimeException("id view_pager should be viewpager!");
135 | }
136 | }
137 |
138 | @Override
139 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
140 | super.onSizeChanged(w, h, oldw, oldh);
141 | mTopViewHeight = mTopView.getMeasuredHeight();
142 | }
143 |
144 | @Override
145 | public void scrollTo(int x, int y) {
146 | if (y < 0) {
147 | y = 0;
148 | }
149 | if (y > mTopViewHeight) {
150 | y = mTopViewHeight;
151 | }
152 | super.scrollTo(x, y);
153 | }
154 |
155 | private void startAnimation(long duration, int startY, int endY) {
156 | if (mValueAnimator == null) {
157 | mValueAnimator = new ValueAnimator();
158 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
159 | @Override
160 | public void onAnimationUpdate(ValueAnimator animation) {
161 | int animatedValue = (int) animation.getAnimatedValue();
162 | scrollTo(0, animatedValue);
163 | }
164 | });
165 | } else {
166 | mValueAnimator.cancel();
167 | }
168 | mValueAnimator.setInterpolator(new DecelerateInterpolator());
169 | mValueAnimator.setIntValues(startY, endY);
170 | mValueAnimator.setDuration(duration);
171 | mValueAnimator.start();
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/view/NestedTraditionLayout.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.view;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.view.ViewPager;
6 | import android.util.AttributeSet;
7 | import android.util.Log;
8 | import android.view.MotionEvent;
9 | import android.view.View;
10 | import android.view.ViewConfiguration;
11 | import android.view.ViewGroup;
12 | import android.widget.LinearLayout;
13 |
14 | import com.jennifer.andy.nestedscrollingdemo.R;
15 |
16 | /**
17 | * Author: andy.xwt
18 | * Date: 2018/8/8 14:27
19 | * Description:
20 | * 传统处理嵌套滑动的方式,如果父控件拦截,根据传统事件分发机制,如果父控件确定拦截事件,那么在同一事件序列中,子控件是没有办法获取到事件,
21 | * 在下面的例子中,如果是同一事件序列中滑动导致headView隐藏,那么除非手指抬起,不然子控件是不能响应事件的。
22 | */
23 |
24 | public class NestedTraditionLayout extends LinearLayout {
25 |
26 | private View mHeadView;
27 | private View mNavView;
28 | private ViewPager mViewPager;
29 |
30 | private int mHeadTopHeight;
31 | private int mLastY;
32 | private boolean isHeadHide;
33 |
34 | private final String TAG = "NestedTraditionLayout";
35 |
36 | public NestedTraditionLayout(Context context) {
37 | this(context, null);
38 | }
39 |
40 | public NestedTraditionLayout(Context context, @Nullable AttributeSet attrs) {
41 | this(context, attrs, 0);
42 | }
43 |
44 | public NestedTraditionLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
45 | super(context, attrs, defStyleAttr);
46 | }
47 |
48 | @Override
49 | public boolean onInterceptTouchEvent(MotionEvent event) {
50 | int action = event.getAction() & MotionEvent.ACTION_MASK;
51 | int y = (int) event.getY();
52 | switch (action) {
53 | case MotionEvent.ACTION_DOWN:
54 | mLastY = y;
55 | break;
56 | case MotionEvent.ACTION_MOVE:
57 | int dy = mLastY - y;
58 | //如果父控件拦截,根据传统事件传递机制,如果父控件确定拦截事件,那么在同一事件序列中,子控件是没有办法获取到事件的。
59 | if (Math.abs(dy) > ViewConfiguration.getTouchSlop()) {
60 | if (dy > 0 && !isHeadHide) { //如果是向上滑,且当前headView没有隐藏,那么就拦截
61 | Log.d(TAG, "onInterceptTouchEvent: 开始向上拦截");
62 | return true;
63 | } else if (dy < 0 && isHeadHide) {//如果是向下, 且将headView已经隐藏,那么就拦截
64 | Log.d(TAG, "onInterceptTouchEvent: 开始向下拦截");
65 | return true;
66 | }
67 | }
68 | break;
69 | }
70 | return super.onInterceptTouchEvent(event);//不拦截事件,把事件让给子控件。
71 |
72 | }
73 |
74 | @Override
75 | public boolean onTouchEvent(MotionEvent event) {
76 |
77 | int action = event.getAction() & MotionEvent.ACTION_MASK;
78 | int y = (int) event.getY();
79 |
80 | switch (action) {
81 | case MotionEvent.ACTION_DOWN:
82 | mLastY = y;
83 | break;
84 | case MotionEvent.ACTION_MOVE:
85 | int dy = mLastY - y;
86 | if (Math.abs(dy) > ViewConfiguration.getTouchSlop()) {
87 | scrollBy(0, dy);
88 | }
89 | mLastY = y;
90 | break;
91 | }
92 | return super.onTouchEvent(event);
93 | }
94 |
95 | /**
96 | * 重新scrollTo方法,因为scrollBy最终会调用,scrollTo方法
97 | */
98 | @Override
99 | public void scrollTo(int x, int y) {
100 | if (y < 0) {
101 | y = 0;
102 | }
103 | if (y > mHeadTopHeight) {
104 | y = mHeadTopHeight;
105 | }
106 | super.scrollTo(x, y);
107 | isHeadHide = getScrollY() == mHeadTopHeight;//判断当前head是否已经隐藏了
108 | }
109 |
110 | @Override
111 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
112 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
113 | //ViewPager修改后的高度= 总高度-导航栏高度
114 | ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams();
115 | layoutParams.height = getMeasuredHeight() - mNavView.getMeasuredHeight();
116 | mViewPager.setLayoutParams(layoutParams);
117 | //当ViewPager修改高度后,重新开始测量
118 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
119 |
120 | }
121 |
122 | @Override
123 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
124 | super.onSizeChanged(w, h, oldw, oldh);
125 | mHeadTopHeight = mHeadView.getMeasuredHeight();//获取headView高度
126 | }
127 |
128 | @Override
129 | protected void onFinishInflate() {
130 | super.onFinishInflate();
131 | mHeadView = findViewById(R.id.iv_head_image);
132 | mNavView = findViewById(R.id.tab_layout);
133 | mViewPager = findViewById(R.id.view_pager);
134 | if (!(mViewPager instanceof ViewPager)) {
135 | throw new RuntimeException("id view_pager should be viewpager!");
136 | }
137 | }
138 |
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jennifer/andy/nestedscrollingdemo/view/StickyNavLayout.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo.view;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.view.NestedScrollingParent2;
7 | import android.support.v4.view.NestedScrollingParentHelper;
8 | import android.support.v4.view.ViewCompat;
9 | import android.support.v4.view.ViewPager;
10 | import android.util.AttributeSet;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.LinearLayout;
14 |
15 | import com.jennifer.andy.nestedscrollingdemo.R;
16 |
17 | /**
18 | * Author: andy.xwt
19 | * Date: 2019-07-08 22:11
20 | * Description:
21 | */
22 |
23 | public class StickyNavLayout extends LinearLayout implements NestedScrollingParent2 {
24 |
25 | private NestedScrollingParentHelper mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
26 | private View mTopView;//头部view
27 | private View mNavView;//导航view
28 | private ViewPager mViewPager;//Viewpager
29 | private ScrollChangeListener mScrollChangeListener;
30 | /**
31 | * 父控件可以滚动的距离
32 | */
33 | private float mCanScrollDistance = 0f;
34 |
35 | public StickyNavLayout(Context context) {
36 | this(context, null);
37 | }
38 |
39 | public StickyNavLayout(Context context, @Nullable AttributeSet attrs) {
40 | this(context, attrs, 0);
41 | }
42 |
43 | public StickyNavLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
44 | super(context, attrs, defStyleAttr);
45 | setOrientation(LinearLayout.VERTICAL);
46 | }
47 |
48 | /**
49 | * 父控件接受嵌套滑动,不管是手势滑动还是fling 父控件都接受
50 | */
51 | @Override
52 | public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) {
53 | return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
54 | }
55 |
56 | @Override
57 | public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
58 | mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type);
59 | }
60 |
61 |
62 | @Override
63 | public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
64 | //如果子view欲向上滑动,则先交给父view滑动
65 | boolean hideTop = dy > 0 && getScrollY() < mCanScrollDistance;
66 | //如果子view欲向下滑动,必须要子view不能向下滑动后,才能交给父view滑动
67 | boolean showTop = dy < 0 && getScrollY() >= 0 && !target.canScrollVertically(-1);
68 | if (hideTop || showTop) {
69 | scrollBy(0, dy);
70 | consumed[1] = dy;// consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离
71 | }
72 | }
73 |
74 |
75 | @Override
76 | public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
77 | if (dyUnconsumed < 0) {//表示已经向下滑动到头,这里不用区分手势还是fling
78 | scrollBy(0, dyUnconsumed);
79 | }
80 | }
81 |
82 | @Override
83 | public void onStopNestedScroll(@NonNull View target, int type) {
84 | mNestedScrollingParentHelper.onStopNestedScroll(target, type);
85 | }
86 |
87 | /**
88 | * 嵌套滑动时,如果父View处理了fling,那子view就没有办法处理fling了,所以这里要返回为false
89 | */
90 | @Override
91 | public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
92 | return false;
93 | }
94 |
95 | @Override
96 | public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
97 | return false;
98 | }
99 |
100 | @Override
101 | protected void onFinishInflate() {
102 | super.onFinishInflate();
103 | mTopView = findViewById(R.id.sl_top_view);
104 | mNavView = findViewById(R.id.sl_tab);
105 | mViewPager = findViewById(R.id.sl_viewpager);
106 |
107 | }
108 |
109 | @Override
110 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
111 | //先测量一次
112 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
113 | //ViewPager修改后的高度= 总高度-TabLayout高度
114 | ViewGroup.LayoutParams lp = mViewPager.getLayoutParams();
115 | lp.height = getMeasuredHeight() - mNavView.getMeasuredHeight();
116 | mViewPager.setLayoutParams(lp);
117 | //因为ViewPager修改了高度,所以需要重新测量
118 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
119 | }
120 |
121 |
122 | @Override
123 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
124 | mCanScrollDistance = mTopView.getMeasuredHeight() - getResources().getDimension(R.dimen.normal_title_height);
125 | }
126 |
127 | @Override
128 | public void scrollTo(int x, int y) {
129 | if (y < 0) {
130 | y = 0;
131 | }
132 | if (y > mCanScrollDistance) {
133 | y = (int) mCanScrollDistance;
134 | }
135 | if (mScrollChangeListener != null) {
136 | mScrollChangeListener.onScroll(y / mCanScrollDistance);
137 | }
138 | if (getScrollY() != y) super.scrollTo(x, y);
139 | }
140 |
141 |
142 | @Override
143 | public int getNestedScrollAxes() {
144 | return mNestedScrollingParentHelper.getNestedScrollAxes();
145 | }
146 |
147 |
148 | public interface ScrollChangeListener {
149 | /**
150 | * 移动监听
151 | *
152 | * @param moveRatio 移动比例
153 | */
154 | void onScroll(float moveRatio);
155 | }
156 |
157 | public void setScrollChangeListener(ScrollChangeListener scrollChangeListener) {
158 | mScrollChangeListener = scrollChangeListener;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_back_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/drawable/ic_action_back_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_back_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/drawable/ic_action_back_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cdl_abl_ctl.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
20 |
21 |
29 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cdl_demo1.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
17 |
18 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cdl_demo2.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
18 |
19 |
20 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cdl_demo3.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
16 |
17 |
18 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cdl_demo4.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
17 |
18 |
19 |
24 |
25 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cdl_with_appbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
19 |
20 |
21 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_coord_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
20 |
21 |
26 |
27 |
28 |
33 |
34 |
39 |
40 |
41 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
16 |
17 |
22 |
23 |
24 |
29 |
30 |
35 |
36 |
37 |
42 |
43 |
48 |
49 |
50 |
51 |
56 |
57 |
58 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_nested_scrolling2_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
29 |
30 |
36 |
37 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_nested_srolling_parent.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
16 |
21 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_nested_srolling_parent2.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
16 |
21 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_nested_tradition.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
17 |
22 |
23 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_single_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_common_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
20 |
21 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000000
4 | #000000
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 48dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NestedScrollingDemo
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/jennifer/andy/nestedscrollingdemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.jennifer.andy.nestedscrollingdemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.4.1'
11 |
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndyJennifer/NestedScrollingDemo/ebbc2b21417f4177c4d71c3f793ebcd129d83f3c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Aug 08 13:45:04 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------