├── .gitignore
├── .idea
├── checkstyle-idea.xml
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── qaplug_profiles.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── cq
│ │ ├── airbnb
│ │ ├── AirbnbActivity.java
│ │ ├── BGBehavior.java
│ │ ├── IsChildRequestScrollListener.java
│ │ ├── ListBehavior.java
│ │ ├── SearchAllBehavior.java
│ │ ├── SearchItemBehavior.java
│ │ ├── TabBehavior.java
│ │ └── UpIconBehavior.java
│ │ └── behaviordemo
│ │ ├── Constants.java
│ │ ├── ItemFragment.java
│ │ ├── MainActivity.java
│ │ ├── adapter
│ │ ├── FriendInfoAdapter.java
│ │ └── ItemAdapter.java
│ │ ├── behavior
│ │ ├── BGBehavior.java
│ │ ├── BGContentBehavior.java
│ │ ├── EditorBehavior.java
│ │ ├── HeaderScrollingViewBehavior.java
│ │ ├── IconBehavior.java
│ │ ├── ListBehavior.java
│ │ ├── NameBehavior.java
│ │ ├── ScoreBehavior.java
│ │ ├── TabBehavior.java
│ │ ├── ToolBarIconBehavior.java
│ │ ├── ViewOffsetBehavior.java
│ │ └── ViewOffsetHelper.java
│ │ ├── listener
│ │ ├── IsChildRequestScrollListener.java
│ │ ├── NeedExpandListener.java
│ │ └── SupportNeedExpendListener.java
│ │ └── view
│ │ └── CircleImageView.java
│ └── res
│ ├── drawable-xhdpi
│ ├── ic_arrow_back_black_24dp.png
│ ├── ic_arrow_back_white_24dp.png
│ ├── ic_icon.png
│ ├── ic_share_black_24dp.png
│ ├── ic_share_white_24dp.png
│ ├── ic_trips_search_anywhere.png
│ ├── ic_trips_search_calendar.png
│ ├── ic_trips_search_guest.png
│ ├── ic_trips_search_view_up_chevron.png
│ └── search.png
│ ├── drawable
│ ├── bg_airbnb_condition.xml
│ ├── bg_airbnb_condition_gray.xml
│ ├── bg_content.xml
│ ├── bg_evaluation.xml
│ ├── bg_main.xml
│ ├── bg_tab.xml
│ ├── bg_tab_white.xml
│ ├── divider_padding.xml
│ └── ic_star.xml
│ ├── layout
│ ├── activity_airbnb.xml
│ ├── activity_iread.xml
│ ├── activity_main.xml
│ ├── fragment_item.xml
│ └── item_friend_info.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── img
└── preview.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/checkstyle-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 1.8
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/qaplug_profiles.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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
--------------------------------------------------------------------------------
/.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 | ## Behavior 实现的漂亮的效果
2 |
3 | ### 简介
4 |
5 | [项目地址](https://github.com/CSnowStack/BehaviorDemo)
6 |
7 |
8 | #### 建议
9 | 请先阅读[这篇文章](http://www.jianshu.com/p/f7989a2a3ec2)
10 | > 好多东西抄自这里
11 |
12 |
13 | #### 我的实现
14 |
15 | 
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | def android_support_version = '25.0.1'
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 | defaultConfig {
7 | applicationId "cq.behaviordemo"
8 | minSdkVersion 14
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | vectorDrawables.useSupportLibrary = true
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile "com.android.support:appcompat-v7:$android_support_version"
25 | compile "com.android.support:design:$android_support_version"
26 | compile "com.android.support:recyclerview-v7:$android_support_version"
27 | compile "com.android.support:cardview-v7:$android_support_version"
28 | compile 'de.hdodenhof:circleimageview:2.1.0'
29 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\androidstudiosdk\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/AirbnbActivity.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.support.v4.view.ViewPager;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.view.View;
9 | import android.widget.LinearLayout;
10 |
11 | import cq.behaviordemo.R;
12 | import cq.behaviordemo.adapter.ItemAdapter;
13 |
14 | /**
15 | * 仿爱彼迎主页
16 | */
17 |
18 | public class AirbnbActivity extends AppCompatActivity {
19 | private TabLayout mTabLayout;
20 | private ViewPager mViewPager;
21 | private LinearLayout mLytUP;
22 | private LinearLayout mLytAll;
23 |
24 | @Override protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_airbnb);
27 | findView();
28 | initView();
29 | initEvent();
30 | }
31 |
32 | private void findView() {
33 | mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
34 | mViewPager = (ViewPager) findViewById(R.id.viewpager);
35 | mLytUP = (LinearLayout) findViewById(R.id.lyt_up);
36 | mLytAll = (LinearLayout) findViewById(R.id.lyt_all);
37 |
38 | mLytUP.setOnClickListener(new View.OnClickListener() {
39 | @Override public void onClick(View v) {
40 | ((TabBehavior) ((CoordinatorLayout.LayoutParams) mTabLayout.getLayoutParams()).getBehavior()).hideItem();
41 |
42 | }
43 | });
44 | mLytAll.setOnClickListener(new View.OnClickListener() {
45 | @Override public void onClick(View v) {
46 | ((TabBehavior) ((CoordinatorLayout.LayoutParams) mTabLayout.getLayoutParams()).getBehavior()).needExpand();
47 |
48 | }
49 | });
50 | }
51 |
52 | private void initView() {
53 | mViewPager.setAdapter(new ItemAdapter(getSupportFragmentManager(), mViewPager, 2));
54 | mTabLayout.setupWithViewPager(mViewPager);
55 | }
56 |
57 | private void initEvent() {
58 | mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
59 | @Override
60 | public void onTabSelected(TabLayout.Tab tab) {
61 | mViewPager.setCurrentItem(tab.getPosition());
62 | }
63 |
64 | @Override
65 | public void onTabUnselected(TabLayout.Tab tab) {
66 |
67 | }
68 |
69 | @Override
70 | public void onTabReselected(TabLayout.Tab tab) {
71 |
72 | }
73 | });
74 |
75 | }
76 |
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/BGBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * 背景的behavior
13 | */
14 |
15 | public class BGBehavior extends CoordinatorLayout.Behavior {
16 |
17 | private int mTranslationMax;
18 | public BGBehavior(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | mTranslationMax=context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_max);
21 | }
22 |
23 |
24 | @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
25 | return dependency instanceof TabLayout;
26 | }
27 |
28 | @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
29 | child.setTranslationY(dependency.getTranslationY()-mTranslationMax);
30 | return true;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/IsChildRequestScrollListener.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | /**
4 | * 判断子view是否需要滑动
5 | */
6 |
7 | public interface IsChildRequestScrollListener {
8 | boolean requestScroll(boolean up,boolean shouldNotRefresh);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/ListBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.support.design.widget.CoordinatorLayout;
6 | import android.support.design.widget.TabLayout;
7 | import android.support.v4.view.GravityCompat;
8 | import android.support.v4.view.ViewCompat;
9 | import android.util.AttributeSet;
10 | import android.view.Gravity;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * 控制列表的移动
18 | */
19 |
20 | public class ListBehavior extends CoordinatorLayout.Behavior {
21 |
22 | final Rect mTempRect1 = new Rect();
23 | final Rect mTempRect2 = new Rect();
24 |
25 | public ListBehavior(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | }
28 |
29 |
30 | /**
31 | * 计算宽高
32 | */
33 | @Override
34 | public boolean onMeasureChild(CoordinatorLayout parent, View child,
35 | int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
36 | int heightUsed) {
37 | final int childLpHeight = child.getLayoutParams().height;
38 | if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
39 | || childLpHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
40 | // If the menu's height is set to match_parent/wrap_content then measure it
41 | // with the maximum visible height
42 |
43 | final List dependencies = parent.getDependencies(child);
44 | final View header = findFirstDependency(dependencies);
45 | if (header != null) {
46 | if (ViewCompat.getFitsSystemWindows(header)
47 | && !ViewCompat.getFitsSystemWindows(child)) {
48 | // If the header is fitting system windows then we need to also,
49 | // otherwise we'll get CoL's compatible measuring
50 | ViewCompat.setFitsSystemWindows(child, true);
51 |
52 | if (ViewCompat.getFitsSystemWindows(child)) {
53 | // If the set succeeded, trigger a new layout and return true
54 | child.requestLayout();
55 | return true;
56 | }
57 | }
58 |
59 | //可用的高度
60 | int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
61 | if (availableHeight == 0) {
62 | // If the measure spec doesn't specify a size, use the current height
63 | availableHeight = parent.getHeight();
64 | }
65 |
66 | //列表高度为=可用高度-依赖的view的高度
67 | final int height = availableHeight - header.getMeasuredHeight();
68 | final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height,
69 | childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
70 | ? View.MeasureSpec.EXACTLY
71 | : View.MeasureSpec.AT_MOST);
72 |
73 | // Now measure the scrolling view with the correct height
74 | parent.onMeasureChild(child, parentWidthMeasureSpec,
75 | widthUsed, heightMeasureSpec, heightUsed);
76 |
77 | return true;
78 | }
79 | }
80 | return false;
81 | }
82 |
83 | @Override public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
84 | final List dependencies = parent.getDependencies(child);
85 | final View header = findFirstDependency(dependencies);
86 |
87 | if (header != null) {
88 | final CoordinatorLayout.LayoutParams lp =
89 | (CoordinatorLayout.LayoutParams) child.getLayoutParams();
90 | final Rect available = mTempRect1;
91 | //列表可用的 layoutParams . bottom 是为了 列表的高度为parent的可用高度
92 | available.set(parent.getPaddingLeft() + lp.leftMargin,
93 | header.getBottom() + lp.topMargin,
94 | parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,
95 | parent.getHeight() + header.getBottom()
96 | - parent.getPaddingBottom() - lp.bottomMargin);
97 |
98 | final Rect out = mTempRect2;
99 | //设置gravity
100 | GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
101 | child.getMeasuredHeight(), available, out, layoutDirection);
102 |
103 | child.layout(out.left, out.top, out.right, out.bottom);
104 | } else {
105 | // If we don't have a dependency, let super handle it
106 | super.onLayoutChild(parent, child, layoutDirection);
107 | }
108 | return true;
109 | }
110 |
111 | @Override
112 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
113 | return isDependOn(dependency);
114 | }
115 |
116 | private View findFirstDependency(List views) {
117 | for (View v : views) {
118 | if (isDependOn(v)) {
119 | return v;
120 | }
121 | }
122 | return null;
123 | }
124 |
125 | private boolean isDependOn(View dependency) {
126 | return dependency instanceof TabLayout;
127 | }
128 |
129 | private static int resolveGravity(int gravity) {
130 | return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
131 | }
132 |
133 |
134 | //跟随tab移动
135 | @Override
136 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
137 |
138 | child.setTranslationY(dependency.getTranslationY());
139 | return true;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/SearchAllBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * 搜索所有的behavior
13 | */
14 |
15 | public class SearchAllBehavior extends CoordinatorLayout.Behavior {
16 |
17 | /**
18 | * 改变行为的高度
19 | * translationY 由 mTranslationMin mTranslationMin+mChangeHeight
20 | */
21 | private int mChangeHeight, mTranslationMin, mTranslationMax;
22 | private int mMarginTop;
23 |
24 | /**
25 | * 需要gone的时候移动到屏幕外
26 | */
27 | private int mTranslationGone;
28 |
29 | private int mHeightUp;
30 |
31 | public SearchAllBehavior(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 | mMarginTop = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_padding_content);
34 | mHeightUp = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_up_height);
35 | mTranslationMin = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_min);
36 | mTranslationMax = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_max);
37 | mChangeHeight = mHeightUp - mMarginTop;
38 |
39 | }
40 |
41 | @Override public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
42 | parent.onLayoutChild(child, layoutDirection);
43 | if (mTranslationGone == 0)
44 | mTranslationGone = -child.getRight();
45 | return true;
46 | }
47 |
48 | /**
49 | * 依赖tabLayout
50 | */
51 | @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
52 | return dependency instanceof TabLayout;
53 | }
54 |
55 | @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
56 | float translationY = dependency.getTranslationY();
57 | if (translationY < mTranslationMin) {//向上移动的时候跟随tab
58 | child.setTranslationX(0);
59 | child.setTranslationY(translationY - mTranslationMin);
60 | } else {//展开的时候,根据比例设置
61 | //tab 位移的比例
62 | float fraction = (translationY - mTranslationMin) / (mTranslationMax - mTranslationMin);
63 | //根据比例设置位移的距离
64 | if (fraction <= 1 / 3f) {
65 | child.setTranslationX(0);
66 | child.setTranslationY(fraction * mChangeHeight);
67 | child.setAlpha(1 - fraction * 3);
68 |
69 | } else {
70 | if (child.getTranslationX() != mTranslationGone) {
71 | child.setTranslationX(mTranslationGone);
72 | child.setAlpha(0);
73 | }
74 |
75 | }
76 | }
77 | return true;
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/SearchItemBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * 搜索的分类的view的behavior
13 | */
14 |
15 | public class SearchItemBehavior extends CoordinatorLayout.Behavior {
16 |
17 | /**
18 | * 最大位移距离 mChangeHeight
19 | * translationY 由 mTranslationMin 移动到mTranslationMax
20 | */
21 | private int mChangeHeight, mTranslationMin, mTranslationMax;
22 | private int mMarginTop;
23 |
24 | /**
25 | * 下面的三个item
26 | */
27 | private View mItemWhere, mItemWhen, mItemWho;
28 |
29 | /**
30 | * 需要gone的时候移动到屏幕外
31 | */
32 | private int mTranslationGone;
33 | private int mHeightUp;
34 |
35 | public SearchItemBehavior(Context context, AttributeSet attrs) {
36 | super(context, attrs);
37 | mMarginTop = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_padding_content);
38 | mHeightUp = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_up_height);
39 |
40 | mTranslationMin = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_min);
41 | mTranslationMax = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_max);
42 | mChangeHeight = mHeightUp - mMarginTop;
43 |
44 | }
45 |
46 | @Override public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
47 | parent.onLayoutChild(child, layoutDirection);
48 |
49 | mItemWhere = child.findViewById(R.id.lyt_where);
50 | mItemWhen = child.findViewById(R.id.lyt_when);
51 | mItemWho = child.findViewById(R.id.lyt_who);
52 |
53 | if (mTranslationGone == 0)
54 | mTranslationGone = -child.getRight();
55 | return true;
56 | }
57 |
58 | /**
59 | * 依赖tabLayout
60 | */
61 | @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
62 | return dependency instanceof TabLayout;
63 | }
64 |
65 | @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
66 | float translationY = dependency.getTranslationY();
67 | if (translationY >= mTranslationMin) {
68 | if (child.getTranslationX() != 0) {
69 | child.setTranslationX(0);
70 | child.setAlpha(1);
71 | }
72 |
73 | //tab 位移的比例
74 | float fraction = (translationY - mTranslationMin) / (mTranslationMax - mTranslationMin);
75 | //根据比例设置位移的距离
76 | child.setTranslationY(fraction * mChangeHeight);
77 |
78 | //根据比例设置透明度
79 | if (fraction < 1 / 3f) {
80 | mItemWhere.setTranslationX(0);
81 | mItemWhen.setTranslationX(mTranslationGone);
82 |
83 | mItemWho.setTranslationX(mTranslationGone);
84 |
85 |
86 | mItemWhere.setAlpha(fraction * 3);
87 | mItemWhen.setAlpha(0);
88 | mItemWho.setAlpha(0);
89 | } else if (fraction < 2 / 3f) {
90 | mItemWhere.setTranslationX(0);
91 | mItemWhen.setTranslationX(0);
92 |
93 | mItemWho.setTranslationX(mTranslationGone);
94 |
95 | mItemWhere.setAlpha(1);
96 | mItemWhen.setAlpha(fraction * 3 - 1);
97 | mItemWho.setAlpha(0);
98 | } else {
99 | mItemWhere.setTranslationX(0);
100 | mItemWhen.setTranslationX(0);
101 |
102 | mItemWho.setTranslationX(0);
103 |
104 | mItemWhere.setAlpha(1);
105 | mItemWhen.setAlpha(1);
106 | mItemWho.setAlpha(fraction * 3 - 2);
107 | }
108 | } else if (child.getTranslationX() != mTranslationGone) {
109 | child.setTranslationX(mTranslationGone);
110 | child.setAlpha(0);
111 | }
112 | return true;
113 |
114 | }
115 | }
116 |
117 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/TabBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.support.design.widget.CoordinatorLayout;
8 | import android.support.design.widget.TabLayout;
9 | import android.support.v4.content.ContextCompat;
10 | import android.support.v4.view.PagerAdapter;
11 | import android.support.v4.view.ViewCompat;
12 | import android.support.v4.view.ViewPager;
13 | import android.util.AttributeSet;
14 | import android.view.View;
15 | import android.widget.TextView;
16 |
17 | import cq.behaviordemo.Constants;
18 | import cq.behaviordemo.R;
19 |
20 | /**
21 | * 控制tab
22 | */
23 |
24 | public class TabBehavior extends CoordinatorLayout.Behavior {
25 | /**
26 | * 向下滑动的最大距离
27 | */
28 | private int mTranslationMax;
29 |
30 | /**
31 | * 最小的距离
32 | */
33 | private int mTranslationMin;
34 |
35 | private int mHeightChild;
36 | /**
37 | * 向上滑动,还是向下滑动
38 | */
39 | private boolean mUp;
40 | /**
41 | * 手指控制的滑动
42 | */
43 | private boolean mControlChange;
44 |
45 | /**
46 | * mValueAnimatorStyle 改变样式
47 | */
48 | private ValueAnimator mValueAnimator;
49 | private ViewPager mViewPager;
50 | private TabLayout mTab;
51 |
52 | /**
53 | * 标记是否是需要改变样式
54 | * 1 头部只显示tab的时候 切换一次
55 | * 2 头部的显示超出 tab和 all的时候切换一次
56 | */
57 | private boolean mWhiteStyle = false;
58 | private TextView mTxtAll;
59 | private View mLytContent, mLytAll;
60 | private Context mContext;
61 | private int mWhite = 0xFFFFFFFF;
62 | private int mGreen = 0xFF128488;
63 |
64 | public TabBehavior(Context context, AttributeSet attrs) {
65 | super(context, attrs);
66 | mValueAnimator = ValueAnimator.ofFloat(0, 1);
67 | mContext = context;
68 | mTranslationMax = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_max);
69 | mTranslationMin = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_min);
70 | mHeightChild = context.getResources().getDimensionPixelOffset(R.dimen.tab_height);
71 |
72 | }
73 |
74 | @Override public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
75 | parent.onLayoutChild(child, layoutDirection);
76 | if(mTab==null){
77 | mTab = (TabLayout) child;
78 | mViewPager = (ViewPager) parent.findViewById(R.id.viewpager);
79 | mLytAll = parent.findViewById(R.id.lyt_all);
80 | mTxtAll = (TextView) mLytAll.findViewById(R.id.txt_all);
81 | mLytContent = parent.findViewById(R.id.content);
82 | mTab.setTranslationY(mTranslationMin);
83 | }
84 |
85 | return true;
86 | }
87 |
88 | @Override
89 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
90 | mControlChange = true;
91 | return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
92 | }
93 |
94 |
95 | /**
96 | * @param dy 向上滑大于0
97 | */
98 | @Override
99 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
100 | mUp = dy > 0;
101 | if (mValueAnimator.isRunning()) {
102 | mValueAnimator.cancel();
103 | }
104 |
105 | //如果list需要滑动 且 不是(向上滑&&tab不在顶部)
106 |
107 | if (isChildRequestScroll(child.getTranslationY())) {
108 | consumed[1] = 0;
109 | return;
110 | }
111 |
112 | consumed[1] = dy;//全部消耗
113 | int distance = -dy / 2;//降低移动的速度
114 |
115 |
116 | if (child.getTranslationY() + distance > mTranslationMax) {//大于最大距离
117 | distance = mTranslationMax;
118 | } else if (child.getTranslationY() + distance < 0) {//到顶部
119 | distance = 0;
120 | } else {//正常
121 | distance = (int) (child.getTranslationY() + distance);
122 | }
123 |
124 | //判断应该显示的样式
125 | if (mUp && distance < (mTranslationMin - mHeightChild / 2) && !mWhiteStyle) {
126 | setWhiteStyle();
127 | } else if (!mUp && distance > (mTranslationMin + mHeightChild / 2) && mWhiteStyle) {
128 | setGreenStyle();
129 | }
130 | child.setTranslationY(distance);
131 | }
132 |
133 | private void setGreenStyle() {
134 | mWhiteStyle = false;
135 | mTab.setTabTextColors(ContextCompat.getColor(mContext, android.R.color.darker_gray), mWhite);
136 | mLytAll.setBackgroundResource(R.drawable.bg_airbnb_condition);
137 | mTxtAll.setTextColor(mWhite);
138 | mTab.setSelectedTabIndicatorColor(mWhite);
139 |
140 | mLytContent.setBackgroundResource(R.color.airbnb_bg);
141 |
142 | }
143 |
144 | private void setWhiteStyle() {
145 | mWhiteStyle = true;
146 | mTab.setTabTextColors(ContextCompat.getColor(mContext, android.R.color.darker_gray), mGreen);
147 | mLytAll.setBackgroundResource(R.drawable.bg_airbnb_condition_gray);
148 | mTxtAll.setTextColor(ContextCompat.getColor(mContext, android.R.color.black));
149 | mTab.setSelectedTabIndicatorColor(mGreen);
150 |
151 | mLytContent.setBackgroundResource(android.R.color.white);
152 | }
153 |
154 |
155 | @Override
156 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
157 | mControlChange = false;
158 | float translationY = child.getTranslationY();
159 | if (translationY == mTranslationMax || translationY == mTranslationMin || translationY == 0) {
160 | return;
161 | }
162 |
163 | scroll(child, translationY);
164 |
165 | }
166 |
167 |
168 | /**
169 | * list 不需要滑动就拦截.需要就不拦截
170 | */
171 |
172 | @Override
173 | public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
174 | if (velocityY < -1000 && child.getTranslationY() == 0) {//向下滑的速度小于负1000
175 | mControlChange = false;
176 | showSearchAll();
177 | }
178 |
179 | return !isChildRequestScroll(child.getTranslationY());
180 | }
181 |
182 | /**
183 | * 三种情况
184 | * 1 在顶部
185 | * 2 在all的下面
186 | * 3 在condition的下面
187 | */
188 | private void scroll(final View child, final float translationY) {
189 | final float shouldMoveDistance;
190 | if (translationY < mHeightChild / 2) {//这段去最上面
191 | shouldMoveDistance = -translationY;
192 | } else if ((translationY > mHeightChild / 2 && translationY < (mTranslationMin + mHeightChild / 2)) ||
193 | (mUp && translationY < (mTranslationMax - mHeightChild))) {//回到中间的点
194 | shouldMoveDistance = mTranslationMin - translationY;
195 | } else {//去最下面
196 | shouldMoveDistance = mTranslationMax - translationY;
197 | }
198 |
199 |
200 | mValueAnimator.setDuration((long) (Math.abs(shouldMoveDistance) / mTranslationMax * Constants.DURATION_SCROLL));
201 | mValueAnimator.removeAllUpdateListeners();
202 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
203 | @Override
204 | public void onAnimationUpdate(ValueAnimator animation) {
205 | child.setTranslationY(translationY + animation.getAnimatedFraction() * shouldMoveDistance);
206 | }
207 | });
208 | mValueAnimator.start();
209 | }
210 |
211 |
212 | /**
213 | * Child是否需要滑动
214 | */
215 | private boolean isChildRequestScroll(float translationY) {
216 | PagerAdapter adapter = mViewPager.getAdapter();
217 | boolean shouldNotRefresh=translationY==mTranslationMin||translationY==0;//这两个位置的时候不应该刷新
218 | return ((translationY == 0 ||((translationY==mTranslationMax||shouldNotRefresh)&&!mUp))&&/*在顶部,或在底部且向下滑,在初始位置的时候,不给刷新,但是可以向下滑*/
219 | adapter != null && //有适配器
220 | adapter.getCount() > 0 &&//有item
221 | adapter instanceof IsChildRequestScrollListener && //实现了
222 | ((IsChildRequestScrollListener) adapter).requestScroll(mUp,shouldNotRefresh)//需要滑动
223 | );
224 | }
225 |
226 |
227 | /**
228 | * 显示搜索全部
229 | */
230 | public void showSearchAll() {
231 | if (!mControlChange && mTab.getTranslationY() == 0 && !mValueAnimator.isRunning()) {
232 | mValueAnimator.setDuration(500);
233 | final float startTranslation = mTab.getTranslationY();
234 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
235 | @Override
236 | public void onAnimationUpdate(ValueAnimator animation) {
237 | mTab.setTranslationY(startTranslation + animation.getAnimatedFraction() * (mTranslationMin - startTranslation));
238 | }
239 | });
240 | mValueAnimator.addListener(new AnimatorListenerAdapter() {
241 | @Override public void onAnimationEnd(Animator animation) {
242 | super.onAnimationEnd(animation);
243 | mTab.setTranslationY(mTranslationMin);
244 | mValueAnimator.removeAllListeners();
245 | mValueAnimator.removeAllUpdateListeners();
246 | }
247 | });
248 | mValueAnimator.start();
249 | }
250 | }
251 |
252 |
253 | /**
254 | * 隐藏搜索的详细条件
255 | */
256 | public void hideItem() {
257 | if (!mValueAnimator.isRunning()) {
258 |
259 | mValueAnimator.setDuration(500);
260 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
261 | @Override
262 | public void onAnimationUpdate(ValueAnimator animation) {
263 | float translation = mTranslationMin + (1 - animation.getAnimatedFraction()) * (mTranslationMax - mTranslationMin);
264 | mTab.setTranslationY(translation);
265 | }
266 | });
267 | mValueAnimator.addListener(new AnimatorListenerAdapter() {
268 | @Override public void onAnimationEnd(Animator animation) {
269 | super.onAnimationEnd(animation);
270 | mTab.setTranslationY(mTranslationMin);
271 | mValueAnimator.removeAllListeners();
272 | mValueAnimator.removeAllUpdateListeners();
273 | }
274 | });
275 | mValueAnimator.start();
276 | }
277 | }
278 |
279 | @Override public void onDetachedFromLayoutParams() {
280 | super.onDetachedFromLayoutParams();
281 | mValueAnimator.cancel();
282 | }
283 |
284 | /**
285 | * list fling到头的时候 展开
286 | */
287 | public void needExpand() {
288 | if (!mControlChange && mTab.getTranslationY() != mTranslationMax && !mValueAnimator.isRunning()) {
289 | mValueAnimator.setDuration(500);
290 | final float startTranslation = mTab.getTranslationY();
291 |
292 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
293 | @Override
294 | public void onAnimationUpdate(ValueAnimator animation) {
295 | int translation = (int) (startTranslation + animation.getAnimatedFraction() * (mTranslationMax - startTranslation));
296 | mTab.setTranslationY(translation);
297 | if (mWhiteStyle && translation > (mTranslationMin + mHeightChild / 2)) {
298 | mWhiteStyle = false;
299 | setGreenStyle();
300 | }
301 |
302 | }
303 | });
304 | mValueAnimator.addListener(new AnimatorListenerAdapter() {
305 | @Override public void onAnimationEnd(Animator animation) {
306 | super.onAnimationEnd(animation);
307 | mTab.setTranslationY(mTranslationMax);
308 | mValueAnimator.removeAllListeners();
309 | mValueAnimator.removeAllUpdateListeners();
310 | }
311 | });
312 | mValueAnimator.start();
313 | }
314 | }
315 | }
316 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/airbnb/UpIconBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.airbnb;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * 向上的图片的 behavior
13 | */
14 |
15 | public class UpIconBehavior extends CoordinatorLayout.Behavior {
16 | private int mChangeHeight, mTranslationMin, mTranslationMax;
17 |
18 |
19 | /**
20 | * 需要gone的时候移动到屏幕外
21 | */
22 | private int mTranslationGone;
23 |
24 | public UpIconBehavior(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 | mTranslationMin = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_min);
27 |
28 | mTranslationMax = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_max);
29 | mChangeHeight = context.getResources().getDimensionPixelOffset(R.dimen.airbnb_up_height) / 4;
30 | }
31 |
32 | @Override public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
33 | parent.onLayoutChild(child, layoutDirection);
34 | if (mTranslationGone == 0)
35 | mTranslationGone = -child.getRight();
36 | return true;
37 | }
38 |
39 | @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
40 | return dependency instanceof TabLayout;
41 | }
42 |
43 | @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
44 | float translationY = dependency.getTranslationY();
45 | float fraction = (translationY - mTranslationMin) / (mTranslationMax - mTranslationMin);
46 | //根据比例设置位移的距离
47 | if (fraction >= 1 / 3f) {
48 | child.setTranslationX(0);
49 |
50 | float fractionItem = (fraction * 3 - 1) / 2;
51 | child.setTranslationY(-(1 - fractionItem) * mChangeHeight);
52 | child.setAlpha(fractionItem);
53 | } else if (child.getTranslationX() != mTranslationGone) {
54 | child.setTranslationX(mTranslationGone);
55 | child.setAlpha(0);
56 |
57 | }
58 |
59 | return true;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/Constants.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo;
2 |
3 | /**
4 | * Created by cqll on 2016/12/13.
5 | */
6 |
7 | public class Constants {
8 | //宽度为占屏幕的比例
9 | public static final float FRACTION_WIDTH_BGCONTENT =0.9f;
10 | //高度占宽度的比例
11 | public static final float FRACTION_HEIGHT_BGCONTENT =0.64f*11/14;
12 |
13 | //高度占宽度的比例
14 | public static final float FRACTION_HEIGHT_EDITOR=0.64f*3/14;
15 |
16 | //content 与 editor的间隙
17 | public static final float FRACTION_PADDING=FRACTION_WIDTH_BGCONTENT*0.64f / 12;
18 |
19 |
20 | //从顶部到底部滑动的总时间
21 | public static final long DURATION_SCROLL=500;
22 |
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/ItemFragment.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.app.Fragment;
7 | import android.support.v4.view.ViewCompat;
8 | import android.support.v4.widget.SwipeRefreshLayout;
9 | import android.support.v7.widget.LinearLayoutManager;
10 | import android.support.v7.widget.RecyclerView;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 |
15 | import cq.airbnb.IsChildRequestScrollListener;
16 | import cq.behaviordemo.adapter.FriendInfoAdapter;
17 |
18 | /**
19 | * 列表Fragment
20 | */
21 |
22 | public class ItemFragment extends Fragment implements IsChildRequestScrollListener {
23 |
24 | private RecyclerView mRecyclerView;
25 | private SwipeRefreshLayout mSwipeRefreshLayout;
26 |
27 | public static ItemFragment newInstance() {
28 |
29 | Bundle args = new Bundle();
30 |
31 | ItemFragment fragment = new ItemFragment();
32 | fragment.setArguments(args);
33 | return fragment;
34 | }
35 |
36 | @Nullable
37 | @Override
38 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
39 | View v = inflater.inflate(R.layout.fragment_item, container, false);
40 | mRecyclerView = (RecyclerView) v.findViewById(R.id.recycler_view);
41 | mSwipeRefreshLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_layout);
42 | initView();
43 | return v;
44 | }
45 |
46 | private void initView() {
47 | mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
48 | mRecyclerView.setAdapter(new FriendInfoAdapter());
49 |
50 | mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
51 | @Override public void onRefresh() {
52 | new Handler().postDelayed(new Runnable() {
53 | @Override public void run() {
54 | mSwipeRefreshLayout.setRefreshing(false);
55 | }
56 | }, 1000);
57 | }
58 | });
59 | }
60 |
61 |
62 | @Override
63 | public boolean requestScroll(boolean up,boolean shouldNotRefresh) {
64 | //向上滑动,并且 mRecyclerView 可以上滑动
65 | return (up && ViewCompat.canScrollVertically(mRecyclerView, 1)) ||
66 | //向下滑动,且可以下滑或者(可以刷新,且不在初始位置,不在顶部)
67 | (!up && (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
68 | (!shouldNotRefresh&&((LinearLayoutManager) mRecyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0)));
69 |
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.v4.view.ViewPager;
6 | import android.support.v7.app.AppCompatActivity;
7 |
8 | import cq.behaviordemo.adapter.ItemAdapter;
9 |
10 | public class MainActivity extends AppCompatActivity {
11 | private TabLayout mTabLayout;
12 | private ViewPager mViewPager;
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 | findView();
18 | initView();
19 | initEvent();
20 | }
21 |
22 | private void findView() {
23 | mTabLayout=(TabLayout) findViewById(R.id.tab_layout);
24 | mViewPager=(ViewPager) findViewById(R.id.viewpager);
25 | }
26 |
27 |
28 | private void initView() {
29 |
30 | mViewPager.setAdapter(new ItemAdapter(getSupportFragmentManager(),mViewPager));
31 | mTabLayout.setupWithViewPager(mViewPager);
32 | }
33 |
34 | private void initEvent() {
35 | mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
36 | @Override
37 | public void onTabSelected(TabLayout.Tab tab) {
38 | mViewPager.setCurrentItem(tab.getPosition());
39 | }
40 |
41 | @Override
42 | public void onTabUnselected(TabLayout.Tab tab) {
43 |
44 | }
45 |
46 | @Override
47 | public void onTabReselected(TabLayout.Tab tab) {
48 |
49 | }
50 | });
51 |
52 | }
53 |
54 |
55 |
56 |
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/adapter/FriendInfoAdapter.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.adapter;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * Created by cqll on 2016/9/30.
13 | */
14 |
15 | public class FriendInfoAdapter extends RecyclerView.Adapter{
16 |
17 | @Override
18 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
19 |
20 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_friend_info,parent,false));
21 | }
22 |
23 | @Override
24 | public void onBindViewHolder(ViewHolder holder, int position) {
25 | }
26 |
27 | @Override
28 | public int getItemCount() {
29 | return 10;
30 | }
31 |
32 | static class ViewHolder extends RecyclerView.ViewHolder{
33 | public TextView txt;
34 | public ViewHolder(View itemView) {
35 | super(itemView);
36 | txt= (TextView) itemView.findViewById(R.id.txt);
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/adapter/ItemAdapter.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.adapter;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentStatePagerAdapter;
6 | import android.support.v4.view.ViewPager;
7 |
8 | import java.lang.ref.WeakReference;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import cq.airbnb.IsChildRequestScrollListener;
13 | import cq.behaviordemo.ItemFragment;
14 | import cq.behaviordemo.listener.NeedExpandListener;
15 | import cq.behaviordemo.listener.SupportNeedExpendListener;
16 |
17 |
18 | public class ItemAdapter extends FragmentStatePagerAdapter implements IsChildRequestScrollListener,SupportNeedExpendListener{
19 | private String [] titles={"MEDIA","ABOUT","REVIEWS"};
20 | private String [] titles_air={"推荐","房源","体验","攻略"};
21 | private WeakReference mViewPager;//也许有点用
22 | private List mFragments;
23 | private NeedExpandListener mNeedExpandListener;
24 |
25 | private int mFlag;
26 | public ItemAdapter(FragmentManager fm) {
27 | this(fm,null);
28 | }
29 |
30 | public ItemAdapter(FragmentManager fm, ViewPager viewPager) {
31 | this(fm,viewPager,1);
32 | }
33 | public ItemAdapter(FragmentManager fm, ViewPager viewPager,int flag) {
34 | super(fm);
35 | mFlag=flag;
36 | mViewPager = new WeakReference(viewPager);
37 |
38 | mFragments=new ArrayList();
39 | mFragments.add(ItemFragment.newInstance());
40 | mFragments.add(ItemFragment.newInstance());
41 | mFragments.add(ItemFragment.newInstance());
42 | if(flag==2){
43 | mFragments.add(ItemFragment.newInstance());
44 | }
45 | fillListener();
46 | }
47 |
48 | @Override
49 | public Fragment getItem(int position) {
50 | return mFragments.get(position);
51 | }
52 |
53 | @Override
54 | public int getCount() {
55 | return mFragments.size();
56 | }
57 |
58 | @Override
59 | public CharSequence getPageTitle(int position) {
60 | return mFlag==1?titles[position]:titles_air[position];
61 | }
62 |
63 |
64 | @Override
65 | public boolean requestScroll(boolean up,boolean shouldNotRefresh) {
66 | //有子项目,有设置 vp ,没被清掉
67 | if(mViewPager!=null&&mViewPager.get()!=null ){
68 | int currentItem=mViewPager.get().getCurrentItem();
69 | //实现了接口
70 | if(getItem(currentItem) instanceof IsChildRequestScrollListener)
71 | return ((IsChildRequestScrollListener)getItem(currentItem)).requestScroll(up,shouldNotRefresh);
72 | }
73 |
74 | return false;
75 | }
76 |
77 | @Override
78 | public void setNeedExpendListener(NeedExpandListener listener) {
79 | mNeedExpandListener=listener;
80 | fillListener();
81 | }
82 |
83 | @Override
84 | public NeedExpandListener getNeedExpendListener() {
85 | return mNeedExpandListener;
86 | }
87 |
88 | private void fillListener(){
89 | if(mFragments.size()>0){
90 | for(Fragment fragment:mFragments){
91 | if(fragment instanceof SupportNeedExpendListener &&((SupportNeedExpendListener)fragment).getNeedExpendListener()==null){
92 | ((SupportNeedExpendListener)fragment).setNeedExpendListener(mNeedExpandListener);
93 | }
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/BGBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.util.DisplayMetrics;
8 | import android.view.View;
9 |
10 | import cq.behaviordemo.Constants;
11 | import cq.behaviordemo.R;
12 |
13 | /**
14 | * Created by cqll on 2016/12/12.
15 | */
16 |
17 | public class BGBehavior extends CoordinatorLayout.Behavior{
18 |
19 | private int mHeightToolbar,mIconSizeStart,mWidth,mStartTop;
20 | public BGBehavior() {
21 | }
22 |
23 | public BGBehavior(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
26 | mIconSizeStart = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start);
27 | DisplayMetrics metrics=context.getResources().getDisplayMetrics();
28 | mWidth=metrics.widthPixels;
29 |
30 | mStartTop=(int) (mHeightToolbar+mIconSizeStart/2+mWidth* Constants.FRACTION_WIDTH_BGCONTENT*Constants.FRACTION_HEIGHT_BGCONTENT/2f);
31 | }
32 |
33 |
34 | @Override
35 | public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
36 | child.measure(parentWidthMeasureSpec, View.MeasureSpec.makeMeasureSpec(parent.findViewById(R.id.tab_layout).getTop()-mStartTop, View.MeasureSpec.EXACTLY));
37 | return true;
38 | }
39 |
40 | //向下滑到指定的地点
41 | @Override
42 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
43 | parent.onLayoutChild(child,layoutDirection);
44 | child.offsetTopAndBottom(mStartTop);
45 | return true;
46 | }
47 |
48 | @Override
49 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
50 | return dependency instanceof TabLayout;
51 | }
52 |
53 |
54 | @Override
55 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
56 | child.setTranslationY((int) (dependency.getTranslationY()));
57 | return true;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/BGContentBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.util.DisplayMetrics;
8 | import android.view.View;
9 |
10 | import cq.behaviordemo.Constants;
11 | import cq.behaviordemo.R;
12 |
13 | /**
14 | * Created by cqll on 2016/12/12.
15 | */
16 |
17 | public class BGContentBehavior extends CoordinatorLayout.Behavior{
18 | private int mTabMaxTranslation,mHeightToolbar,mIconSizeStart,mBgWidthStart,mBgHeightStart,mWidth,mDistance;
19 | private float mFraction;
20 | private View mStatistics;
21 | public BGContentBehavior() {
22 | }
23 |
24 | public BGContentBehavior(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
27 | mIconSizeStart = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start);
28 |
29 | DisplayMetrics metrics=context.getResources().getDisplayMetrics();
30 | mWidth=metrics.widthPixels;
31 | mBgWidthStart= (int) (mWidth* Constants.FRACTION_WIDTH_BGCONTENT);
32 | mBgHeightStart= (int) (mBgWidthStart*Constants.FRACTION_HEIGHT_BGCONTENT);
33 |
34 | }
35 |
36 |
37 |
38 | //测量宽高
39 | @Override
40 | public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
41 | child.measure(View.MeasureSpec.makeMeasureSpec(mBgWidthStart, View.MeasureSpec.EXACTLY),View.MeasureSpec.makeMeasureSpec(mBgHeightStart, View.MeasureSpec.EXACTLY));
42 | return true;
43 | }
44 |
45 | //移动view到中间
46 | @Override
47 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
48 | parent.onLayoutChild(child,layoutDirection);
49 | int dependencyInitialTop = parent.findViewById(R.id.tab_layout).getTop();
50 |
51 | //当child上滑动到 toolbar 下面的时候,大小应该正好占满屏幕
52 |
53 | //计算此时 tab移动的比例
54 | float fraction = 1-(dependencyInitialTop-mIconSizeStart- mHeightToolbar) /1.0f/ (dependencyInitialTop - mHeightToolbar);
55 |
56 | //计算缩放要调整的比例
57 | mFraction=(mWidth/1.0f/mBgWidthStart-1)/fraction;
58 |
59 |
60 | mDistance=2*mHeightToolbar+mIconSizeStart/2;
61 | child.offsetTopAndBottom(mHeightToolbar+mIconSizeStart/2);
62 | child.offsetLeftAndRight(mWidth/2-mBgWidthStart/2);
63 |
64 | mStatistics=child.findViewById(R.id.lyt_statistics);
65 |
66 | //tab最大的translation的距离
67 | mTabMaxTranslation=parent.findViewById(R.id.tab_layout).getTop()-mHeightToolbar;
68 | return true;
69 | }
70 |
71 | @Override
72 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
73 | return dependency instanceof TabLayout;
74 | }
75 |
76 |
77 | @Override
78 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
79 | //计算移动的比例 0~1
80 | float fraction = Math.abs(dependency.getTranslationY())/ mTabMaxTranslation;
81 | child.setScaleX(1 + (fraction*mFraction));
82 |
83 | //下面的文字不缩放..
84 | mStatistics.setScaleX(1/(1 + (fraction*mFraction)));
85 |
86 | child.setTranslationY(-fraction*mDistance);
87 | if(fraction<0.7f){
88 | mStatistics.setAlpha(1-fraction/0.7f);
89 | }else {
90 | mStatistics.setAlpha(0);
91 | }
92 |
93 | return true;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/EditorBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.util.DisplayMetrics;
8 | import android.view.View;
9 |
10 | import cq.behaviordemo.Constants;
11 | import cq.behaviordemo.R;
12 |
13 | /**
14 | * Created by cqll on 2016/12/13.
15 | */
16 |
17 | public class EditorBehavior extends CoordinatorLayout.Behavior{
18 | private float mIconSizeStart,mDistance,mWidth;
19 | private int mHeightToolbar,mTabMaxTranslation,mChildWidth,mChildHeight,mBgContentHeight,mEditorPadding;
20 | public EditorBehavior() {
21 | }
22 |
23 | public EditorBehavior(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 |
26 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
27 | mIconSizeStart = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start);
28 | mEditorPadding = context.getResources().getDimensionPixelOffset(R.dimen.editor_padding);
29 | mDistance=2*mHeightToolbar+mIconSizeStart/2;
30 |
31 | DisplayMetrics metrics=context.getResources().getDisplayMetrics();
32 | mWidth=metrics.widthPixels;
33 | mChildWidth= (int) (mWidth* Constants.FRACTION_WIDTH_BGCONTENT);
34 | mChildHeight= (int) (mChildWidth*Constants.FRACTION_HEIGHT_EDITOR);
35 | mBgContentHeight= (int) (mChildWidth*Constants.FRACTION_HEIGHT_BGCONTENT);
36 | }
37 |
38 | @Override
39 | public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
40 | child.measure(View.MeasureSpec.makeMeasureSpec(mChildWidth, View.MeasureSpec.EXACTLY),View.MeasureSpec.makeMeasureSpec(mChildHeight, View.MeasureSpec.EXACTLY));
41 | return true;
42 | }
43 |
44 | @Override
45 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
46 | parent.onLayoutChild(child,layoutDirection);
47 |
48 | //tab最大的translation的距离
49 | mTabMaxTranslation=parent.findViewById(R.id.tab_layout).getTop()-mHeightToolbar;
50 | //留下预先设置好的padding
51 | child.offsetTopAndBottom((int) (mHeightToolbar+mIconSizeStart/2+mBgContentHeight+mEditorPadding));
52 | child.offsetLeftAndRight((int) (mWidth/2-mChildWidth/2));
53 | return true;
54 | }
55 |
56 | @Override
57 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
58 | return dependency instanceof TabLayout;
59 | }
60 |
61 |
62 | //跟 bgContent的移动一样,只不过是自身 alpha的变化
63 | @Override
64 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
65 | //计算移动的比例 0~1
66 | float fraction = Math.abs(dependency.getTranslationY())/ mTabMaxTranslation;;
67 | child.setTranslationY(-fraction*mDistance);
68 |
69 | if(fraction<0.7f){
70 | child.setAlpha(1-fraction/0.7f);
71 | }else {
72 | child.setAlpha(0);
73 | }
74 |
75 | return true;
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/HeaderScrollingViewBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.support.design.widget.AppBarLayout;
6 | import android.support.design.widget.CoordinatorLayout;
7 | import android.support.v4.view.GravityCompat;
8 | import android.support.v4.view.ViewCompat;
9 | import android.util.AttributeSet;
10 | import android.view.Gravity;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * 头部需要滚动的,列表的behavior
18 | */
19 |
20 | public abstract class HeaderScrollingViewBehavior extends ViewOffsetBehavior {
21 |
22 | final Rect mTempRect1 = new Rect();
23 | final Rect mTempRect2 = new Rect();
24 |
25 | private int mVerticalLayoutGap = 0;
26 | private int mOverlayTop;
27 |
28 | public HeaderScrollingViewBehavior() {
29 | }
30 |
31 | public HeaderScrollingViewBehavior(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 | }
34 |
35 | /**
36 | * 计算宽高
37 | *
38 | */
39 | @Override
40 | public boolean onMeasureChild(CoordinatorLayout parent, View child,
41 | int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
42 | int heightUsed) {
43 | final int childLpHeight = child.getLayoutParams().height;
44 | if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
45 | || childLpHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
46 | // If the menu's height is set to match_parent/wrap_content then measure it
47 | // with the maximum visible height
48 |
49 | final List dependencies = parent.getDependencies(child);
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 | //可用的高度
66 | int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
67 | if (availableHeight == 0) {
68 | // If the measure spec doesn't specify a size, use the current height
69 | availableHeight = parent.getHeight();
70 | }
71 |
72 | //列表高度为=可用高度-依赖的view的高度+滚动的距离
73 | final int height = availableHeight - header.getMeasuredHeight()
74 | + getScrollRange(header);
75 | final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height,
76 | childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
77 | ? View.MeasureSpec.EXACTLY
78 | : View.MeasureSpec.AT_MOST);
79 |
80 | // Now measure the scrolling view with the correct height
81 | parent.onMeasureChild(child, parentWidthMeasureSpec,
82 | widthUsed, heightMeasureSpec, heightUsed);
83 |
84 | return true;
85 | }
86 | }
87 | return false;
88 | }
89 |
90 | @Override
91 | protected void layoutChild(final CoordinatorLayout parent, final View child,
92 | final int layoutDirection) {
93 | final List dependencies = parent.getDependencies(child);
94 | final View header = findFirstDependency(dependencies);
95 |
96 | if (header != null) {
97 | final CoordinatorLayout.LayoutParams lp =
98 | (CoordinatorLayout.LayoutParams) child.getLayoutParams();
99 | final Rect available = mTempRect1;
100 | //列表可用的 layoutParams . bottom 是为了 列表的高度为parent的可用高度
101 | available.set(parent.getPaddingLeft() + lp.leftMargin,
102 | header.getBottom() + lp.topMargin,
103 | parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,
104 | parent.getHeight() + header.getBottom()
105 | - parent.getPaddingBottom() - lp.bottomMargin);
106 |
107 | final Rect out = mTempRect2;
108 | //设置gravity
109 | GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
110 | child.getMeasuredHeight(), available, out, layoutDirection);
111 | //重叠的大小
112 | final int overlap = getOverlapPixelsForOffset(header);
113 |
114 | child.layout(out.left, out.top - overlap, out.right, out.bottom - overlap);
115 | mVerticalLayoutGap = out.top - header.getBottom();
116 | } else {
117 | // If we don't have a dependency, let super handle it
118 | super.layoutChild(parent, child, layoutDirection);
119 | mVerticalLayoutGap = 0;
120 | }
121 | }
122 |
123 | float getOverlapRatioForOffset(final View header) {
124 | return 1f;
125 | }
126 |
127 | final int getOverlapPixelsForOffset(final View header) {
128 | return mOverlayTop == 0 ? 0 : MathUtils.constrain(
129 | (int) (getOverlapRatioForOffset(header) * mOverlayTop), 0, mOverlayTop);
130 | }
131 |
132 | private static int resolveGravity(int gravity) {
133 | return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
134 | }
135 |
136 | public abstract View findFirstDependency(List views);
137 |
138 | protected int getScrollRange(View v) {
139 | return v.getMeasuredHeight();
140 | }
141 |
142 | /**
143 | * The gap between the top of the scrolling view and the bottom of the header layout in pixels.
144 | */
145 | final int getVerticalLayoutGap() {
146 | return mVerticalLayoutGap;
147 | }
148 |
149 | /**
150 | * Set the distance that this view should overlap any {@link AppBarLayout}.
151 | *
152 | * @param overlayTop the distance in px
153 | */
154 | public final void setOverlayTop(int overlayTop) {
155 | mOverlayTop = overlayTop;
156 | }
157 |
158 | /**
159 | * Returns the distance that this view should overlap any {@link AppBarLayout}.
160 | */
161 | public final int getOverlayTop() {
162 | return mOverlayTop;
163 | }
164 |
165 | static class MathUtils {
166 |
167 | static int constrain(int amount, int low, int high) {
168 | return amount < low ? low : (amount > high ? high : amount);
169 | }
170 |
171 | static float constrain(float amount, float low, float high) {
172 | return amount < low ? low : (amount > high ? high : amount);
173 | }
174 |
175 | }
176 |
177 |
178 |
179 |
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/IconBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * Created by cqll on 2016/12/8.
13 | * 头像
14 | */
15 |
16 | public class IconBehavior extends CoordinatorLayout.Behavior {
17 |
18 | //依赖的view的距离顶部的初始距离,被依赖的view会先绘制
19 | private float mIconStartLeft,
20 | mIconSizeStart, mIconSizeEnd, mIconDistanceX, mIconDistanceY, mHeightToolbar;
21 | private int mTabMaxTranslation;
22 |
23 | public IconBehavior() {
24 |
25 | }
26 |
27 | public IconBehavior(Context context, AttributeSet attrs) {
28 | super(context, attrs);
29 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
30 | mIconSizeStart = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start);
31 | mIconSizeEnd = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_end);
32 |
33 | }
34 |
35 | /**
36 | * 绘制完成后应该移动到toolbar下面,水平方向居中
37 | */
38 | @Override
39 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
40 | parent.onLayoutChild(child, layoutDirection);
41 |
42 | //tab最大的translation的距离
43 | mTabMaxTranslation = (int) (parent.findViewById(R.id.tab_layout).getTop() - mHeightToolbar);
44 |
45 | float mWidth = parent.findViewById(R.id.toolbar).getWidth();
46 |
47 | //头像开始时距离左边的距离
48 | mIconStartLeft = mWidth / 2 - mIconSizeStart / 2;
49 |
50 | //用头像的圆点计算
51 | mIconDistanceY = mHeightToolbar + mIconSizeStart / 2 - mHeightToolbar / 2;
52 | mIconDistanceX = mWidth / 2 - parent.findViewById(R.id.img_back).getRight() - mIconSizeEnd / 2;
53 | //移动到 toolbar下面
54 | child.offsetTopAndBottom((int) mHeightToolbar);
55 | child.offsetLeftAndRight((int) mIconStartLeft);
56 | return true;
57 | }
58 |
59 | //跟随 vp
60 | @Override
61 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
62 | return dependency instanceof TabLayout;
63 | }
64 |
65 |
66 | //tab向上向下移动是改变头像
67 | @Override
68 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
69 | //计算移动的比例 0~1
70 | float fraction = Math.abs(dependency.getTranslationY()) / mTabMaxTranslation;
71 |
72 | //负的向上
73 | child.setTranslationY(-mIconDistanceY * fraction);
74 | child.setTranslationX(-mIconDistanceX * fraction);
75 |
76 | fraction = 1 - (1 - mIconSizeEnd / mIconSizeStart) * fraction;
77 | //缩放
78 | child.setScaleX(fraction);
79 | child.setScaleY(fraction);
80 |
81 | return true;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/ListBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import java.util.List;
10 |
11 | import cq.behaviordemo.R;
12 |
13 | /**
14 | * Created by cqll on 2016/12/9.
15 | */
16 |
17 | public class ListBehavior extends HeaderScrollingViewBehavior {
18 | private int mHeightToolbar;
19 | public ListBehavior(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | mHeightToolbar=context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
22 | }
23 |
24 | @Override
25 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
26 | return isDependOn(dependency);
27 | }
28 |
29 |
30 | @Override public View findFirstDependency(List views) {
31 | for (View view : views) {
32 | if (isDependOn(view))
33 | return view;
34 | }
35 | return null;
36 | }
37 |
38 | @Override protected int getScrollRange(View v) {
39 | if (isDependOn(v)) {
40 | return -mHeightToolbar;
41 | } else {
42 | return super.getScrollRange(v);
43 | }
44 | }
45 |
46 | private boolean isDependOn(View dependency) {
47 | return dependency instanceof TabLayout;
48 | }
49 |
50 | //跟随tab移动
51 | @Override
52 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
53 |
54 | child.setTranslationY(dependency.getTranslationY());
55 | return true;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/NameBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * Created by cqll on 2016/12/13.
13 | */
14 |
15 | public class NameBehavior extends CoordinatorLayout.Behavior{
16 | private float mPaddingIcon, mPaddingScore,mIconSizeStart,mIconSizeEnd,mHeightToolbar,mChildWidth,mChildHeight,mWidth,mIconDistanceY,mIconDistanceX;
17 | private float mFractionScale=0.1f;//缩小的比例
18 | private int mTabMaxTranslation,mEditorPadding;
19 | public NameBehavior() {
20 | }
21 |
22 | public NameBehavior(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
25 | mIconSizeStart = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start);
26 | mIconSizeEnd = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_end);
27 | mPaddingIcon = context.getResources().getDimensionPixelOffset(R.dimen.icon_padding);
28 | mPaddingScore = context.getResources().getDimensionPixelOffset(R.dimen.score_padding);
29 | mWidth=context.getResources().getDisplayMetrics().widthPixels;
30 | mEditorPadding =context.getResources().getDimensionPixelOffset(R.dimen.editor_padding);
31 | }
32 |
33 |
34 | //移动到指定位置
35 | @Override
36 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
37 | parent.onLayoutChild(child,layoutDirection);
38 | mChildWidth=child.getWidth();
39 | mChildHeight=child.getHeight();
40 |
41 | int scoreHeight=parent.findViewById(R.id.lyt_score).getHeight();
42 | mIconDistanceY =mChildHeight/2+ scoreHeight/2+ mPaddingScore +mIconSizeStart+mHeightToolbar-mHeightToolbar/4;
43 | mIconDistanceX = mWidth / 2 - parent.findViewById(R.id.img_back).getRight() - mIconSizeEnd - mPaddingIcon -mChildWidth/2;
44 |
45 | //tab最大的translation的距离
46 | mTabMaxTranslation= (int) (parent.findViewById(R.id.tab_layout).getTop()-mHeightToolbar-mEditorPadding);
47 |
48 | child.offsetTopAndBottom((int) (mHeightToolbar+mIconSizeStart+scoreHeight/2+ mPaddingScore ));
49 | child.offsetLeftAndRight((int) (mWidth/2-mChildWidth/2));
50 | return true;
51 | }
52 |
53 | @Override
54 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
55 | return dependency instanceof TabLayout;
56 | }
57 |
58 | @Override
59 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
60 | //计算移动的比例 0~1
61 | float fraction = Math.abs(dependency.getTranslationY())/ mTabMaxTranslation;
62 |
63 | //缩小
64 | child.setPivotX(0);
65 | child.setPivotY(child.getHeight()/2);
66 | child.setScaleX(1-fraction*mFractionScale);
67 | child.setScaleY(1-fraction*mFractionScale);
68 |
69 | child.setTranslationY(-mIconDistanceY * fraction);
70 | child.setTranslationX(-mIconDistanceX * fraction);
71 | return true;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/ScoreBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.content.Context;
4 | import android.support.design.widget.CoordinatorLayout;
5 | import android.support.design.widget.TabLayout;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * Created by cqll on 2016/12/13.
13 | */
14 |
15 | public class ScoreBehavior extends CoordinatorLayout.Behavior{
16 | private float mIconPadding,mIconSizeStart,mIconSizeEnd,mHeightToolbar,mChildWidth,mChildHeight,mWidth,mIconDistanceY,mIconDistanceX;
17 | private int mTabMaxTranslation;
18 | public ScoreBehavior() {
19 | }
20 |
21 | public ScoreBehavior(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
24 | mIconSizeStart = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start);
25 | mIconSizeEnd = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_end);
26 | mIconPadding = context.getResources().getDimensionPixelOffset(R.dimen.icon_padding);
27 | mWidth=context.getResources().getDisplayMetrics().widthPixels;
28 |
29 | }
30 |
31 |
32 | //移动到指定位置
33 | @Override
34 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
35 | parent.onLayoutChild(child,layoutDirection);
36 | mChildWidth=child.getWidth();
37 | mChildHeight=child.getHeight();
38 | //tab最大的translation的距离
39 | mTabMaxTranslation= (int) (parent.findViewById(R.id.tab_layout).getTop()-mHeightToolbar);
40 |
41 | mIconDistanceY = mIconSizeStart+mHeightToolbar-mHeightToolbar*3/4;
42 | mIconDistanceX = mWidth / 2 - parent.findViewById(R.id.img_back).getRight() - mIconSizeEnd - mIconPadding-mChildWidth/2;
43 |
44 |
45 | child.offsetTopAndBottom((int) (mHeightToolbar+mIconSizeStart-mChildHeight/2));
46 | child.offsetLeftAndRight((int) (mWidth/2-mChildWidth/2));
47 | return true;
48 | }
49 |
50 | @Override
51 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
52 | return dependency instanceof TabLayout;
53 | }
54 |
55 | @Override
56 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
57 | //计算移动的比例 0~1
58 | float fraction = Math.abs(dependency.getTranslationY())/ mTabMaxTranslation;
59 |
60 | child.setTranslationY(-mIconDistanceY * fraction);
61 | child.setTranslationX(-mIconDistanceX * fraction);
62 | return true;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/TabBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.support.design.widget.CoordinatorLayout;
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 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | import cq.behaviordemo.Constants;
17 | import cq.behaviordemo.R;
18 | import cq.behaviordemo.listener.IsChildRequestScrollListener;
19 | import cq.behaviordemo.listener.NeedExpandListener;
20 | import cq.behaviordemo.listener.SupportNeedExpendListener;
21 |
22 | /**
23 | * Created by cqll on 2016/12/9.
24 | */
25 |
26 | public class TabBehavior extends CoordinatorLayout.Behavior implements NeedExpandListener {
27 | private int mHeightToolbar, mMaxDistance, mEditorPadding;
28 | private Context mContext;
29 | private boolean mUp/*向上滑动,还是向下滑动*/,mControlChange/*手指控制的滑动*/;
30 | private ValueAnimator mValueAnimator;
31 | private ViewPager mViewPager;
32 | private View mTab;
33 | private List mHardwareViews;
34 | public TabBehavior() {
35 | }
36 |
37 | public TabBehavior(Context context, AttributeSet attrs) {
38 | super(context, attrs);
39 | mContext = context;
40 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
41 | mEditorPadding = context.getResources().getDimensionPixelOffset(R.dimen.editor_padding);
42 |
43 | mValueAnimator = ValueAnimator.ofFloat(0, 1);
44 | mHardwareViews=new ArrayList<>();
45 | }
46 |
47 |
48 | /**
49 | * 计算tab应该出现的位置
50 | * toolbar 高度
51 | * icon 高度 /2
52 | * child.getWidth()*0.9f*0.64f 白色填充的当前高度
53 | * child.getWidth()*0.9f*0.64/16 间隙的高度
54 | */
55 | @Override
56 | public boolean onLayoutChild(final CoordinatorLayout parent, final View child, int layoutDirection) {
57 | parent.onLayoutChild(child, layoutDirection);
58 | mTab=child;
59 | mViewPager = (ViewPager) parent.findViewById(R.id.viewpager);
60 | mMaxDistance = (int) (child.getWidth() * Constants.FRACTION_WIDTH_BGCONTENT * 0.64f + mContext.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start) / 2 + child.getWidth() * Constants.FRACTION_PADDING + mEditorPadding);
61 | child.offsetTopAndBottom(mMaxDistance + mHeightToolbar);
62 | if(mHardwareViews.size()==0){
63 | mHardwareViews.add(parent.findViewById(R.id.txt_name));
64 | mHardwareViews.add(parent.findViewById(R.id.img_icon));
65 | mHardwareViews.add(parent.findViewById(R.id.lyt_score));
66 | mHardwareViews.add(parent.findViewById(R.id.tab_layout));
67 | mHardwareViews.add(parent.findViewById(R.id.bg));
68 | mHardwareViews.add(parent.findViewById(R.id.lyt_editor));
69 | mHardwareViews.add(parent.findViewById(R.id.lyt_statistics));
70 |
71 | //开启硬件离屏缓存
72 | mValueAnimator.addListener(new AnimatorListenerAdapter() {
73 | @Override
74 | public void onAnimationEnd(Animator animation) {
75 | super.onAnimationEnd(animation);
76 | for(View v:mHardwareViews){
77 | v.setLayerType(View.LAYER_TYPE_NONE,null);
78 | }
79 | }
80 |
81 | @Override
82 | public void onAnimationStart(Animator animation) {
83 | super.onAnimationStart(animation);
84 | for(View v:mHardwareViews){
85 | v.setLayerType(View.LAYER_TYPE_HARDWARE,null);
86 | }
87 | }
88 | });
89 | }
90 |
91 |
92 |
93 | return true;
94 | }
95 |
96 |
97 | @Override
98 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
99 | mControlChange=true;
100 |
101 | if(mViewPager.getAdapter() != null && //有适配器
102 | mViewPager.getAdapter().getCount() > 0 &&//有item
103 | mViewPager.getAdapter() instanceof SupportNeedExpendListener&&
104 | ((SupportNeedExpendListener) mViewPager.getAdapter()).getNeedExpendListener()==null){
105 | ((SupportNeedExpendListener) mViewPager.getAdapter()).setNeedExpendListener(this);
106 | }
107 | return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
108 | }
109 |
110 |
111 | /**
112 | * @param dy 向上滑大于0
113 | */
114 | @Override
115 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
116 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
117 | mUp = dy > 0;
118 | if(isChildRequestScroll(child.getTranslationY())){//如果list需要滑动这边就不动
119 | consumed[1]=0;
120 | return;
121 | }
122 | consumed[1]=dy;//全部消耗
123 | int distance = -dy / 2;//降低移动的速度
124 |
125 |
126 | if (child.getTranslationY() + distance < -mMaxDistance) {
127 | distance = -mMaxDistance;
128 | } else if (child.getTranslationY() + distance > 0) {
129 | distance = 0;
130 | } else {
131 | distance = (int) (child.getTranslationY() + distance);
132 | }
133 | child.setTranslationY(distance);
134 | }
135 |
136 |
137 | @Override
138 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
139 | super.onStopNestedScroll(coordinatorLayout, child, target);
140 | mControlChange=false;
141 | float translationY = child.getTranslationY();
142 | if (Math.abs(translationY) == mMaxDistance || translationY == 0) {
143 | return;
144 | }
145 |
146 | scroll(child, translationY);
147 |
148 | }
149 |
150 |
151 | /**
152 | * list 不需要滑动就拦截.需要就不拦截
153 | */
154 |
155 | @Override
156 | public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
157 | return !isChildRequestScroll(child.getTranslationY());
158 | }
159 |
160 | private void scroll(final View child, final float translationY) {
161 | final float shouldMoveDistance;
162 | if (mUp) {
163 | //没超过 1/8
164 | if (Math.abs(translationY) < mMaxDistance / 8f) {
165 | shouldMoveDistance = Math.abs(translationY);
166 | } else {
167 | shouldMoveDistance = Math.abs(translationY) - mMaxDistance;
168 | }
169 | } else {//向下滑动
170 | // 没超过 1/8
171 | if (Math.abs(translationY) > mMaxDistance * 7f / 8f) {
172 | shouldMoveDistance = -(mMaxDistance + translationY);
173 | } else {
174 | shouldMoveDistance = Math.abs(translationY);
175 | }
176 | }
177 |
178 | mValueAnimator.setDuration((long) (Math.abs(shouldMoveDistance) / mMaxDistance * Constants.DURATION_SCROLL));
179 | mValueAnimator.removeAllUpdateListeners();
180 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
181 | @Override
182 | public void onAnimationUpdate(ValueAnimator animation) {
183 | child.setTranslationY(translationY + animation.getAnimatedFraction() * shouldMoveDistance);
184 | }
185 | });
186 | mValueAnimator.start();
187 | }
188 |
189 |
190 | /**
191 | * Child是否需要滑动
192 | */
193 | private boolean isChildRequestScroll(float translationY) {
194 | return (translationY == -mMaxDistance &&//在顶部
195 | mViewPager.getAdapter() != null && //有适配器
196 | mViewPager.getAdapter().getCount() > 0 &&//有item
197 | mViewPager.getAdapter() instanceof IsChildRequestScrollListener && //实现了
198 | ((IsChildRequestScrollListener) mViewPager.getAdapter()).requestScroll(mUp)//需要滑动
199 | );
200 | }
201 |
202 |
203 | /**
204 | * list fling到头的时候 展开
205 | */
206 | @Override
207 | public void needExpand() {
208 | if(!mControlChange){
209 | mValueAnimator.setDuration(500);
210 | mValueAnimator.removeAllUpdateListeners();
211 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
212 | @Override
213 | public void onAnimationUpdate(ValueAnimator animation) {
214 | mTab.setTranslationY((animation.getAnimatedFraction()-1)*mMaxDistance);
215 | }
216 | });
217 | mValueAnimator.start();
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/ToolBarIconBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.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.ImageView;
8 |
9 | import cq.behaviordemo.R;
10 |
11 | /**
12 | * Created by cqll on 2016/12/13.
13 | */
14 |
15 | public class ToolBarIconBehavior extends CoordinatorLayout.Behavior {
16 | private float mHeightToolbar, mDependencyMaxTranslation, mIconSizeStart;
17 | private ImageView mImgBack, mImgShare;
18 |
19 | public ToolBarIconBehavior() {
20 | }
21 |
22 | public ToolBarIconBehavior(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | mHeightToolbar = context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
25 | mIconSizeStart = context.getResources().getDimensionPixelOffset(R.dimen.img_icon_height_start);
26 |
27 | }
28 |
29 | @Override
30 | public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
31 | parent.onLayoutChild(child, layoutDirection);
32 | mImgBack = (ImageView) parent.findViewById(R.id.img_back);
33 | mImgShare = (ImageView) parent.findViewById(R.id.img_share);
34 |
35 | mDependencyMaxTranslation = 2*mHeightToolbar+mIconSizeStart/2;
36 | return true;
37 | }
38 |
39 |
40 | //依赖白色的内容背景
41 | @Override
42 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
43 | return dependency.getId() == R.id.fyt_content;
44 | }
45 |
46 |
47 | @Override
48 | public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
49 | float fraction = Math.abs(dependency.getTranslationY()) / mDependencyMaxTranslation;
50 |
51 | if (fraction < 0.2) {//用于白色变化
52 | fraction = 1 - fraction / 0.2f;// 从1到0
53 | setWhiteIcon();
54 |
55 | } else {//用于黑色变化
56 | fraction = (fraction - 0.2f) / 0.8f;// 从0到1
57 | setBlackIcon();
58 | }
59 | mImgBack.setAlpha(fraction);
60 | mImgShare.setAlpha(fraction);
61 |
62 | return true;
63 |
64 | }
65 |
66 | private void setWhiteIcon() {
67 | mImgBack.setImageResource(R.drawable.ic_arrow_back_white_24dp);
68 | mImgShare.setImageResource(R.drawable.ic_share_white_24dp);
69 | }
70 |
71 | private void setBlackIcon() {
72 | mImgBack.setImageResource(R.drawable.ic_arrow_back_black_24dp);
73 | mImgShare.setImageResource(R.drawable.ic_share_black_24dp);
74 | }
75 |
76 |
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/ViewOffsetBehavior.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.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 | *
10 | */
11 |
12 | public class ViewOffsetBehavior extends CoordinatorLayout.Behavior {
13 |
14 | private ViewOffsetHelper mViewOffsetHelper;
15 |
16 | private int mTempTopBottomOffset = 0;
17 | private int mTempLeftRightOffset = 0;
18 |
19 | public ViewOffsetBehavior() {
20 | }
21 |
22 | public ViewOffsetBehavior(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | @Override
27 | public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
28 | // First let lay the child out
29 | layoutChild(parent, child, layoutDirection);
30 |
31 | if (mViewOffsetHelper == null) {
32 | mViewOffsetHelper = new ViewOffsetHelper(child);
33 | }
34 | mViewOffsetHelper.onViewLayout();
35 |
36 | if (mTempTopBottomOffset != 0) {
37 | mViewOffsetHelper.setTopAndBottomOffset(mTempTopBottomOffset);
38 | mTempTopBottomOffset = 0;
39 | }
40 | if (mTempLeftRightOffset != 0) {
41 | mViewOffsetHelper.setLeftAndRightOffset(mTempLeftRightOffset);
42 | mTempLeftRightOffset = 0;
43 | }
44 |
45 | return true;
46 | }
47 |
48 | protected void layoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
49 | // Let the parent lay it out by default
50 | parent.onLayoutChild(child, layoutDirection);
51 | }
52 |
53 | public boolean setTopAndBottomOffset(int offset) {
54 | if (mViewOffsetHelper != null) {
55 | return mViewOffsetHelper.setTopAndBottomOffset(offset);
56 | } else {
57 | mTempTopBottomOffset = offset;
58 | }
59 | return false;
60 | }
61 |
62 | public boolean setLeftAndRightOffset(int offset) {
63 | if (mViewOffsetHelper != null) {
64 | return mViewOffsetHelper.setLeftAndRightOffset(offset);
65 | } else {
66 | mTempLeftRightOffset = offset;
67 | }
68 | return false;
69 | }
70 |
71 | public int getTopAndBottomOffset() {
72 | return mViewOffsetHelper != null ? mViewOffsetHelper.getTopAndBottomOffset() : 0;
73 | }
74 |
75 | public int getLeftAndRightOffset() {
76 | return mViewOffsetHelper != null ? mViewOffsetHelper.getLeftAndRightOffset() : 0;
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/behavior/ViewOffsetHelper.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.behavior;
2 |
3 | import android.support.v4.view.ViewCompat;
4 | import android.view.View;
5 |
6 | /**
7 | * 偏移的工具类
8 | */
9 | public class ViewOffsetHelper {
10 |
11 | private final View mView;
12 |
13 | private int mLayoutTop;
14 | private int mLayoutLeft;
15 | private int mOffsetTop;
16 | private int mOffsetLeft;
17 |
18 | public ViewOffsetHelper(View view) {
19 | mView = view;
20 | }
21 |
22 | /**
23 | * 保留初始的top 和 left
24 | */
25 | public void onViewLayout() {
26 | // Now grab the intended top
27 | mLayoutTop = mView.getTop();
28 | mLayoutLeft = mView.getLeft();
29 |
30 | // And offset it as needed
31 | updateOffsets();
32 | }
33 |
34 | private void updateOffsets() {
35 | ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop));
36 | ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft));
37 | }
38 |
39 | /**
40 | * Set the top and bottom offset for this {@link android.support.design.widget.ViewOffsetHelper}'s view.
41 | *
42 | * @param offset the offset in px.
43 | * @return true if the offset has changed
44 | */
45 | public boolean setTopAndBottomOffset(int offset) {
46 | if (mOffsetTop != offset) {
47 | mOffsetTop = offset;
48 | updateOffsets();
49 | return true;
50 | }
51 | return false;
52 | }
53 |
54 | /**
55 | * Set the left and right offset for this {@link android.support.design.widget.ViewOffsetHelper}'s view.
56 | *
57 | * @param offset the offset in px.
58 | * @return true if the offset has changed
59 | */
60 | public boolean setLeftAndRightOffset(int offset) {
61 | if (mOffsetLeft != offset) {
62 | mOffsetLeft = offset;
63 | updateOffsets();
64 | return true;
65 | }
66 | return false;
67 | }
68 |
69 | public int getTopAndBottomOffset() {
70 | return mOffsetTop;
71 | }
72 |
73 | public int getLeftAndRightOffset() {
74 | return mOffsetLeft;
75 | }
76 |
77 | public int getLayoutTop() {
78 | return mLayoutTop;
79 | }
80 |
81 | public int getLayoutLeft() {
82 | return mLayoutLeft;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/listener/IsChildRequestScrollListener.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.listener;
2 |
3 | /**
4 | * Created by cqll on 2016/12/15.
5 | * 判断子view是否需要滑动
6 | */
7 |
8 | public interface IsChildRequestScrollListener {
9 | boolean requestScroll(boolean up);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/listener/NeedExpandListener.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.listener;
2 |
3 | /**
4 | * Created by cqll on 2016/12/15.
5 | * list可以滑动的时候,向下滑,然后松开手指,list滑到顶的时候触发
6 | * 通知 tab 展开头部
7 | */
8 |
9 | public interface NeedExpandListener {
10 | void needExpand();
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/listener/SupportNeedExpendListener.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.listener;
2 |
3 | /**
4 | * Created by cqll on 2016/12/15.
5 | * 是否支持 展开
6 | */
7 |
8 | public interface SupportNeedExpendListener {
9 | void setNeedExpendListener(NeedExpandListener listener);
10 | NeedExpandListener getNeedExpendListener();
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/cq/behaviordemo/view/CircleImageView.java:
--------------------------------------------------------------------------------
1 | package cq.behaviordemo.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Path;
6 | import android.util.AttributeSet;
7 | import android.widget.ImageView;
8 |
9 | /**
10 | * Created by cqll on 2016/12/8.
11 | * copy from 巴掌
12 | */
13 |
14 | public class CircleImageView extends ImageView {
15 | private Path mPath;
16 | public CircleImageView(Context context) {
17 | super(context);
18 | init();
19 | }
20 |
21 | public CircleImageView(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | init();
24 | }
25 |
26 | private void init(){
27 | mPath=new Path();
28 | }
29 |
30 |
31 | @Override
32 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
33 | super.onSizeChanged(w, h, oldw, oldh);
34 | mPath.reset();
35 | mPath.addCircle(w/2,h/2,Math.min(w,h)/2, Path.Direction.CW);
36 |
37 | }
38 |
39 |
40 | @Override
41 | protected void dispatchDraw(Canvas canvas) {
42 | try {
43 | int save=canvas.save();
44 | canvas.clipPath(mPath);
45 | super.dispatchDraw(canvas);
46 | canvas.restoreToCount(save);
47 | }catch (Exception e){
48 | super.dispatchDraw(canvas);
49 | e.printStackTrace();
50 | }
51 | }
52 |
53 | @Override
54 | protected void onDraw(Canvas canvas) {
55 | try {
56 | int save=canvas.save();
57 | canvas.clipPath(mPath);
58 | super.onDraw(canvas);
59 | canvas.restoreToCount(save);
60 | }catch (Exception e){
61 | super.onDraw(canvas);
62 | e.printStackTrace();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_arrow_back_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_arrow_back_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_share_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_trips_search_anywhere.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_trips_search_anywhere.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_trips_search_calendar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_trips_search_calendar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_trips_search_guest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_trips_search_guest.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_trips_search_view_up_chevron.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/ic_trips_search_view_up_chevron.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/drawable-xhdpi/search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_airbnb_condition.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_airbnb_condition_gray.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_evaluation.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_tab_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/divider_padding.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_star.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_airbnb.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
25 |
26 |
31 |
32 |
33 |
45 |
46 |
55 |
56 |
61 |
62 |
69 |
70 |
71 |
80 |
81 |
86 |
87 |
94 |
95 |
96 |
97 |
106 |
107 |
112 |
113 |
120 |
121 |
122 |
123 |
124 |
125 |
138 |
139 |
145 |
146 |
154 |
155 |
156 |
157 |
166 |
167 |
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_iread.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
25 |
33 |
41 |
42 |
43 |
49 |
50 |
59 |
65 |
72 |
76 |
77 |
83 |
90 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
109 |
110 |
119 |
120 |
130 |
131 |
132 |
133 |
134 |
142 |
143 |
144 |
145 |
156 |
157 |
161 |
162 |
170 |
171 |
179 |
180 |
188 |
189 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_friend_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
26 |
27 |
40 |
41 |
45 |
46 |
54 |
55 |
56 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #3184F7
8 |
9 | #F5F5F5
10 | #E7EEFE
11 |
12 | #1E1E1E
13 |
14 |
15 | #128488
16 | #359A9D
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 5dp
6 | 10dp
7 | 2dp
8 | 55dp
9 | 50dp
10 | 210dp
11 | 170dp
12 | 70dp
13 | 40dp
14 |
15 | 8dp
16 | 4dp
17 |
18 |
19 |
20 | 16dp
21 | 12dp
22 | 10dp
23 |
24 | 24dp
25 | 20dp
26 | 60dp
27 | 56dp
28 | 68dp
29 | 260dp
30 |
31 | 180dp
32 | 310dp
33 |
34 | 16sp
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BehaviorDemo
3 |
4 |
5 |
6 | 分享
7 | 返回
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
21 |
22 |
29 |
30 |
35 |
40 |
54 |
55 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.1'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Mar 01 22:26:32 CST 2017
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-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/img/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSnowStack/BehaviorDemo/e9385f18db9d94753b4dc1fab2c137fbe97b6fab/img/preview.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------