8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidSweetBehavior
2 | Android support Behavior 实践
3 | ###实践1:
4 |
5 |
6 | 
7 |
8 |
9 | 通过自定义Behavior 和 Nest 事件,达到 instagram 选择照片的的效果.
10 | 使用方法:http://www.jianshu.com/p/5081c6a0113b
11 |
12 | 存在的问题:
13 | 里面有一块使用了setPadding的方法.导致有点顿,后期使用自定LayoutManager 来实现;
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ##实践2
23 | 
24 | SheetBehavior :扩展自BottomSheetBehavior ,可以下拉的SheetBehavior
25 | 使用方法:http://www.jianshu.com/p/d338695d4fd8
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app-1.1.2.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app-1.1.2.apk
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
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 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.0"
6 |
7 | defaultConfig {
8 | applicationId "com.mingle.androidsweetbehavior"
9 | minSdkVersion 14
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
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 'com.github.zzz40500:ScaleLayout:0.1'
24 | compile 'com.android.support:appcompat-v7:23.2.0'
25 | compile 'com.android.support:recyclerview-v7:23.2.0'
26 | compile 'com.github.bumptech.glide:glide:3.5.2'
27 | compile project(':sweetbehavior')
28 | }
29 |
--------------------------------------------------------------------------------
/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 /Users/zzz40500/Documents/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/androidTest/java/com/mingle/androidsweetbehavior/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.mingle.androidsweetbehavior;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/androidsweetbehavior/InstagramActivity.java:
--------------------------------------------------------------------------------
1 | package com.mingle.androidsweetbehavior;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.support.design.widget.AppBarLayout;
6 | import android.support.design.widget.CoordinatorLayout;
7 | import android.support.design.widget.InAppBarBehavior;
8 | import android.support.design.widget.InNestChildBehavior;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.os.Bundle;
11 | import android.support.v7.widget.GridLayoutManager;
12 | import android.support.v7.widget.RecyclerView;
13 | import android.view.Menu;
14 | import android.view.MenuItem;
15 | import android.view.View;
16 | import android.view.WindowManager;
17 | import android.widget.ImageView;
18 | import android.widget.Toast;
19 |
20 | import com.bumptech.glide.Glide;
21 | import com.mingle.androidsweetbehavior.adapter.ImageRVAdapter;
22 | import com.mingle.androidsweetbehavior.entity.ImageEntity;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 | import java.util.Random;
27 |
28 | public class InstagramActivity extends AppCompatActivity implements View.OnClickListener, ImageRVAdapter.OnRvItemClickListener {
29 |
30 |
31 | private RecyclerView mRV;
32 |
33 | private ImageView mContentIv;
34 |
35 | private AppBarLayout mAppBarLayout;
36 | private CoordinatorLayout mCoordinatorLayout;
37 | private GridLayoutManager gridLayoutManager;
38 | private int totalScroll;
39 |
40 | public static void startActivity(Context ctx) {
41 | ctx.startActivity(new Intent(ctx, InstagramActivity.class));
42 | }
43 |
44 | private int[] randomIntArray = new int[]{R.mipmap.ic_01, R.mipmap.ic_03,
45 |
46 | R.mipmap.ic_06, R.mipmap.ic_7,
47 | R.mipmap.ic_08, R.mipmap.ic_10,
48 | R.mipmap.ic_11, R.mipmap.ic_01,
49 |
50 | };
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
55 | super.onCreate(savedInstanceState);
56 | setContentView(R.layout.activity_instagram);
57 | mRV = (RecyclerView) findViewById(R.id.rv);
58 | mAppBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
59 | mContentIv = (ImageView) findViewById(R.id.contentIv);
60 | mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorLY);
61 | mContentIv.setOnClickListener(this);
62 |
63 |
64 | List list = new ArrayList<>();
65 | list.add(new ImageEntity(randomIntArray[0]));
66 | for (int i = 0; i < 78; i++) {
67 | ImageEntity item = new ImageEntity();
68 | item.resId = randomIntArray[new Random().nextInt(8)];
69 | list.add(item);
70 | }
71 |
72 | gridLayoutManager = new GridLayoutManager(this, 4, GridLayoutManager.VERTICAL, false);
73 |
74 | mRV.setLayoutManager(gridLayoutManager);
75 |
76 |
77 | mRV.setAdapter(new ImageRVAdapter(list, this));
78 | }
79 |
80 | @Override
81 | public boolean onCreateOptionsMenu(Menu menu) {
82 | // Inflate the menu; this adds items to the action bar if it is present.
83 | getMenuInflater().inflate(R.menu.menu_instagram, menu);
84 | return true;
85 | }
86 |
87 | @Override
88 | public boolean onOptionsItemSelected(MenuItem item) {
89 | int id = item.getItemId();
90 | if (id == R.id.action_settings) {
91 | return true;
92 | }
93 |
94 | return super.onOptionsItemSelected(item);
95 | }
96 |
97 | @Override
98 | public void onClick(View v) {
99 | Toast.makeText(this, "小样,点击了图片", Toast.LENGTH_LONG).show();
100 |
101 | }
102 |
103 | @Override
104 | public void onRvItemClick(View view, Object o, int position) {
105 |
106 | ImageEntity imageEntity = (ImageEntity) o;
107 | Glide.with(this).load(imageEntity.resId).into(mContentIv);
108 | InAppBarBehavior appBarBehavior = (InAppBarBehavior) ((CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams()).getBehavior();
109 | appBarBehavior.setExpanded(true, true);
110 |
111 | InNestChildBehavior inNestChildBehavior= (InNestChildBehavior) ((CoordinatorLayout.LayoutParams)mRV.getLayoutParams()).getBehavior();
112 | inNestChildBehavior.smoothScrollToView(view, mRV);
113 | }
114 |
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/androidsweetbehavior/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.mingle.androidsweetbehavior;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.view.Menu;
8 | import android.view.MenuItem;
9 | import android.view.View;
10 | import android.widget.Toast;
11 |
12 | public class MainActivity extends AppCompatActivity {
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_main);
18 | }
19 |
20 | @Override
21 | public boolean onCreateOptionsMenu(Menu menu) {
22 | getMenuInflater().inflate(R.menu.menu_main, menu);
23 | return true;
24 | }
25 |
26 | @Override
27 | public boolean onOptionsItemSelected(MenuItem item) {
28 | // Handle action bar item clicks here. The action bar will
29 | // automatically handle clicks on the Home/Up button, so long
30 | // as you specify a parent activity in AndroidManifest.xml.
31 | int id = item.getItemId();
32 |
33 | //noinspection SimplifiableIfStatement
34 | if (id == R.id.action_github) {
35 | Uri uri = Uri.parse("https://github.com/zzz40500/AndroidSweetBehavior");
36 | startActivity(new Intent(Intent.ACTION_VIEW,uri));
37 | return true;
38 | }
39 |
40 | return super.onOptionsItemSelected(item);
41 | }
42 |
43 | public void instagramAction(View view) {
44 |
45 | InstagramActivity.startActivity(this);
46 | }
47 |
48 | public void studyNotesAction(View view) {
49 |
50 | WebActivity.startActivity(this,"http://www.jianshu.com/p/99adaad8d55c");
51 | }
52 |
53 | public void secondClick(View view) {
54 | SecondActivity.startActivity(this);
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/androidsweetbehavior/SecondActivity.java:
--------------------------------------------------------------------------------
1 | package com.mingle.androidsweetbehavior;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.support.annotation.NonNull;
6 | import android.support.design.widget.SheetBehavior;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 | import android.view.View;
13 |
14 | public class SecondActivity extends AppCompatActivity {
15 |
16 | private SheetBehavior mSheetBehavior;
17 |
18 | public static void startActivity(Context ctx) {
19 | ctx.startActivity(new Intent(ctx, SecondActivity.class));
20 | }
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_second);
25 | View view=findViewById(R.id.design_bottom_sheet);
26 |
27 |
28 | mSheetBehavior =SheetBehavior.from(view);
29 | // mSheetBehavior.setHideable(true);
30 | mSheetBehavior.setBottomSheetCallback(new SheetBehavior.SheetCallback() {
31 | @Override
32 | public void onStateChanged(@NonNull View bottomSheet, @SheetBehavior.State int newState) {
33 |
34 | }
35 |
36 | @Override
37 | public void onSlide(@NonNull View bottomSheet, float slideOffset) {
38 |
39 | Log.e("dim",slideOffset+"");
40 | }
41 | });
42 |
43 | }
44 | @Override
45 | public boolean onCreateOptionsMenu(Menu menu) {
46 | getMenuInflater().inflate(R.menu.menu_second, menu);
47 | return true;
48 | }
49 |
50 | @Override
51 | public boolean onOptionsItemSelected(MenuItem item) {
52 | // Handle action bar item clicks here. The action bar will
53 | // automatically handle clicks on the Home/Up button, so long
54 | // as you specify a parent activity in AndroidManifest.xml.
55 | int id = item.getItemId();
56 |
57 | //noinspection SimplifiableIfStatement
58 | if (id == R.id.action_switch) {
59 | if( mSheetBehavior.getSlideModel()== SheetBehavior.TOP_SHEET){
60 | mSheetBehavior.setSlideModel(SheetBehavior.BOTTOM_SHEET);
61 | }else {
62 | mSheetBehavior.setSlideModel(SheetBehavior.TOP_SHEET);
63 | }
64 | return true;
65 | }
66 |
67 | return super.onOptionsItemSelected(item);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/androidsweetbehavior/WebActivity.java:
--------------------------------------------------------------------------------
1 | package com.mingle.androidsweetbehavior;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.os.Bundle;
8 | import android.webkit.WebChromeClient;
9 | import android.webkit.WebResourceError;
10 | import android.webkit.WebResourceRequest;
11 | import android.webkit.WebView;
12 | import android.webkit.WebViewClient;
13 |
14 | import com.mingle.widget.LoadingToolBar;
15 |
16 | /**
17 | * 内部网页加载器
18 | */
19 | public class WebActivity extends AppCompatActivity {
20 |
21 |
22 |
23 |
24 |
25 | public static final String KEY_URL="key_url";
26 |
27 | private WebView mWebView;
28 | private String mUrl;
29 |
30 | private LoadingToolBar mLoadingToolBar;
31 |
32 | public static void startActivity(Context ctx, String url){
33 | Intent intent=new Intent(ctx,WebActivity.class);
34 | intent.putExtra(KEY_URL, url);
35 | ctx.startActivity(intent);
36 | }
37 |
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_webctivity);
43 |
44 | if(getIntent().hasExtra(KEY_URL)){
45 | handIntentExtra();
46 | }
47 | initView();
48 |
49 |
50 | }
51 |
52 | private void initView() {
53 | mWebView= (WebView) findViewById(R.id.webView);
54 | mWebView.setWebChromeClient(new WebChromeClient());
55 | mLoadingToolBar= (LoadingToolBar) findViewById(R.id.toolbar);
56 | mLoadingToolBar.setColorScheme(R.color.triangle, R.color.rect, R.color.circle, R.color.rect);
57 |
58 | setSupportActionBar(mLoadingToolBar);
59 | mWebView.setWebViewClient(new WebViewClient() {
60 | @Override
61 | public void onPageStarted(WebView view, String url, Bitmap favicon) {
62 | super.onPageStarted(view, url, favicon);
63 | mLoadingToolBar.setRefreshing(true);
64 | }
65 |
66 | @Override
67 | public void onPageFinished(WebView view, String url) {
68 | super.onPageFinished(view, url);
69 | mLoadingToolBar.setRefreshing(false);
70 |
71 | }
72 |
73 | @Override
74 | public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
75 | super.onReceivedError(view, request, error);
76 | }
77 | });
78 | mWebView.loadUrl(mUrl);
79 |
80 | }
81 |
82 | private void handIntentExtra() {
83 |
84 | mUrl=getIntent().getStringExtra(KEY_URL);
85 |
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/androidsweetbehavior/adapter/ImageRVAdapter.java:
--------------------------------------------------------------------------------
1 | package com.mingle.androidsweetbehavior.adapter;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.bumptech.glide.Glide;
11 | import com.mingle.androidsweetbehavior.R;
12 | import com.mingle.androidsweetbehavior.entity.ImageEntity;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Created by zzz40500 on 15/11/16.
18 | */
19 | public class ImageRVAdapter extends RecyclerView.Adapter {
20 |
21 |
22 | private OnRvItemClickListener mOnRvItemClickListener;
23 |
24 | private List mData;
25 |
26 | public ImageRVAdapter(@NonNull List data,OnRvItemClickListener onRvItemClickListener) {
27 | mData=data;
28 | mOnRvItemClickListener=onRvItemClickListener;
29 | }
30 |
31 |
32 |
33 | @Override
34 | public VH onCreateViewHolder(ViewGroup parent, int viewType) {
35 | return new VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image,parent,false));
36 | }
37 |
38 | @Override
39 | public void onBindViewHolder(VH holder, final int position) {
40 | Glide.with(holder.itemView.getContext()).load(mData.get(position).resId).into(holder.mIv);
41 | holder.itemView.setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View v) {
44 | mOnRvItemClickListener.onRvItemClick(v,mData.get(position),position);
45 | }
46 | });
47 | }
48 |
49 | @Override
50 | public int getItemCount() {
51 | return mData.size();
52 | }
53 |
54 |
55 | public interface OnRvItemClickListener {
56 | void onRvItemClick(View view,Object o,int position);
57 | }
58 | public static class VH extends RecyclerView.ViewHolder {
59 |
60 | ImageView mIv;
61 | public VH(View itemView) {
62 | super(itemView);
63 | mIv= (ImageView) itemView.findViewById(R.id.imageIv);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/androidsweetbehavior/entity/ImageEntity.java:
--------------------------------------------------------------------------------
1 | package com.mingle.androidsweetbehavior.entity;
2 |
3 | /**
4 | * 类:
5 | * Created by zwm on 15/11/16.
6 | */
7 | public class ImageEntity {
8 |
9 | public int resId;
10 |
11 | public ImageEntity(int resId) {
12 | this.resId = resId;
13 | }
14 |
15 | public ImageEntity() {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/widget/BakedBezierInterpolator.java:
--------------------------------------------------------------------------------
1 | package com.mingle.widget;
2 |
3 | import android.view.animation.Interpolator;
4 |
5 | /**
6 | * A pre-baked bezier-curved interpolator for indeterminate progress animations.
7 | */
8 | final class BakedBezierInterpolator implements Interpolator {
9 | private static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
10 |
11 | public final static BakedBezierInterpolator getInstance() {
12 | return INSTANCE;
13 | }
14 |
15 | /**
16 | * Use getInstance instead of instantiating.
17 | */
18 | private BakedBezierInterpolator() {
19 | super();
20 | }
21 |
22 | /**
23 | * Lookup table values.
24 | * Generated using a Bezier curve from (0,0) to (1,1) with control points:
25 | * P0 (0,0)
26 | * P1 (0.4, 0)
27 | * P2 (0.2, 1.0)
28 | * P3 (1.0, 1.0)
29 | *
30 | * Values sampled with x at regular intervals between 0 and 1.
31 | */
32 | private static final float[] VALUES = new float[] {
33 | 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
34 | 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
35 | 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
36 | 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
37 | 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
38 | 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
39 | 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
40 | 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
41 | 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
42 | 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
43 | };
44 |
45 | private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
46 |
47 | @Override
48 | public float getInterpolation(float input) {
49 | if (input >= 1.0f) {
50 | return 1.0f;
51 | }
52 |
53 | if (input <= 0f) {
54 | return 0f;
55 | }
56 |
57 | int position = Math.min(
58 | (int)(input * (VALUES.length - 1)),
59 | VALUES.length - 2);
60 |
61 | float quantized = position * STEP_SIZE;
62 | float difference = input - quantized;
63 | float weight = difference / STEP_SIZE;
64 |
65 | return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/widget/LoadingToolBar.java:
--------------------------------------------------------------------------------
1 | package com.mingle.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.graphics.Canvas;
6 | import android.support.v7.widget.Toolbar;
7 | import android.util.AttributeSet;
8 | import android.util.DisplayMetrics;
9 |
10 | /**
11 | * Created by zzz40500 on 15/9/21.
12 | */
13 | public class LoadingToolBar extends Toolbar {
14 |
15 |
16 | private SwipeProgressBar mProgressBar;
17 | private int mProgressBarHeight;
18 | private static final float PROGRESS_BAR_HEIGHT = 4;
19 | private boolean mRefreshing;
20 | private PopupIndicator mPopupIndicator;
21 |
22 |
23 | public LoadingToolBar(Context context) {
24 | super(context);
25 | init();
26 | }
27 |
28 |
29 |
30 | public LoadingToolBar(Context context, AttributeSet attrs) {
31 | super(context, attrs);
32 | init();
33 | }
34 |
35 | public LoadingToolBar(Context context, AttributeSet attrs, int defStyleAttr) {
36 | super(context, attrs, defStyleAttr);
37 | init();
38 | }
39 |
40 | private void init() {
41 | setWillNotDraw(false);
42 | //progressbar 生成
43 | mProgressBar = new SwipeProgressBar(this);
44 | final DisplayMetrics metrics = getResources().getDisplayMetrics();
45 |
46 | //bar 的高度
47 | mProgressBarHeight = (int) (metrics.density * PROGRESS_BAR_HEIGHT);
48 | mPopupIndicator=new PopupIndicator(getContext());
49 | }
50 |
51 |
52 |
53 | /**
54 | * Notify the widget that refresh state has changed. Do not call this when
55 | * refresh is triggered by a swipe gesture.
56 | *
57 | * @param refreshing Whether or not the view should show refresh progress.
58 | */
59 | public void setRefreshing(boolean refreshing) {
60 | if (mRefreshing != refreshing) {
61 | mRefreshing = refreshing;
62 | if (mRefreshing) {
63 | mPopupIndicator.showIndicator(this);
64 | // mProgressBar.start();
65 | } else {
66 | mPopupIndicator.dismissIndicator();
67 | // mProgressBar.stop();
68 | }
69 | }
70 | }
71 |
72 |
73 | /**
74 | * Set the four colors used in the progress animation. The first color will
75 | * also be the color of the bar that grows in response to a user swipe
76 | * gesture.
77 | *
78 | * @param colorRes1 Color resource.
79 | * @param colorRes2 Color resource.
80 | * @param colorRes3 Color resource.
81 | * @param colorRes4 Color resource.
82 | */
83 | public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) {
84 | final Resources res = getResources();
85 | final int color1 = res.getColor(colorRes1);
86 | final int color2 = res.getColor(colorRes2);
87 | final int color3 = res.getColor(colorRes3);
88 | final int color4 = res.getColor(colorRes4);
89 | mProgressBar.setColorScheme(color1, color2, color3, color4);
90 | mPopupIndicator.setColorScheme(color1, color2, color3, color4);
91 | }
92 | public void draw(Canvas canvas) {
93 | super.draw(canvas);
94 | mProgressBar.draw(canvas);
95 | }
96 | @Override
97 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
98 | final int width = getMeasuredWidth();
99 | final int height = getMeasuredHeight();
100 | mProgressBar.setBounds(0, height-mProgressBarHeight, width, height);
101 | super.onLayout(changed,left,top,right,bottom);
102 | }
103 |
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/widget/PopupIndicator.java:
--------------------------------------------------------------------------------
1 | package com.mingle.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.PixelFormat;
5 | import android.graphics.Point;
6 | import android.os.IBinder;
7 | import android.support.v4.view.GravityCompat;
8 | import android.util.DisplayMetrics;
9 | import android.view.Gravity;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.view.WindowManager;
13 |
14 | /**
15 | * Created by zzz40500 on 15/9/25.
16 | */
17 | public class PopupIndicator implements SwipeProgressBar.AnimListener {
18 |
19 |
20 |
21 | private final WindowManager mWindowManager;
22 | private boolean mShowing;
23 | Point screenSize = new Point();
24 | private int[] mDrawingLocation = new int[2];
25 |
26 | private SwipeProgressView mSwipeProgressView;
27 |
28 |
29 |
30 | public PopupIndicator(Context context) {
31 | mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
32 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
33 | screenSize.set(displayMetrics.widthPixels, displayMetrics.heightPixels);
34 | mSwipeProgressView=new SwipeProgressView(context);
35 | mSwipeProgressView.setOnAnimListener(this);
36 | }
37 | public boolean isShowing() {
38 | return mShowing;
39 | }
40 |
41 |
42 | public void showIndicator(View parent) {
43 | if (isShowing()) {
44 | // mPopupView.mMarker.animateOpen();
45 | return;
46 | }
47 |
48 | IBinder windowToken = parent.getWindowToken();
49 |
50 | if (windowToken != null) {
51 | WindowManager.LayoutParams p = createPopupLayout(windowToken);
52 |
53 | p.gravity = Gravity.TOP | GravityCompat.START;
54 | updateLayoutParamsForPosiion(parent, p);
55 | mShowing = true;
56 | invokePopup(p);
57 | }
58 |
59 | }
60 |
61 | public void dismissIndicator(){
62 |
63 | mSwipeProgressView.
64 | setRefreshing(false);
65 |
66 | }
67 |
68 |
69 | private void measureFloater() {
70 | int specWidth = View.MeasureSpec.makeMeasureSpec(screenSize.x, View.MeasureSpec.EXACTLY);
71 | int specHeight = View.MeasureSpec.makeMeasureSpec(screenSize.y, View.MeasureSpec.AT_MOST);
72 | mSwipeProgressView.measure(specWidth, specHeight);
73 | }
74 |
75 | private void invokePopup(WindowManager.LayoutParams p) {
76 | mWindowManager.addView(mSwipeProgressView, p);
77 | mSwipeProgressView.
78 | setRefreshing(true);
79 | }
80 | private void updateLayoutParamsForPosiion(View anchor, WindowManager.LayoutParams p) {
81 | // measureFloater();
82 | int measuredHeight = mSwipeProgressView.getMeasuredHeight();
83 | anchor.getLocationInWindow(mDrawingLocation);
84 | p.x = 0;
85 | p.y = mDrawingLocation[1]+anchor.getMeasuredHeight() ;
86 | p.width = screenSize.x;
87 | p.height = 12;
88 |
89 |
90 | }
91 |
92 |
93 | private WindowManager.LayoutParams createPopupLayout(IBinder token) {
94 | WindowManager.LayoutParams p = new WindowManager.LayoutParams();
95 | p.gravity = Gravity.START | Gravity.TOP;
96 | p.width = ViewGroup.LayoutParams.MATCH_PARENT;
97 | p.height = ViewGroup.LayoutParams.MATCH_PARENT;
98 | p.format = PixelFormat.TRANSLUCENT;
99 | p.flags = computeFlags(p.flags);
100 | p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
101 | p.token = token;
102 | p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
103 | return p;
104 | }
105 |
106 | /**
107 | * I'm NOT completely sure how all this bitwise things work...
108 | *
109 | * @param curFlags
110 | * @return
111 | */
112 | private int computeFlags(int curFlags) {
113 | curFlags &= ~(
114 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
115 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
116 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
117 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
118 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
119 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
120 | curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
121 | curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
122 | curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
123 | curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
124 | return curFlags;
125 | }
126 |
127 |
128 | @Override
129 | public void onStart() {
130 |
131 | }
132 |
133 | @Override
134 | public void onStop() {
135 | mShowing = false;
136 | if(mSwipeProgressView.getParent()!= null) {
137 | mWindowManager.removeView(mSwipeProgressView);
138 | }
139 | }
140 |
141 | void setColorScheme(int color1, int color2, int color3, int color4) {
142 | mSwipeProgressView.setColorScheme(color1, color2, color3, color4);
143 |
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/widget/SwipeProgressBar.java:
--------------------------------------------------------------------------------
1 | package com.mingle.widget;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.graphics.RectF;
8 | import android.support.v4.view.ViewCompat;
9 | import android.view.View;
10 | import android.view.animation.AnimationUtils;
11 | import android.view.animation.Interpolator;
12 |
13 |
14 | /**
15 | * Custom progress bar that shows a cycle of colors as widening circles that
16 | * overdraw each other. When finished, the bar is cleared from the inside out as
17 | * the main cycle continues. Before running, this can also indicate how close
18 | * the user is to triggering something (e.g. how far they need to pull down to
19 | * trigger a refresh).
20 | */
21 | public class SwipeProgressBar {
22 |
23 | // Default progress animation colors are grays.
24 | private final static int COLOR1 = 0xB3000000;
25 | private final static int COLOR2 = 0x80000000;
26 | private final static int COLOR3 = 0x4d000000;
27 | private final static int COLOR4 = 0x1a000000;
28 |
29 | // The duration of the animation cycle.
30 | private static final int ANIMATION_DURATION_MS = 2000;
31 |
32 | // The duration of the animation to clear the bar.
33 | private static final int FINISH_ANIMATION_DURATION_MS = 1000;
34 |
35 | // Interpolator for varying the speed of the animation.
36 | private static final Interpolator INTERPOLATOR = BakedBezierInterpolator.getInstance();
37 |
38 | private final Paint mPaint = new Paint();
39 | private final RectF mClipRect = new RectF();
40 | private float mTriggerPercentage;
41 | private long mStartTime;
42 | private long mFinishTime;
43 | private boolean mRunning;
44 | private AnimListener mAnimListener;
45 |
46 | // Colors used when rendering the animation,
47 | private int mColor1;
48 | private int mColor2;
49 | private int mColor3;
50 | private int mColor4;
51 | private View mParent;
52 |
53 | private Rect mBounds = new Rect();
54 |
55 | public SwipeProgressBar(View parent) {
56 | mParent = parent;
57 | mColor1 = COLOR1;
58 | mColor2 = COLOR2;
59 | mColor3 = COLOR3;
60 | mColor4 = COLOR4;
61 | }
62 |
63 | /**
64 | * Set the four colors used in the progress animation. The first color will
65 | * also be the color of the bar that grows in response to a user swipe
66 | * gesture.
67 | *
68 | * @param color1 Integer representation of a color.
69 | * @param color2 Integer representation of a color.
70 | * @param color3 Integer representation of a color.
71 | * @param color4 Integer representation of a color.
72 | */
73 | void setColorScheme(int color1, int color2, int color3, int color4) {
74 | mColor1 = color1;
75 | mColor2 = color2;
76 | mColor3 = color3;
77 | mColor4 = color4;
78 | }
79 |
80 | /**
81 | * Update the progress the user has made toward triggering the swipe
82 | * gesture. and use this value to update the percentage of the trigger that
83 | * is shown.
84 | */
85 | void setTriggerPercentage(float triggerPercentage) {
86 | mTriggerPercentage = triggerPercentage;
87 | mStartTime = 0;
88 | ViewCompat.postInvalidateOnAnimation(mParent);
89 | }
90 |
91 | /**
92 | * Start showing the progress animation.
93 | */
94 | void start() {
95 | if (!mRunning) {
96 | mTriggerPercentage = 0;
97 | mStartTime = AnimationUtils.currentAnimationTimeMillis();
98 | mRunning = true;
99 | if(mAnimListener != null){
100 | mAnimListener.onStart();
101 | }
102 | mParent.postInvalidate();
103 | }
104 | }
105 |
106 | /**
107 | * Stop showing the progress animation.
108 | */
109 | void stop() {
110 | if (mRunning) {
111 | mTriggerPercentage = 0;
112 | mFinishTime = AnimationUtils.currentAnimationTimeMillis();
113 | mRunning = false;
114 | mParent.postInvalidate();
115 | }
116 | }
117 |
118 | /**
119 | * @return Return whether the progress animation is currently running.
120 | */
121 | boolean isRunning() {
122 | return mRunning || mFinishTime > 0;
123 | }
124 |
125 | void draw(Canvas canvas) {
126 | final int width = mBounds.width();
127 | final int height = mBounds.height();
128 | final int cx = width / 2;
129 | final int cy = mBounds.top+height / 2;
130 | boolean drawTriggerWhileFinishing = false;
131 | int restoreCount = canvas.save();
132 | canvas.clipRect(mBounds);
133 |
134 | if (mRunning || (mFinishTime > 0)) {
135 | long now = AnimationUtils.currentAnimationTimeMillis();
136 | long elapsed = (now - mStartTime) % ANIMATION_DURATION_MS;
137 | long iterations = (now - mStartTime) / ANIMATION_DURATION_MS;
138 | float rawProgress = (elapsed / (ANIMATION_DURATION_MS / 100f));
139 |
140 | // If we're not running anymore, that means we're running through
141 | // the finish animation.
142 | if (!mRunning) {
143 | // If the finish animation is done, don't draw anything, and
144 | // don't repost.
145 | if ((now - mFinishTime) >= FINISH_ANIMATION_DURATION_MS) {
146 | mFinishTime = 0;
147 | if(mAnimListener != null){
148 | mAnimListener.onStop();
149 | }
150 | return;
151 | }
152 |
153 | // Otherwise, use a 0 opacity alpha layer to clear the animation
154 | // from the inside out. This layer will prevent the circles from
155 | // drawing within its bounds.
156 | long finishElapsed = (now - mFinishTime) % FINISH_ANIMATION_DURATION_MS;
157 | float finishProgress = (finishElapsed / (FINISH_ANIMATION_DURATION_MS / 100f));
158 | float pct = (finishProgress / 100f);
159 | // Radius of the circle is half of the screen.
160 | float clearRadius = width / 2 * INTERPOLATOR.getInterpolation(pct);
161 | mClipRect.set(cx - clearRadius, mBounds.top, cx + clearRadius, mBounds.bottom);
162 | canvas.saveLayerAlpha(mClipRect, 0, 0);
163 |
164 | // Only draw the trigger if there is a space in the center of
165 | // this refreshing view that needs to be filled in by the
166 | // trigger. If the progress view is just still animating, let it
167 | // continue animating.
168 | drawTriggerWhileFinishing = true;
169 | }
170 |
171 | // First fill in with the last color that would have finished drawing.
172 | if (iterations == 0) {
173 | canvas.drawColor(mColor1);
174 | } else {
175 | if (rawProgress >= 0 && rawProgress < 25) {
176 | canvas.drawColor(mColor4);
177 | } else if (rawProgress >= 25 && rawProgress < 50) {
178 | canvas.drawColor(mColor1);
179 | } else if (rawProgress >= 50 && rawProgress < 75) {
180 | canvas.drawColor(mColor2);
181 | } else {
182 | canvas.drawColor(mColor3);
183 | }
184 | }
185 |
186 | // Then draw up to 4 overlapping concentric circles of varying radii, based on how far
187 | // along we are in the cycle.
188 | // progress 0-50 draw mColor2
189 | // progress 25-75 draw mColor3
190 | // progress 50-100 draw mColor4
191 | // progress 75 (wrap to 25) draw mColor1
192 | if ((rawProgress >= 0 && rawProgress <= 25)) {
193 | float pct = (((rawProgress + 25) * 2) / 100f);
194 | drawCircle(canvas, cx, cy, mColor1, pct);
195 | }
196 | if (rawProgress >= 0 && rawProgress <= 50) {
197 | float pct = ((rawProgress * 2) / 100f);
198 | drawCircle(canvas, cx, cy, mColor2, pct);
199 | }
200 | if (rawProgress >= 25 && rawProgress <= 75) {
201 | float pct = (((rawProgress - 25) * 2) / 100f);
202 | drawCircle(canvas, cx, cy, mColor3, pct);
203 | }
204 | if (rawProgress >= 50 && rawProgress <= 100) {
205 | float pct = (((rawProgress - 50) * 2) / 100f);
206 | drawCircle(canvas, cx, cy, mColor4, pct);
207 | }
208 | if ((rawProgress >= 75 && rawProgress <= 100)) {
209 | float pct = (((rawProgress - 75) * 2) / 100f);
210 | drawCircle(canvas, cx, cy, mColor1, pct);
211 | }
212 | if (mTriggerPercentage > 0 && drawTriggerWhileFinishing) {
213 | // There is some portion of trigger to draw. Restore the canvas,
214 | // then draw the trigger. Otherwise, the trigger does not appear
215 | // until after the bar has finished animating and appears to
216 | // just jump in at a larger width than expected.
217 | canvas.restoreToCount(restoreCount);
218 | restoreCount = canvas.save();
219 | canvas.clipRect(mBounds);
220 | drawTrigger(canvas, cx, cy);
221 | }
222 | // Keep running until we finish out the last cycle.
223 | ViewCompat.postInvalidateOnAnimation(mParent);
224 | } else {
225 | // Otherwise if we're in the middle of a trigger, draw that.
226 | if (mTriggerPercentage > 0
227 | && mTriggerPercentage <= 1.0
228 | ) {
229 | mPaint.setColor(mColor1);
230 | drawTrigger(canvas, cx, cy);
231 | } else if (mTriggerPercentage > 1) {
232 |
233 | mPaint.setColor(deepColor(mColor1));
234 | drawTrigger(canvas, cx, cy);
235 | }
236 | }
237 | canvas.restoreToCount(restoreCount);
238 | }
239 |
240 | private void drawTrigger(Canvas canvas, int cx, int cy) {
241 | mPaint.setColor(mColor1);
242 | canvas.drawCircle(cx, cy, cx * mTriggerPercentage, mPaint);
243 | }
244 |
245 | private int deepColor(int color) {
246 | int red = Color.red(color);
247 | int nextRed = (int) (red + 99 * (mTriggerPercentage - 1));
248 | if (nextRed > 0xff) {
249 | red = 0xff;
250 | } else {
251 | red = nextRed;
252 | }
253 | int green = Color.green(color);
254 | int nextGreen = (int) (green + 99 * (mTriggerPercentage - 1));
255 | if (nextRed > 0xff) {
256 | green = 0xff;
257 | } else {
258 | green = nextGreen;
259 | }
260 |
261 | int blue = Color.blue(color);
262 | int nextBlue = (int) (blue + 99 * (mTriggerPercentage - 1));
263 | if (nextRed > 0xff) {
264 | blue = 0xff;
265 | } else {
266 | blue = nextBlue;
267 | }
268 |
269 |
270 | return Color.argb(Color.alpha(color), red, green, blue);
271 | }
272 |
273 |
274 | /**
275 | * Draws a circle centered in the view.
276 | *
277 | * @param canvas the canvas to draw on
278 | * @param cx the center x coordinate
279 | * @param cy the center y coordinate
280 | * @param color the color to draw
281 | * @param pct the percentage of the view that the circle should cover
282 | */
283 | private void drawCircle(Canvas canvas, float cx, float cy, int color, float pct) {
284 | mPaint.setColor(color);
285 | canvas.save();
286 | canvas.translate(cx, cy);
287 | float radiusScale = INTERPOLATOR.getInterpolation(pct);
288 | canvas.scale(radiusScale, radiusScale);
289 | canvas.drawCircle(0, 0, cx, mPaint);
290 | canvas.restore();
291 | }
292 |
293 | /**
294 | * Set the drawing bounds of this SwipeProgressBar.
295 | */
296 | void setBounds(int left, int top, int right, int bottom) {
297 | mBounds.left = left;
298 | mBounds.top = top;
299 | mBounds.right = right;
300 | mBounds.bottom = bottom;
301 | }
302 |
303 |
304 | public void setOnAnimListener(AnimListener animListener) {
305 | mAnimListener = animListener;
306 | }
307 |
308 | public interface AnimListener{
309 | void onStart();
310 | void onStop();
311 | }
312 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/mingle/widget/SwipeProgressView.java:
--------------------------------------------------------------------------------
1 | package com.mingle.widget;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 |
10 | /**
11 | * Created by zzz40500 on 15/9/24.
12 | */
13 | public class SwipeProgressView extends View {
14 |
15 |
16 | private SwipeProgressBar mProgressBar;
17 | private boolean mRefreshing;
18 | public SwipeProgressView(Context context) {
19 | super(context);
20 | init();
21 | }
22 |
23 | private void init() {
24 | setWillNotDraw(false);
25 | mProgressBar = new SwipeProgressBar(this);
26 | }
27 |
28 | public SwipeProgressView(Context context, AttributeSet attrs) {
29 | super(context, attrs);
30 | init();
31 | }
32 |
33 | public SwipeProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
34 | super(context, attrs, defStyleAttr);
35 | init();
36 | }
37 |
38 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
39 | public SwipeProgressView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
40 | super(context, attrs, defStyleAttr, defStyleRes);
41 | init();
42 | }
43 |
44 |
45 | @Override
46 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
47 |
48 |
49 | MeasureSpec.getMode(heightMeasureSpec);
50 |
51 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
52 | }
53 |
54 | @Override
55 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
56 | super.onSizeChanged(w, h, oldw, oldh);
57 | mProgressBar.setBounds(0, 0, w, h);
58 | }
59 |
60 | public void draw(Canvas canvas) {
61 | super.draw(canvas);
62 | mProgressBar.draw(canvas);
63 | }
64 |
65 | /**
66 | * Notify the widget that refresh state has changed. Do not call this when
67 | * refresh is triggered by a swipe gesture.
68 | *
69 | * @param refreshing Whether or not the view should show refresh progress.
70 | */
71 | public void setRefreshing(boolean refreshing) {
72 | if (mRefreshing != refreshing) {
73 | mRefreshing = refreshing;
74 | if (mRefreshing) {
75 | mProgressBar.start();
76 | } else {
77 | mProgressBar.stop();
78 | }
79 | }
80 | }
81 |
82 | /**
83 | * Set the four colors used in the progress animation. The first color will
84 | * also be the color of the bar that grows in response to a user swipe
85 | * gesture.
86 | *
87 | * @param colorRes1 Color resource.
88 | * @param colorRes2 Color resource.
89 | * @param colorRes3 Color resource.
90 | * @param colorRes4 Color resource.
91 | */
92 | public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) {
93 | mProgressBar.setColorScheme(colorRes1, colorRes2, colorRes3, colorRes4);
94 | }
95 |
96 |
97 | public void setOnAnimListener(SwipeProgressBar.AnimListener animListener) {
98 | mProgressBar.setOnAnimListener(animListener);
99 | }
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_instagram.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
17 |
18 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
20 |
21 |
27 |
28 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_second.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
13 |
14 |
15 |
21 |
24 |
28 |
29 |
30 |
34 |
38 |
42 |
46 |
50 |
54 |
58 |
62 |
66 |
70 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_webctivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_instagram.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_second.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_01.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_03.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_06.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_08.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_09.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_10.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_11.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_7.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app/src/main/res/mipmap-xxhdpi/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 |
4 | #aa72d572
5 | #aa738ffe
6 | #aae84e40
7 | #00000000
8 | #25808080
9 | #f5f5f5
10 |
11 | #795548
12 | #5D4037
13 | #D7CCC8
14 | #795548
15 | #212121
16 | #727272
17 | #FFFFFF
18 | #B6B6B6
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidSweetBehavior
3 |
4 | Hello world!
5 | github
6 | Instagram
7 | switch
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app0.1.1.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/app0.1.1.apk
--------------------------------------------------------------------------------
/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.0.0-beta6'
9 |
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | jcenter()
16 |
17 | maven {
18 | url "https://jitpack.io"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zzz40500/AndroidSweetBehavior/bc5ffbf7c8186d2f2941227868e3f5ef64f9849a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Nov 16 04:29:49 CST 2015
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-2.10-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 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':sweetbehavior'
2 |
--------------------------------------------------------------------------------
/sweetbehavior/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sweetbehavior/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile 'com.android.support:design:23.2.0'
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/sweetbehavior/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 /Users/zzz40500/Documents/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 |
--------------------------------------------------------------------------------
/sweetbehavior/src/androidTest/java/com/mingle/sweetbehavior/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.mingle.sweetbehavior;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/sweetbehavior/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sweetbehavior/src/main/java/android/support/design/widget/InAppBarBehavior.java:
--------------------------------------------------------------------------------
1 | package android.support.design.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 |
8 | /**
9 | * Created by zzz40500 on 15/11/14.
10 | */
11 | public class InAppBarBehavior extends AppBarLayout.Behavior {
12 |
13 | private static final String TAG = "InAppBarBehavior";
14 |
15 | private boolean mIsNested = false;
16 |
17 | private boolean isExpand = false;
18 |
19 | private float mStartY;
20 |
21 | public InAppBarBehavior(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 |
26 | @Override
27 | public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
28 | final int x = (int) ev.getX();
29 | final int y = (int) ev.getY();
30 | switch (ev.getAction()) {
31 | case MotionEvent.ACTION_DOWN:
32 | if (getTopAndBottomOffset() == 0) {
33 | isExpand = true;
34 | } else {
35 | isExpand = false;
36 | }
37 | mStartY = ev.getRawY();
38 | if (!parent.isPointInChildBounds(child, x, y)) {
39 | mIsNested = false;
40 | }
41 | break;
42 | case MotionEvent.ACTION_MOVE:
43 | if (parent.isPointInChildBounds(child, x, y) || ev.getY() - mStartY > 0) {
44 | mIsNested = true;
45 | }
46 | }
47 | return super.onInterceptTouchEvent(parent, child, ev);
48 |
49 | }
50 |
51 |
52 | @Override
53 | public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
54 | switch (ev.getAction()) {
55 | case MotionEvent.ACTION_UP:
56 | snapScrollTo(parent, child);
57 | return true;
58 | }
59 | return super.onTouchEvent(parent, child, ev);
60 | }
61 |
62 | @Override
63 | public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {
64 | if (isExpand) {
65 | return this.getTopAndBottomOffset() != 0;
66 |
67 | } else {
68 | return this.getTopAndBottomOffset() != getMaxDragOffset(child);
69 | }
70 | }
71 |
72 |
73 | public boolean isExpend() {
74 | return getTopAndBottomOffset() == 0;
75 | }
76 |
77 | @Override
78 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
79 |
80 | if (mIsNested) {
81 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
82 | }
83 | }
84 |
85 |
86 | @Override
87 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target) {
88 | snapScrollTo(coordinatorLayout, abl);
89 | }
90 |
91 |
92 | @Override
93 | public int getMaxDragOffset(AppBarLayout view) {
94 | return super.getMaxDragOffset(view);
95 | }
96 |
97 | public void snapScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, boolean isExpand) {
98 |
99 | int offset = 0;
100 | if (isExpand) {
101 | offset = 0;
102 | } else {
103 | offset = getMaxDragOffset(abl);
104 | }
105 | animateOffsetTo(coordinatorLayout, abl, offset);
106 | }
107 |
108 | private ValueAnimatorCompat mAnimator;
109 |
110 |
111 | private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
112 | final AppBarLayout child, int offset) {
113 | if (mAnimator == null) {
114 | mAnimator = ViewUtils.createAnimator();
115 | mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
116 | mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
117 | @Override
118 | public void onAnimationUpdate(ValueAnimatorCompat animator) {
119 |
120 | setHeaderTopBottomOffset(coordinatorLayout, child,
121 | animator.getAnimatedIntValue());
122 | }
123 | });
124 | } else {
125 | mAnimator.cancel();
126 | }
127 | mAnimator.setIntValues(getTopBottomOffsetForScrollingSibling(), offset);
128 | mAnimator.setDuration(300);
129 | mAnimator.start();
130 | }
131 |
132 |
133 | private void snapScrollTo(CoordinatorLayout coordinatorLayout, AppBarLayout abl) {
134 |
135 | int distance = -getMaxDragOffset(abl);
136 | int offset = -getTopAndBottomOffset();
137 | if (isExpand) {
138 | snapScroll(coordinatorLayout, abl, offset < distance / 12);
139 | } else {
140 | snapScroll(coordinatorLayout, abl, offset < distance * 11 / 12);
141 | }
142 | }
143 |
144 |
145 | @Override
146 | public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
147 |
148 | if (mIsNested) {
149 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
150 | }
151 |
152 | }
153 |
154 |
155 | @Override
156 | public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
157 |
158 | if (mIsNested) {
159 | return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
160 | }
161 | return false;
162 | }
163 |
164 | private CoordinatorLayout mCoordinatorLayout;
165 | private AppBarLayout mAppBarLayout;
166 |
167 | @Override
168 | public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) {
169 | mCoordinatorLayout = parent;
170 | mAppBarLayout = abl;
171 | return super.onLayoutChild(parent, abl, layoutDirection);
172 | }
173 |
174 | public void setExpanded(boolean expanded, boolean animate) {
175 |
176 | snapScroll(mCoordinatorLayout,mAppBarLayout,expanded);
177 | }
178 |
179 |
180 |
181 | @Override
182 | boolean canDragView(AppBarLayout view) {
183 | return true;
184 | }
185 |
186 | public void setIsNest(boolean isNest) {
187 | this.mIsNested = isNest;
188 | }
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/sweetbehavior/src/main/java/android/support/design/widget/InNestChildBehavior.java:
--------------------------------------------------------------------------------
1 | package android.support.design.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.support.v4.view.ViewCompat;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.MotionEvent;
10 | import android.view.View;
11 |
12 |
13 | /**
14 | * Created by zzz40500 on 15/11/14.
15 | */
16 | public class InNestChildBehavior extends AppBarLayout.ScrollingViewBehavior implements View.OnTouchListener {
17 |
18 | private static final String TAG = "InNestChildBehavior";
19 |
20 | private InAppBarBehavior mDependencyBehavior;
21 |
22 | private View mSelfView;
23 |
24 | private int mOffset;
25 |
26 |
27 | public InNestChildBehavior(Context context, AttributeSet attrs) {
28 | super(context, attrs);
29 | }
30 |
31 | @Override
32 | public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
33 |
34 | child.setOnTouchListener(this);
35 | mSelfView = child;
36 | return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
37 | }
38 |
39 |
40 | @Override
41 | public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
42 |
43 | if (dependency instanceof AppBarLayout) {
44 | CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) dependency.getLayoutParams();
45 | CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
46 | if (behavior instanceof InAppBarBehavior) {
47 | mDependencyBehavior = (InAppBarBehavior) behavior;
48 | }
49 | }
50 | return super.layoutDependsOn(parent, child, dependency);
51 | }
52 |
53 |
54 | /**
55 | * rv 可以设置最大的 paddingBottom
56 | */
57 | private int mMaxPadding;
58 |
59 |
60 | @Override
61 | public boolean onDependentViewChanged(final CoordinatorLayout parent, final View child, final View dependency) {
62 |
63 | final InAppBarBehavior behavior =
64 | (InAppBarBehavior) ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
65 | super.onDependentViewChanged(parent, child, dependency);
66 | if (mMaxPadding == 0)
67 | mMaxPadding = dependency.getMeasuredHeight() + mDependencyBehavior.getMaxDragOffset((AppBarLayout) dependency);
68 | mOffset = child.getHeight() - (parent.getHeight() - dependency.getBottom());
69 | mSelfView.setPadding(0, 0, 0, mOffset);
70 | // if (mOffset <= 0) {
71 | // mSelfView.setPadding(0, 0, 0, mOffset);
72 | // } else {
73 | // ViewCompat.postOnAnimation(mSelfView, new Runnable() {
74 | // @Override
75 | // public void run() {
76 | // mSelfView.removeCallbacks(mRunnable);
77 | // mSelfView.post(mRunnable);
78 | // mDependencyBehavior.setIsNest(false);
79 | // }
80 | // });
81 | // }
82 | return true;
83 | }
84 |
85 |
86 | public void smoothScrollToView(final View view, final RecyclerView rv) {
87 |
88 | if (rv != mSelfView) {
89 | return;
90 | }
91 |
92 | Rect childRect = new Rect();
93 | view.getGlobalVisibleRect(childRect);
94 | Rect rVRect = new Rect();
95 | rv.getGlobalVisibleRect(rVRect);
96 |
97 | final int scrollBy;
98 | if (childRect.top == rVRect.top) {
99 | scrollBy = childRect.bottom - view.getHeight() - rVRect.top;
100 | } else {
101 | scrollBy = childRect.top - rVRect.top;
102 | }
103 | if (scrollBy <= 0) {
104 | rv.smoothScrollBy(0, scrollBy);
105 | } else {
106 | if (mDependencyBehavior.isExpend()) {
107 | rv.smoothScrollBy(0, scrollBy);
108 | } else {
109 | rv.smoothScrollBy(0, scrollBy);
110 | rv.postDelayed(new Runnable() {
111 | @Override
112 | public void run() {
113 | Rect childRect = new Rect();
114 | view.getGlobalVisibleRect(childRect);
115 |
116 | Rect rVRect = new Rect();
117 | rv.getGlobalVisibleRect(rVRect);
118 |
119 | final int scrollBy;
120 | if (childRect.top == rVRect.top) {
121 | scrollBy = childRect.bottom - view.getHeight() - rVRect.top;
122 | } else {
123 | scrollBy = childRect.top - rVRect.top;
124 |
125 | }
126 |
127 | if (scrollBy != 0) {
128 | rv.smoothScrollBy(0, scrollBy);
129 | }
130 |
131 | }
132 | }, 300);
133 | }
134 | }
135 |
136 | }
137 |
138 |
139 | public Runnable mRunnable = new Runnable() {
140 | @Override
141 | public void run() {
142 | if (mSelfView.getPaddingBottom() != mOffset - mMaxPadding) {
143 | if (mSelfView.getPaddingBottom() >= mOffset - mMaxPadding) {
144 | mSelfView.setPadding(0, 0, 0,
145 | mSelfView.getPaddingBottom() + (mOffset - mMaxPadding - mSelfView.getPaddingBottom()) / 9
146 | );
147 | } else {
148 | mSelfView.setPadding(0, 0, 0, mOffset - mMaxPadding);
149 | }
150 | mSelfView.postDelayed(this, 10);
151 | }
152 | }
153 | };
154 |
155 |
156 | @Override
157 | public boolean onTouch(View v, MotionEvent ev) {
158 |
159 | switch (ev.getAction()) {
160 | case MotionEvent.ACTION_DOWN:
161 | if (mDependencyBehavior != null) {
162 | mDependencyBehavior.setIsNest(false);
163 | }
164 | break;
165 | case MotionEvent.ACTION_MOVE:
166 | if (ev.getY() <= 0) {
167 | if (mDependencyBehavior != null) {
168 | mDependencyBehavior.setIsNest(true);
169 | }
170 | }
171 | break;
172 | }
173 | return false;
174 | }
175 |
176 |
177 | @Override
178 | public boolean setTopAndBottomOffset(int offset) {
179 | // mOffset = offset;
180 | return super.setTopAndBottomOffset(offset);
181 | }
182 |
183 |
184 | }
--------------------------------------------------------------------------------
/sweetbehavior/src/main/java/android/support/design/widget/SheetBehavior.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.support.design.widget;
18 |
19 | import com.mingle.sweetbehavior.R;
20 |
21 | import android.content.Context;
22 | import android.content.res.TypedArray;
23 | import android.os.Build;
24 | import android.os.Parcel;
25 | import android.os.Parcelable;
26 | import android.support.annotation.IntDef;
27 | import android.support.annotation.NonNull;
28 | import android.support.v4.view.MotionEventCompat;
29 | import android.support.v4.view.NestedScrollingChild;
30 | import android.support.v4.view.VelocityTrackerCompat;
31 | import android.support.v4.view.ViewCompat;
32 | import android.support.v4.widget.ViewDragHelper;
33 | import android.util.AttributeSet;
34 | import android.view.MotionEvent;
35 | import android.view.VelocityTracker;
36 | import android.view.View;
37 | import android.view.ViewConfiguration;
38 | import android.view.ViewGroup;
39 | import android.view.ViewParent;
40 |
41 | import java.lang.annotation.Retention;
42 | import java.lang.annotation.RetentionPolicy;
43 | import java.lang.ref.WeakReference;
44 |
45 |
46 | /**
47 | * 在原本的基础上支持向下
48 | * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as
49 | * a bottom sheet.
50 | */
51 | public class SheetBehavior extends CoordinatorLayout.Behavior {
52 |
53 |
54 | /**
55 | * Callback for monitoring events about bottom sheets.
56 | */
57 | public abstract static class SheetCallback {
58 |
59 | /**
60 | * Called when the bottom sheet changes its state.
61 | *
62 | * @param bottomSheet The bottom sheet view.
63 | * @param newState The new state. This will be one of {@link #STATE_DRAGGING},
64 | * {@link #STATE_SETTLING}, {@link #STATE_EXPANDED},
65 | * {@link #STATE_COLLAPSED}, or {@link #STATE_HIDDEN}.
66 | */
67 | public abstract void onStateChanged(@NonNull View bottomSheet, @State int newState);
68 |
69 | /**
70 | * Called when the bottom sheet is being dragged.
71 | *
72 | * @param bottomSheet The bottom sheet view.
73 | * @param slideOffset The new offset of this bottom sheet within its range, from 0 to 1
74 | * when it is moving upward, and from 0 to -1 when it moving downward.
75 | */
76 | public abstract void onSlide(@NonNull View bottomSheet, float slideOffset);
77 | }
78 |
79 | /**
80 | * The bottom sheet is dragging.
81 | */
82 | public static final int STATE_DRAGGING = 1;
83 |
84 | /**
85 | * The bottom sheet is settling.
86 | */
87 | public static final int STATE_SETTLING = 2;
88 |
89 | /**
90 | * The bottom sheet is expanded.
91 | */
92 | public static final int STATE_EXPANDED = 3;
93 |
94 | /**
95 | * The bottom sheet is collapsed.
96 | */
97 | public static final int STATE_COLLAPSED = 4;
98 |
99 | /**
100 | * The bottom sheet is hidden.
101 | */
102 | public static final int STATE_HIDDEN = 5;
103 |
104 |
105 | public static final int TOP_SHEET = 1;
106 | public static final int BOTTOM_SHEET = 2;
107 |
108 |
109 | /**
110 | * @hide
111 | */
112 | @IntDef({TOP_SHEET, BOTTOM_SHEET})
113 | @Retention(RetentionPolicy.SOURCE)
114 | public @interface SlideMode {
115 | }
116 |
117 | /**
118 | * @hide
119 | */
120 | @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN})
121 | @Retention(RetentionPolicy.SOURCE)
122 | public @interface State {
123 | }
124 |
125 |
126 | private static final float HIDE_THRESHOLD = 0.5f;
127 |
128 | private static final float HIDE_FRICTION = 0.1f;
129 |
130 | private float mMaximumVelocity;
131 |
132 | private int mPeekHeight;
133 |
134 |
135 | private boolean mHideable;
136 |
137 | @State
138 | private int mState = STATE_COLLAPSED;
139 |
140 | private ViewDragHelper mViewDragHelper;
141 |
142 | private boolean mIgnoreEvents;
143 |
144 | private int mLastNestedScrollDy;
145 |
146 | private boolean mNestedScrolled;
147 |
148 | private int mParentHeight;
149 |
150 | private WeakReference mViewRef;
151 |
152 | private WeakReference mNestedScrollingChildRef;
153 |
154 | private SheetCallback mCallback;
155 |
156 | private VelocityTracker mVelocityTracker;
157 |
158 | private int mActivePointerId;
159 |
160 | private int mInitialY;
161 |
162 | private
163 | @SheetBehavior.SlideMode
164 | int mSlideModel = BOTTOM_SHEET;
165 |
166 | private SlideHelper mSlideHelper;
167 |
168 |
169 | private boolean mTouchingScrollingChild;
170 |
171 | /**
172 | * Default constructor for instantiating BottomSheetBehaviors.
173 | */
174 | public SheetBehavior() {
175 | }
176 |
177 | /**
178 | * Default constructor for inflating BottomSheetBehaviors from layout.
179 | *
180 | * @param context The {@link Context}.
181 | * @param attrs The {@link AttributeSet}.
182 | */
183 | public SheetBehavior(Context context, AttributeSet attrs) {
184 | super(context, attrs);
185 | TypedArray a = context.obtainStyledAttributes(attrs,
186 | R.styleable.SheetBehavior);
187 | setPeekHeight(a.getDimensionPixelSize(
188 | R.styleable.SheetBehavior_peekHeight, 0));
189 | setHideable(a.getBoolean(R.styleable.SheetBehavior_hiddenEnable, false));
190 | mSlideModel = a.getInt(R.styleable.SheetBehavior_slideMode, BOTTOM_SHEET);
191 | a.recycle();
192 | ViewConfiguration configuration = ViewConfiguration.get(context);
193 | mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
194 | }
195 |
196 | @Override
197 | public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
198 | return new SavedState(super.onSaveInstanceState(parent, child), mState);
199 | }
200 |
201 | @Override
202 | public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
203 | SavedState ss = (SavedState) state;
204 | super.onRestoreInstanceState(parent, child, ss.getSuperState());
205 | // Intermediate states are restored as collapsed state
206 | if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) {
207 | mState = STATE_COLLAPSED;
208 | } else {
209 | mState = ss.state;
210 | }
211 | }
212 |
213 | @Override
214 | public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
215 | // First let the parent lay it out
216 | if (mState != STATE_DRAGGING && mState != STATE_SETTLING) {
217 | parent.onLayoutChild(child, layoutDirection);
218 | }
219 | if (mSlideHelper == null) {
220 | mSlideHelper = createSlideHelper(mSlideModel);
221 | }
222 | if (mViewDragHelper == null) {
223 | mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
224 | }
225 |
226 | // Offset the bottom sheet
227 | mSlideHelper.onLayoutChild(child);
228 | mViewRef = new WeakReference<>(child);
229 | mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
230 | return true;
231 | }
232 |
233 | @Override
234 | public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
235 | if (!child.isShown()) {
236 | return false;
237 | }
238 | int action = MotionEventCompat.getActionMasked(event);
239 | // Record the velocity
240 | if (action == MotionEvent.ACTION_DOWN) {
241 | reset();
242 | }
243 | if (mVelocityTracker == null) {
244 | mVelocityTracker = VelocityTracker.obtain();
245 | }
246 | mVelocityTracker.addMovement(event);
247 | switch (action) {
248 | case MotionEvent.ACTION_UP:
249 | case MotionEvent.ACTION_CANCEL:
250 | mTouchingScrollingChild = false;
251 | mActivePointerId = MotionEvent.INVALID_POINTER_ID;
252 | // Reset the ignore flag
253 | if (mIgnoreEvents) {
254 | mIgnoreEvents = false;
255 | return false;
256 | }
257 | break;
258 | case MotionEvent.ACTION_DOWN:
259 | mInitialY = (int) event.getY();
260 | int initialX = (int) event.getX();
261 | View scroll = mNestedScrollingChildRef.get();
262 | if (scroll != null && parent.isPointInChildBounds(scroll, initialX, mInitialY)) {
263 | mActivePointerId = event.getPointerId(event.getActionIndex());
264 | mTouchingScrollingChild = true;
265 | }
266 | mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID &&
267 | !parent.isPointInChildBounds(child, initialX, mInitialY);
268 | break;
269 | }
270 | if (!mIgnoreEvents &&
271 | mViewDragHelper.shouldInterceptTouchEvent(event)) {
272 | return true;
273 | }
274 | // We have to handle cases that the ViewDragHelper does not capture the bottom sheet because
275 | // it is not the top most view of its parent. This is not necessary when the touch event is
276 | // happening over the scrolling content as nested scrolling logic handles that case.
277 | View scroll = mNestedScrollingChildRef.get();
278 | return action == MotionEvent.ACTION_MOVE
279 | &&
280 | scroll != null &&
281 | !mIgnoreEvents &&
282 | mState != STATE_DRAGGING &&
283 | !parent.isPointInChildBounds(scroll, (int) event.getX(), (int) event.getY()) &&
284 | Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop();
285 | }
286 |
287 | @Override
288 | public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
289 | if (!child.isShown()) {
290 | return false;
291 | }
292 | int action = MotionEventCompat.getActionMasked(event);
293 | if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) {
294 | return true;
295 | }
296 | mViewDragHelper.processTouchEvent(event);
297 | // Record the velocity
298 | if (action == MotionEvent.ACTION_DOWN) {
299 | reset();
300 | }
301 | if (mVelocityTracker == null) {
302 | mVelocityTracker = VelocityTracker.obtain();
303 | }
304 | mVelocityTracker.addMovement(event);
305 | // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
306 | // to capture the bottom sheet in case it is not captured and the touch slop is passed.
307 | if (action == MotionEvent.ACTION_MOVE) {
308 |
309 |
310 | if (mSlideHelper.canScrollHorizontally() != 0 && Math.abs(mInitialY - event.getX()) > mViewDragHelper.getTouchSlop()) {
311 | mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex()));
312 | } else if (mSlideHelper.canScrollVertically() != 0 && Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) {
313 | mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex()));
314 | }
315 | }
316 | return true;
317 | }
318 |
319 | @Override
320 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child,
321 | View directTargetChild, View target, int nestedScrollAxes) {
322 | mLastNestedScrollDy = 0;
323 |
324 | View scrollingChild = mNestedScrollingChildRef.get();
325 |
326 | return target == scrollingChild && mSlideHelper.canScrollVertically() != 0 && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
327 | }
328 |
329 | @Override
330 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
331 | int dx,
332 | int dy, int[] consumed) {
333 | mSlideHelper.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
334 | }
335 |
336 |
337 | @Override
338 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
339 | mSlideHelper.onStopNestedScroll(coordinatorLayout, child, target);
340 | }
341 |
342 | @Override
343 | public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
344 | float velocityX, float velocityY) {
345 | return (mState != STATE_EXPANDED ||
346 | super.onNestedPreFling(coordinatorLayout, child, target,
347 | velocityX, velocityY));
348 | }
349 |
350 | /**
351 | * Sets the height of the bottom sheet when it is collapsed.
352 | *
353 | * @param peekHeight The height of the collapsed bottom sheet in pixels.
354 | * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight
355 | */
356 |
357 | public final void setPeekHeight(int peekHeight) {
358 |
359 | mPeekHeight = peekHeight;
360 | if (mSlideHelper != null) {
361 | mSlideHelper.setPeekHeight(peekHeight);
362 | }
363 | }
364 |
365 | /**
366 | * Gets the height of the bottom sheet when it is collapsed.
367 | *
368 | * @return The height of the collapsed bottom sheet.
369 | * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight
370 | */
371 | public final int getPeekHeight() {
372 | return mPeekHeight;
373 | }
374 |
375 | /**
376 | * Sets whether this bottom sheet can hide when it is swiped down.
377 | *
378 | * @param hideable {@code true} to make this bottom sheet hideable.
379 | * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_hideable
380 | */
381 | public void setHideable(boolean hideable) {
382 | mHideable = hideable;
383 | }
384 |
385 | public void setSlideModel(int slideModel) {
386 | mSlideModel = slideModel;
387 | mSlideHelper = createSlideHelper(slideModel);
388 | if (mViewRef.get() != null) {
389 | resetPosition(mViewRef.get());
390 | mSlideHelper.onLayoutChild(mViewRef.get());
391 | }
392 | }
393 |
394 | private void resetPosition(V v) {
395 | int top = v.getTop();
396 | ViewCompat.offsetTopAndBottom(v, -top);
397 | }
398 |
399 |
400 | public int getSlideModel() {
401 | return mSlideModel;
402 | }
403 |
404 | /**
405 | * Gets whether this bottom sheet can hide when it is swiped down.
406 | *
407 | * @return {@code true} if this bottom sheet can hide.
408 | * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_hideable
409 | */
410 | public boolean isHideable() {
411 | return mHideable;
412 | }
413 |
414 | /**
415 | * Sets a callback to be notified of bottom sheet events.
416 | *
417 | * @param callback The callback to notify when bottom sheet events occur.
418 | */
419 | public void setBottomSheetCallback(SheetCallback callback) {
420 | mCallback = callback;
421 | }
422 |
423 | /**
424 | * Sets the state of the bottom sheet. The bottom sheet will transition to that state with
425 | * animation.
426 | *
427 | * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED}, or
428 | * {@link #STATE_HIDDEN}.
429 | */
430 | public final void setState(@State int state) {
431 | mSlideHelper.setState(state);
432 | }
433 |
434 |
435 | public void expand() {
436 | mSlideHelper.setState(STATE_EXPANDED);
437 | }
438 |
439 | public void collapsed() {
440 | mSlideHelper.setState(STATE_COLLAPSED);
441 | }
442 |
443 | public void hidden() {
444 | mSlideHelper.setState(STATE_HIDDEN);
445 | }
446 |
447 | /**
448 | * Gets the current state of the bottom sheet.
449 | *
450 | * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING},
451 | * and {@link #STATE_SETTLING}.
452 | */
453 | @State
454 | public final int getState() {
455 | return mState;
456 | }
457 |
458 | private void setStateInternal(@State int state) {
459 | if (mState == state) {
460 | return;
461 | }
462 | mState = state;
463 | View bottomSheet = mViewRef.get();
464 | if (bottomSheet != null && mCallback != null) {
465 | mCallback.onStateChanged(bottomSheet, state);
466 | }
467 | }
468 |
469 | private void reset() {
470 | mActivePointerId = ViewDragHelper.INVALID_POINTER;
471 | if (mVelocityTracker != null) {
472 | mVelocityTracker.recycle();
473 | mVelocityTracker = null;
474 | }
475 | }
476 |
477 |
478 | private View findScrollingChild(View view) {
479 | if (view instanceof NestedScrollingChild) {
480 | return view;
481 | }
482 | if (view instanceof ViewGroup) {
483 | ViewGroup group = (ViewGroup) view;
484 | for (int i = 0, count = group.getChildCount(); i < count; i++) {
485 | View scrollingChild = findScrollingChild(group.getChildAt(i));
486 | if (scrollingChild != null) {
487 | return scrollingChild;
488 | }
489 | }
490 | }
491 | return null;
492 | }
493 |
494 | private float getYVelocity() {
495 | mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
496 | return VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId);
497 | }
498 |
499 | private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
500 |
501 | @Override
502 | public boolean tryCaptureView(View child, int pointerId) {
503 | if (mState == STATE_DRAGGING) {
504 | return false;
505 | }
506 | if (mTouchingScrollingChild) {
507 | return false;
508 | }
509 | if (mState == STATE_EXPANDED && mActivePointerId == pointerId) {
510 | View scroll = mNestedScrollingChildRef.get();
511 | if (scroll != null && ViewCompat.canScrollVertically(scroll, mSlideHelper.canScrollVertically())) {
512 | // Let the content scroll up
513 | return false;
514 | }
515 | }
516 | return mViewRef != null && mViewRef.get() == child;
517 | }
518 |
519 | @Override
520 | public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
521 | mSlideHelper.dispatchOnSlide(top);
522 | }
523 |
524 | @Override
525 | public void onViewDragStateChanged(int state) {
526 | if (state == ViewDragHelper.STATE_DRAGGING) {
527 | setStateInternal(STATE_DRAGGING);
528 | }
529 | }
530 |
531 | @Override
532 | public void onViewReleased(View releasedChild, float xvel, float yvel) {
533 | mSlideHelper.onViewReleased(releasedChild, xvel, yvel);
534 | }
535 |
536 | @Override
537 | public int clampViewPositionVertical(View child, int top, int dy) {
538 | return mSlideHelper.clampViewPositionVertical(child, top, dy);
539 | }
540 |
541 | @Override
542 | public int clampViewPositionHorizontal(View child, int left, int dx) {
543 | return mSlideHelper.clampViewPositionHorizontal(child, left, dx);
544 | }
545 |
546 | @Override
547 | public int getViewVerticalDragRange(View child) {
548 | return mSlideHelper.getViewVerticalDragRange(child);
549 | }
550 | };
551 |
552 |
553 | private class SettleRunnable implements Runnable {
554 |
555 | private final View mView;
556 |
557 | @State
558 | private final int mTargetState;
559 |
560 | SettleRunnable(View view, @State int targetState) {
561 | mView = view;
562 | mTargetState = targetState;
563 | }
564 |
565 | @Override
566 | public void run() {
567 | if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
568 | ViewCompat.postOnAnimation(mView, this);
569 | } else {
570 | setStateInternal(mTargetState);
571 | }
572 | }
573 | }
574 |
575 | protected static class SavedState extends View.BaseSavedState {
576 |
577 | @State
578 | final int state;
579 |
580 | public SavedState(Parcel source) {
581 | super(source);
582 | //noinspection ResourceType
583 | state = source.readInt();
584 | }
585 |
586 | public SavedState(Parcelable superState, @State int state) {
587 | super(superState);
588 | this.state = state;
589 | }
590 |
591 | @Override
592 | public void writeToParcel(Parcel out, int flags) {
593 | super.writeToParcel(out, flags);
594 | out.writeInt(state);
595 | }
596 |
597 | public static final Creator CREATOR =
598 | new Creator() {
599 | @Override
600 | public SavedState createFromParcel(Parcel source) {
601 | return new SavedState(source);
602 | }
603 |
604 | @Override
605 | public SavedState[] newArray(int size) {
606 | return new SavedState[size];
607 | }
608 | };
609 | }
610 |
611 | /**
612 | * A utility function to get the {@link SheetBehavior} associated with the {@code view}.
613 | *
614 | * @param view The {@link View} with {@link SheetBehavior}.
615 | * @return The {@link SheetBehavior} associated with the {@code view}.
616 | */
617 | @SuppressWarnings("unchecked")
618 | public static SheetBehavior from(V view) {
619 | ViewGroup.LayoutParams params = view.getLayoutParams();
620 | if (!(params instanceof CoordinatorLayout.LayoutParams)) {
621 | throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
622 | }
623 | CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
624 | .getBehavior();
625 | if (!(behavior instanceof SheetBehavior)) {
626 | throw new IllegalArgumentException(
627 | "The view is not associated with BottomSheetBehavior");
628 | }
629 | return (SheetBehavior) behavior;
630 | }
631 |
632 | public SlideHelper createSlideHelper(int slideMode) {
633 |
634 | switch (slideMode) {
635 | case TOP_SHEET:
636 | return new TopSlideHelper();
637 | case BOTTOM_SHEET:
638 | return new BottomSlideHelper();
639 |
640 | }
641 | throw new IllegalArgumentException("invalid orientation");
642 | }
643 |
644 |
645 | private abstract class SlideHelper {
646 |
647 | int mMinOffset;
648 | int mMaxOffset;
649 | int mParentHeight;
650 |
651 | abstract void onLayoutChild(V child);
652 |
653 | abstract void setPeekHeight(int peekHeight);
654 |
655 | abstract void setState(@State int state);
656 |
657 | /**
658 | * @return <0 表示支持往下滑, 0 表示不能滑动, >0 表示往上滑动
659 | */
660 | public int canScrollVertically() {
661 | return 0;
662 | }
663 |
664 | /**
665 | * @return >0 表示支持往左滑, 0 表示不能滑动, < 0 表示往右边滑动
666 | */
667 | public int canScrollHorizontally() {
668 | return 0;
669 | }
670 |
671 | public void dispatchOnSlide(int top) {
672 | View bottomSheet = mViewRef.get();
673 | if (bottomSheet != null && mCallback != null) {
674 | if (top > mMaxOffset) {
675 | mCallback.onSlide(bottomSheet, (float) (mMaxOffset - top) / mPeekHeight);
676 | } else {
677 |
678 | float slide = (float) (mMaxOffset - top) / ((mMaxOffset - mMinOffset));
679 | mCallback.onSlide(bottomSheet, slide);
680 |
681 | if (mNeedTickleInvalidationFlag && slide > 0) {
682 | mNeedTickleInvalidationFlag = false;
683 | updateOffsets(bottomSheet);
684 | }
685 | }
686 | }
687 | }
688 |
689 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
690 | int dx,
691 | int dy, int[] consumed) {
692 | }
693 |
694 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
695 |
696 | }
697 |
698 | public void onViewReleased(View releasedChild, float xvel, float yvel) {
699 |
700 | }
701 |
702 | public int clampViewPositionVertical(View child, int top, int dy) {
703 | return 0;
704 | }
705 |
706 | public int clampViewPositionHorizontal(View child, int left, int dx) {
707 | return 0;
708 | }
709 |
710 | public int getViewVerticalDragRange(View child) {
711 | return 0;
712 | }
713 |
714 | }
715 |
716 |
717 | private class TopSlideHelper extends SlideHelper {
718 |
719 |
720 | private int mChildHeight;
721 |
722 | @Override
723 | void onLayoutChild(V child) {
724 | mChildHeight = child.getHeight();
725 |
726 | mMaxOffset = -mChildHeight + mPeekHeight;
727 | mMinOffset = 0;
728 | if (mPeekHeight == 0) {
729 | mNeedTickleInvalidationFlag = true;
730 | }
731 | if (mState == STATE_EXPANDED) {
732 | ViewCompat.offsetTopAndBottom(child, mMinOffset);
733 | } else if (mHideable && mState == STATE_HIDDEN) {
734 | ViewCompat.offsetTopAndBottom(child, mMaxOffset - mPeekHeight);
735 | } else if (mState == STATE_COLLAPSED) {
736 | ViewCompat.offsetTopAndBottom(child, mMaxOffset);
737 | }
738 | }
739 |
740 | @Override
741 | void setPeekHeight(int peekHeight) {
742 | mPeekHeight = Math.max(0, peekHeight);
743 | mMaxOffset = -mChildHeight + mPeekHeight;
744 | if (mPeekHeight == 0) {
745 | mNeedTickleInvalidationFlag = true;
746 | }
747 | }
748 |
749 | public int canScrollVertically() {
750 | return 1;
751 | }
752 |
753 | public void setState(@State int state) {
754 | V child = mViewRef.get();
755 | if (child == null) {
756 | return;
757 | }
758 | int top;
759 | if (state == STATE_COLLAPSED) {
760 | top = mMaxOffset;
761 | } else if (state == STATE_EXPANDED) {
762 | top = mMinOffset;
763 | } else if (mHideable && state == STATE_HIDDEN) {
764 | top = mMaxOffset - mPeekHeight;
765 | } else {
766 | throw new IllegalArgumentException("Illegal state argument: " + state);
767 | }
768 | setStateInternal(STATE_SETTLING);
769 | if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
770 | ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
771 | }
772 | }
773 |
774 |
775 | public boolean shouldHide(View child, float yvel) {
776 | if (Math.abs(child.getTop()) < Math.abs(mMaxOffset)) {
777 | // It should not hide, but collapse.
778 | return false;
779 | }
780 | final float newTop = child.getTop() + yvel * HIDE_FRICTION;
781 | return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD;
782 | }
783 |
784 |
785 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
786 | int dx,
787 | int dy, int[] consumed) {
788 |
789 | int currentTop = child.getTop();
790 | int newTop = currentTop - dy;
791 | if (dy > 0) { // Upward
792 | if (!ViewCompat.canScrollVertically(target, 1)) {
793 | if (newTop >= mMaxOffset || mHideable) {
794 | consumed[1] = dy;
795 | ViewCompat.offsetTopAndBottom(child, -dy);
796 | setStateInternal(STATE_DRAGGING);
797 | } else {
798 | consumed[1] = currentTop - mMaxOffset;
799 | ViewCompat.offsetTopAndBottom(child, -consumed[1]);
800 | setStateInternal(STATE_COLLAPSED);
801 | }
802 | }
803 |
804 |
805 | } else if (dy < 0) { // Downward
806 |
807 | if (newTop > mMinOffset) {
808 | consumed[1] = currentTop - mMinOffset;
809 | ViewCompat.offsetTopAndBottom(child, -consumed[1]);
810 | setStateInternal(STATE_EXPANDED);
811 | } else {
812 | consumed[1] = dy;
813 | ViewCompat.offsetTopAndBottom(child, -dy);
814 | setStateInternal(STATE_DRAGGING);
815 | }
816 | }
817 | dispatchOnSlide(child.getTop());
818 | mLastNestedScrollDy = dy;
819 | }
820 |
821 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
822 | if (child.getTop() == mMinOffset) {
823 | setStateInternal(STATE_EXPANDED);
824 | return;
825 | }
826 | int top;
827 | int targetState;
828 | if (mLastNestedScrollDy < 0) { //down
829 | top = mMinOffset;
830 | targetState = STATE_EXPANDED;
831 | } else if (mHideable && shouldHide(child, getYVelocity())) {
832 | top = mMaxOffset - mPeekHeight;
833 | targetState = STATE_HIDDEN;
834 | } else if (mLastNestedScrollDy == 0) {
835 | int currentTop = child.getTop();
836 | if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
837 | top = mMinOffset;
838 | targetState = STATE_EXPANDED;
839 | } else {
840 | top = mMaxOffset;
841 | targetState = STATE_COLLAPSED;
842 | }
843 | } else {
844 | top = mMaxOffset;
845 | targetState = STATE_COLLAPSED;
846 | }
847 | if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
848 | setStateInternal(STATE_SETTLING);
849 | ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
850 | } else {
851 | setStateInternal(targetState);
852 | }
853 | }
854 |
855 |
856 | public void onViewReleased(View releasedChild, float xvel, float yvel) {
857 | int top;
858 | @State int targetState;
859 | if (yvel > 0) { // Moving down
860 | // if (mHideable && releasedChild.getTop() < mMaxOffset) {
861 | // top = mMaxOffset;
862 | // targetState = STATE_COLLAPSED;
863 | // } else {
864 | top = mMinOffset;
865 | targetState = STATE_EXPANDED;
866 | // }
867 | } else if (mHideable && shouldHide(releasedChild, yvel)) {
868 | top = mMaxOffset - mPeekHeight;
869 | targetState = STATE_HIDDEN;
870 | } else if (yvel == 0.f) {
871 | int currentTop = releasedChild.getTop();
872 | if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
873 | top = mMinOffset;
874 | targetState = STATE_EXPANDED;
875 | } else {
876 | top = mMaxOffset;
877 | targetState = STATE_COLLAPSED;
878 | }
879 | } else {
880 | top = mMaxOffset;
881 | targetState = STATE_COLLAPSED;
882 | }
883 | if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
884 | setStateInternal(STATE_SETTLING);
885 | ViewCompat.postOnAnimation(releasedChild,
886 | new SettleRunnable(releasedChild, targetState));
887 | } else {
888 | setStateInternal(targetState);
889 | }
890 | }
891 |
892 | public int clampViewPositionVertical(View child, int top, int dy) {
893 | return MathUtils.constrain(top, mHideable ? mMaxOffset - mPeekHeight : mMaxOffset, mMinOffset);
894 | }
895 |
896 | public int clampViewPositionHorizontal(View child, int left, int dx) {
897 | return child.getLeft();
898 | }
899 |
900 | public int getViewVerticalDragRange(View child) {
901 | if (mHideable) {
902 | return mMinOffset - mMaxOffset + mPeekHeight;
903 | } else {
904 | return mMinOffset - mMaxOffset;
905 | }
906 | }
907 |
908 |
909 | public void dispatchOnSlide(int top) {
910 | View bottomSheet = mViewRef.get();
911 | if (bottomSheet != null && mCallback != null) {
912 |
913 |
914 | if (top < mMaxOffset) {
915 | mCallback.onSlide(bottomSheet, (float) (top - mMaxOffset) / mPeekHeight);
916 | } else {
917 |
918 | float slide = (float) (top - mMaxOffset) / ((mMinOffset - mMaxOffset));
919 | mCallback.onSlide(bottomSheet, slide);
920 |
921 | if (mNeedTickleInvalidationFlag && slide > 0) {
922 | mNeedTickleInvalidationFlag = false;
923 | updateOffsets(bottomSheet);
924 | }
925 | }
926 | }
927 | }
928 |
929 |
930 | }
931 |
932 | private boolean mNeedTickleInvalidationFlag = false;
933 |
934 | private void updateOffsets(View view) {
935 |
936 | // Manually invalidate the view and parent to make sure we get drawn pre-M
937 | if (Build.VERSION.SDK_INT < 23) {
938 | tickleInvalidationFlag(view);
939 | final ViewParent vp = view.getParent();
940 | if (vp instanceof View) {
941 | tickleInvalidationFlag((View) vp);
942 | }
943 | }
944 | }
945 |
946 | private static void tickleInvalidationFlag(View view) {
947 | final float y = ViewCompat.getTranslationY(view);
948 | ViewCompat.setTranslationY(view, y + 1);
949 | ViewCompat.setTranslationY(view, y);
950 | }
951 |
952 | private class BottomSlideHelper extends SlideHelper {
953 |
954 |
955 | public void onLayoutChild(V child) {
956 |
957 | View parent = child.getParent() instanceof View ? (View) child.getParent() : null;
958 |
959 | if (parent != null) {
960 | mParentHeight = parent.getHeight();
961 | mMinOffset = Math.max(0, mParentHeight - child.getHeight());
962 | mMaxOffset = mParentHeight - mPeekHeight;
963 | if (mPeekHeight == 0) {
964 | mNeedTickleInvalidationFlag = true;
965 | }
966 | if (mState == STATE_EXPANDED) {
967 | ViewCompat.offsetTopAndBottom(child, mMinOffset);
968 | } else if (mHideable && mState == STATE_HIDDEN) {
969 | ViewCompat.offsetTopAndBottom(child, mParentHeight);
970 | } else if (mState == STATE_COLLAPSED) {
971 | ViewCompat.offsetTopAndBottom(child, mMaxOffset);
972 | }
973 | }
974 | }
975 |
976 |
977 | public int canScrollVertically() {
978 | return -1;
979 | }
980 |
981 |
982 | public void setPeekHeight(int peekHeight) {
983 | mPeekHeight = Math.max(0, peekHeight);
984 | mMaxOffset = mParentHeight - peekHeight;
985 | if (mPeekHeight == 0) {
986 | mNeedTickleInvalidationFlag = true;
987 | }
988 | }
989 |
990 | public void setState(@State int state) {
991 | V child = mViewRef.get();
992 | if (child == null) {
993 | return;
994 | }
995 | int top;
996 | if (state == STATE_COLLAPSED) {
997 | top = mMaxOffset;
998 | } else if (state == STATE_EXPANDED) {
999 | top = mMinOffset;
1000 | } else if (mHideable && state == STATE_HIDDEN) {
1001 | top = mParentHeight;
1002 | } else {
1003 | throw new IllegalArgumentException("Illegal state argument: " + state);
1004 | }
1005 | setStateInternal(STATE_SETTLING);
1006 | if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
1007 | ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
1008 | }
1009 | }
1010 |
1011 |
1012 | public boolean shouldHide(View child, float yvel) {
1013 | if (child.getTop() < mMaxOffset) {
1014 | // It should not hide, but collapse.
1015 | return false;
1016 | }
1017 | final float newTop = child.getTop() + yvel * HIDE_FRICTION;
1018 | return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD;
1019 | }
1020 |
1021 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
1022 | int dx,
1023 | int dy, int[] consumed) {
1024 |
1025 | int currentTop = child.getTop();
1026 | int newTop = currentTop - dy;
1027 | if (dy > 0) { // Upward
1028 | if (newTop < mMinOffset) {
1029 | consumed[1] = currentTop - mMinOffset;
1030 | ViewCompat.offsetTopAndBottom(child, -consumed[1]);
1031 | setStateInternal(STATE_EXPANDED);
1032 | } else {
1033 | consumed[1] = dy;
1034 | ViewCompat.offsetTopAndBottom(child, -dy);
1035 | setStateInternal(STATE_DRAGGING);
1036 | }
1037 | } else if (dy < 0) { // Downward
1038 | if (!ViewCompat.canScrollVertically(target, -1)) {
1039 | if (newTop <= mMaxOffset || mHideable) {
1040 | consumed[1] = dy;
1041 | ViewCompat.offsetTopAndBottom(child, -dy);
1042 | setStateInternal(STATE_DRAGGING);
1043 | } else {
1044 | consumed[1] = currentTop - mMaxOffset;
1045 | ViewCompat.offsetTopAndBottom(child, -consumed[1]);
1046 | setStateInternal(STATE_COLLAPSED);
1047 | }
1048 | }
1049 | }
1050 | dispatchOnSlide(child.getTop());
1051 | mLastNestedScrollDy = dy;
1052 | }
1053 |
1054 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
1055 | if (child.getTop() == mMinOffset) {
1056 | setStateInternal(STATE_EXPANDED);
1057 | return;
1058 | }
1059 | int top;
1060 | int targetState;
1061 | if (mLastNestedScrollDy > 0) {
1062 | top = mMinOffset;
1063 | targetState = STATE_EXPANDED;
1064 | } else if (mHideable && shouldHide(child, getYVelocity())) {
1065 | top = mParentHeight;
1066 | targetState = STATE_HIDDEN;
1067 | } else if (mLastNestedScrollDy == 0) {
1068 | int currentTop = child.getTop();
1069 | if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
1070 | top = mMinOffset;
1071 | targetState = STATE_EXPANDED;
1072 | } else {
1073 | top = mMaxOffset;
1074 | targetState = STATE_COLLAPSED;
1075 | }
1076 | } else {
1077 | top = mMaxOffset;
1078 | targetState = STATE_COLLAPSED;
1079 | }
1080 | if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
1081 | setStateInternal(STATE_SETTLING);
1082 | ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
1083 | } else {
1084 | setStateInternal(targetState);
1085 | }
1086 | }
1087 |
1088 | public void onViewReleased(View releasedChild, float xvel, float yvel) {
1089 | int top;
1090 | @State int targetState;
1091 | if (yvel < 0) { // Moving up
1092 | // if (mHideable && releasedChild.getTop() < mMaxOffset) {
1093 | // top = mMaxOffset;
1094 | // targetState = STATE_COLLAPSED;
1095 | // } else {
1096 | top = mMinOffset;
1097 | targetState = STATE_EXPANDED;
1098 | // }
1099 | } else if (mHideable && shouldHide(releasedChild, yvel)) {
1100 | top = mParentHeight;
1101 | targetState = STATE_HIDDEN;
1102 | } else if (yvel == 0.f) {
1103 | int currentTop = releasedChild.getTop();
1104 | if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
1105 | top = mMinOffset;
1106 | targetState = STATE_EXPANDED;
1107 | } else {
1108 | top = mMaxOffset;
1109 | targetState = STATE_COLLAPSED;
1110 | }
1111 | } else {
1112 | top = mMaxOffset;
1113 | targetState = STATE_COLLAPSED;
1114 | }
1115 | if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
1116 | setStateInternal(STATE_SETTLING);
1117 | ViewCompat.postOnAnimation(releasedChild,
1118 | new SettleRunnable(releasedChild, targetState));
1119 | } else {
1120 | setStateInternal(targetState);
1121 | }
1122 | }
1123 |
1124 | public int clampViewPositionVertical(View child, int top, int dy) {
1125 | return MathUtils.constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
1126 | }
1127 |
1128 | public int clampViewPositionHorizontal(View child, int left, int dx) {
1129 | return child.getLeft();
1130 | }
1131 |
1132 | public int getViewVerticalDragRange(View child) {
1133 | if (mHideable) {
1134 | return mParentHeight - mMinOffset;
1135 | } else {
1136 | return mMaxOffset - mMinOffset;
1137 | }
1138 | }
1139 | }
1140 |
1141 | }
1142 |
--------------------------------------------------------------------------------
/sweetbehavior/src/main/res/values/behavior_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | android.support.design.widget.InAppBarBehavior
4 | android.support.design.widget.InNestChildBehavior
5 | android.support.design.widget.SheetBehavior
6 |
7 |
--------------------------------------------------------------------------------
/sweetbehavior/src/main/res/values/sweet_behavior_attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/sweetbehavior/sweetbehavior.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |