├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── simmorsal │ │ └── concealernestedscrollview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── simmorsal │ │ │ └── concealernestedscrollview │ │ │ ├── AdapterRv.java │ │ │ ├── ConcealerRecyclerViewTestActivity.java │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_concealer_recycler_view_test.xml │ │ ├── activity_main.xml │ │ └── rv_layout.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── simmorsal │ └── concealernestedscrollview │ └── ExampleUnitTest.java ├── build.gradle ├── concealer_nested_scroll_view ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── simmorsal │ │ └── library │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── simmorsal │ │ │ └── library │ │ │ ├── ConcealerNestedScrollView.java │ │ │ └── ConcealerRecyclerView.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── simmorsal │ └── library │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | 11 | \.idea/ 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ConcealerNestedScrollView & ConcealerRecyclerView 2 | 3 | A library to make views hide from top and bottom while scrolling 4 | a custom NestedScrollView and\or a custom RecyclerView. 5 | 6 | ![gif_image](https://user-images.githubusercontent.com/24822099/34994798-e5d54432-fae9-11e7-8246-a12e66b20f18.gif) 7 | 8 | 9 | ## Changelog 10 | 11 | > **V 2.0.0** 12 | > 13 | > * ConcealerNSV now has the ability to auto hide header and footer views 14 | > 15 | > ``` java 16 | > cnsv.setHeaderAutoHide(true); // true by default 17 | > ``` 18 | > 19 | > * You can set how fast the views would auto hide 20 | > 21 | > ``` java 22 | > cnsv.setHeaderAutoHideSpeed(300); // in milliseconds 23 | > ``` 24 | > 25 | > * You can set how much of the view would be hidden so it would auto hide 26 | > ``` java 27 | > cnsv.setHeaderPercentageToHide(50); // 40 by default 28 | > ``` 29 | > 30 | > * You can make the views concealable or not, and also visible or not 31 | > ``` java 32 | > // parameters are (boolean viewConcealable, boolean shouldViewBeVisible) 33 | > cnsv.setHeaderConcealable(false, true); 34 | > ``` 35 | > 36 | > * All methods above apply to `footerView` as well. 37 | > 38 | > * Added **`ConcealerRecyclerView`** 39 | > 40 | > * Usage is the same as `ConcealerNestedScrollView` 41 | > 42 | > * Every method that works on `CNSV`, also works on `CRV` 43 | > 44 | > * **KNOWN ISSUES** 45 | > 46 | > If the starting touch position is iniatiated on a clickable view inside `CNSV` and `CRV`, 47 | > both classes will be unable to detect the touch 48 | > (`MotionEvent.ACTION_DOWN` and `MotionEvent.ACTION_UP`), so they 49 | > wouldn't be able to do auto hiding the moment the finger is lifted 50 | > off the screen, and so they will do it 70 milliseconds after the last 51 | > call on `onScrollChanged`. Pull requests are welcome. 52 | 53 | 54 | 55 | ## Usage 56 | 57 | ### Gradle 58 | 59 | ``` gradle 60 | implementation 'com.simmorsal.library:concealer_nested_scroll_view:2.0.0' 61 | ``` 62 | 63 | ### XML 64 | Starting with your XML layout, it should look like this: 65 | 66 | ![concealernsv-layout-setup](https://user-images.githubusercontent.com/24822099/34965249-ea66cfca-fa67-11e7-9982-20bf76e61551.png) 67 | 68 | A parent `RelativeLayout` or `FrameLayout` that inside it is the 69 | `ConcealerNestedScrollView` on top, and two views (or one) as 70 | header and footer below it. 71 | 72 | __IMPORTANT:__ DO NOT give `margin_top` to header view, 73 | or `margin_bottom` to footer view. We'll do that in Java. 74 | 75 | Click [here](https://github.com/SIMMORSAL/ConcealerNestedScrollView/blob/master/app/src/main/res/layout/activity_main.xml) to see a XML sample. 76 | 77 | ### JAVA 78 | 79 | In your java first get a reference to the `ConcealerNestedScrollView` 80 | widget. (also get a reference to the header and footer views as well). 81 | 82 | ``` JAVA 83 | ConcealerNestedScrollView cnsv = findViewById(R.id.cnsv); 84 | ``` 85 | 86 | Then you should pass the `headerView` and `footerView` (and margin top 87 | for header and margin bottom for footer views) to the `cnsv` object. 88 | 89 | To pass these views, you should make sure that they are completely 90 | drawn on the screen, so the library would be able to get their sizes. 91 | 92 | In order to do so, in your `onCreate` call `.post()` on header and footer 93 | views: 94 | 95 | ``` JAVA 96 | headerView.post(new Runnable() { 97 | @Override 98 | public void run() { 99 | // parameters are (View headerView, int marginTop) 100 | cnsv.setHeaderView(headerView, 15); 101 | } 102 | }); 103 | footerView.post(new Runnable() { 104 | @Override 105 | public void run() { 106 | // parameters are (View footerView, int marginBottom) 107 | cnsv.setFooterView(footerView, 0); 108 | } 109 | }); 110 | ``` 111 | 112 | __IMPORTANT NOTES:__ 113 | 114 | * if you dont want to set header and footer views 115 | from inside the onCreate, or you are sure when you want to pass 116 | header or footer views, they are drawn on the screen, dont call 117 | `.post()` on the views, and directly pass the views. 118 | 119 | * if on runtime your `headerView` height size changes (either by 120 | directly changing it's size, or setting a view inside it to `VISIBLE` 121 | that causes the view's height size to change), make sure to call 122 | `cnsv.resetHeaderHeight();` immediately after the code for 123 | resizing has been run. Likewise for the `footerView`, call 124 | `cnsv.resetFooterHeight();`. 125 | 126 | * You can make the views hide twice as fast if the 127 | `cnsv` has been scrolled more than the view's heights, by calling 128 | `cnsv.setHeaderFastHide(true);` and/or `cnsv.setFooterFastHide(true);`. 129 | 130 | 131 | 132 | ## License 133 | 134 | None. Do with it what you will. 135 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.simmorsal.concealernestedscrollview" 7 | minSdkVersion 14 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:27.1.1' 24 | implementation 'com.android.support:cardview-v7:27.1.1' 25 | implementation 'com.android.support:design:27.1.1' 26 | testImplementation 'junit:junit:4.12' 27 | implementation project(path: ':concealer_nested_scroll_view') 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/simmorsal/concealernestedscrollview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.concealernestedscrollview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.simmorsal.concealernestedscrollview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/simmorsal/concealernestedscrollview/AdapterRv.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.concealernestedscrollview; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.simmorsal.library.ConcealerRecyclerView; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | 16 | 17 | public class AdapterRv extends RecyclerView.Adapter { 18 | 19 | private List data = Arrays.asList("455A64", "90A4AE", "455A64", "90A4AE", "455A64", "90A4AE", "455A64", "90A4AE"); 20 | private Activity context; 21 | private ConcealerRecyclerView rv; 22 | 23 | 24 | public AdapterRv(Activity context, ConcealerRecyclerView rv) { 25 | this.context = context; 26 | this.rv = rv; 27 | } 28 | 29 | @Override 30 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 31 | final View view = LayoutInflater.from(context).inflate(R.layout.rv_layout, parent, false); 32 | return new CellFeedViewHolder(view); 33 | } 34 | 35 | @Override 36 | public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) { 37 | final CellFeedViewHolder h = (CellFeedViewHolder) viewHolder; 38 | 39 | h.root.setBackgroundColor(Color.parseColor("#" + data.get(position))); 40 | 41 | h.root.setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | switch (position) { 45 | case 0: 46 | rv.setHeaderConcealable(false, true); 47 | break; 48 | 49 | case 1: 50 | rv.setFooterConcealable(false, false); 51 | break; 52 | 53 | case 2: 54 | rv.setHeaderConcealable(true, false); 55 | break; 56 | 57 | case 3: 58 | rv.setFooterConcealable(true, true); 59 | } 60 | } 61 | }); 62 | } 63 | 64 | @Override 65 | public int getItemCount() { 66 | return data.size(); 67 | } 68 | 69 | 70 | private class CellFeedViewHolder extends RecyclerView.ViewHolder { 71 | 72 | View root; 73 | 74 | CellFeedViewHolder(View view) { 75 | super(view); 76 | 77 | root = view.findViewById(R.id.root); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/simmorsal/concealernestedscrollview/ConcealerRecyclerViewTestActivity.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.concealernestedscrollview; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.view.View; 7 | 8 | import com.simmorsal.library.ConcealerRecyclerView; 9 | 10 | public class ConcealerRecyclerViewTestActivity extends AppCompatActivity { 11 | 12 | private ConcealerRecyclerView rv; 13 | private View viewHeader, viewFooter; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_concealer_recycler_view_test); 19 | 20 | rv = findViewById(R.id.rv); 21 | viewHeader = findViewById(R.id.crdHeaderView); 22 | viewFooter = findViewById(R.id.linFooterView); 23 | 24 | setupConcealerRV(); 25 | runRv(); 26 | } 27 | 28 | private void setupConcealerRV() { 29 | viewHeader.post(new Runnable() { 30 | @Override 31 | public void run() { 32 | rv.setHeaderView(viewHeader, 16); 33 | } 34 | }); 35 | viewFooter.post(new Runnable() { 36 | @Override 37 | public void run() { 38 | rv.setFooterView(viewFooter, 0); 39 | } 40 | }); 41 | 42 | rv.setFooterPercentageToHide(80); 43 | } 44 | 45 | private void runRv() { 46 | rv.setAdapter(new AdapterRv(this, rv)); 47 | rv.setLayoutManager(new LinearLayoutManager(this)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/simmorsal/concealernestedscrollview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.concealernestedscrollview; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.CardView; 6 | import android.widget.LinearLayout; 7 | 8 | import com.simmorsal.library.ConcealerNestedScrollView; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | ConcealerNestedScrollView concealerNSV; 13 | CardView crdHeaderView; 14 | LinearLayout linFooterView; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_main); 20 | 21 | concealerNSV = findViewById(R.id.concealerNSV); 22 | crdHeaderView = findViewById(R.id.crdHeaderView); 23 | linFooterView = findViewById(R.id.linFooterView); 24 | 25 | setupConcealerNSV(); 26 | } 27 | 28 | private void setupConcealerNSV() { 29 | // if you're setting header and footer views at the very start of the layout creation (onCreate), 30 | // as the views are not drawn yet, the library cant get their correct sizes, so you have to do this: 31 | 32 | crdHeaderView.post(new Runnable() { 33 | @Override 34 | public void run() { 35 | concealerNSV.setHeaderView(crdHeaderView, 15); 36 | } 37 | }); 38 | linFooterView.post(new Runnable() { 39 | @Override 40 | public void run() { 41 | concealerNSV.setFooterView(linFooterView, 0); 42 | } 43 | }); 44 | 45 | 46 | // pass a true to setHeaderFastHide to make views hide faster 47 | concealerNSV.setHeaderFastHide(true); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_concealer_recycler_view_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | 22 | 23 | 30 | 31 | 32 | 38 | 39 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 43 | 44 | 45 | 52 | 53 | 60 | 61 | 62 | 68 | 69 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rv_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #455A64 4 | #263238 5 | #F4511E 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ConcealerNestedScrollView 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/simmorsal/concealernestedscrollview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.concealernestedscrollview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.2' 11 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.2' 12 | classpath "com.github.dcendents:android-maven-gradle-plugin:1.4.1" 13 | 14 | 15 | // NOTE: Do not place your application dependencies here; they belong 16 | // in the individual module build.gradle files 17 | } 18 | } 19 | 20 | allprojects { 21 | repositories { 22 | google() 23 | jcenter() 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | 4 | final VERSION_NAME = "2.0.0" 5 | final VERSION_CODE = 2 6 | 7 | ext { 8 | bintrayRepo = 'maven' 9 | bintrayName = 'concealer-nested-scroll-view' 10 | 11 | publishedGroupId = 'com.simmorsal.library' 12 | libraryName = 'ConcealerNestedScrollView' 13 | artifact = 'concealer_nested_scroll_view' 14 | 15 | libraryDescription = 'A library to make views hide from top and bottom while scrolling a custom NestedScrollView (and RecyclerView)' 16 | 17 | siteUrl = 'https://github.com/SIMMORSAL/ConcealerNestedScrollView' 18 | gitUrl = 'https://github.com/SIMMORSAL/ConcealerNestedScrollView' 19 | 20 | libraryVersion = VERSION_NAME 21 | 22 | developerId = 'simmorsal' 23 | developerName = 'SIMMORSAL' 24 | developerEmail = 'simmorsal@gmail.com' 25 | 26 | licenseName = 'The Apache Software License, Version 2.0' 27 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 28 | allLicenses = ["Apache-2.0"] 29 | } 30 | 31 | android { 32 | compileSdkVersion 27 33 | 34 | 35 | 36 | defaultConfig { 37 | minSdkVersion 14 38 | targetSdkVersion 27 39 | versionCode VERSION_CODE 40 | versionName VERSION_NAME 41 | 42 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 43 | 44 | } 45 | 46 | buildTypes { 47 | release { 48 | minifyEnabled false 49 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 50 | } 51 | } 52 | 53 | } 54 | 55 | dependencies { 56 | implementation fileTree(dir: 'libs', include: ['*.jar']) 57 | 58 | implementation 'com.android.support:appcompat-v7:27.1.1' 59 | implementation "com.android.support:design:27.1.1" 60 | testImplementation 'junit:junit:4.12' 61 | } 62 | 63 | 64 | apply from: 'https://raw.githubusercontent.com/SIMMORSAL/JCenter/master/installv1.gradle' 65 | apply from: 'https://raw.githubusercontent.com/SIMMORSAL/JCenter/master/bintrayv1.gradle' 66 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/src/androidTest/java/com/simmorsal/library/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.library; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.simmorsal.library.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/src/main/java/com/simmorsal/library/ConcealerNestedScrollView.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.library; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.os.Build; 6 | import android.os.Handler; 7 | import android.support.v4.widget.NestedScrollView; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.ViewTreeObserver; 12 | 13 | public class ConcealerNestedScrollView extends NestedScrollView { 14 | public ConcealerNestedScrollView(Context context) { 15 | super(context); 16 | } 17 | 18 | public ConcealerNestedScrollView(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public ConcealerNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | @Override 27 | public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 28 | dispatchNestedPreScroll(dx, dy, consumed, null); 29 | } 30 | 31 | 32 | private View headerView, footerView; 33 | private boolean isThereTouch = false; 34 | 35 | private int deltaY; 36 | private int t = 0; 37 | private int headerViewHeight = 0; 38 | private int headerTranslateY = 0; 39 | private int headerMaxTranslate; 40 | private int headerMinTranslate; 41 | private int footerViewHeight = 0; 42 | private int footerTranslateY = 0; 43 | private int footerMaxTranslate; 44 | private int footerMinTranslate; 45 | private boolean isHeaderFastHide = false, isFooterFastHide = false; 46 | 47 | private boolean shouldHeaderAutoHide = true, shouldFooterAutoHide = true; 48 | private int headerAutoHideSpeed = 200, footerAutoHideSpeed = 200; 49 | private int headerPercentageToHide = 40, footerPercentageToHide = 40; 50 | private Handler handlerHeader = new Handler(), handlerFooter = new Handler(); 51 | private boolean hasHeaderRunnableRun = false, hasFooterRunnableRun = false; 52 | 53 | private boolean isHeaderConcealable = true, isFooterConcealable = true; 54 | 55 | 56 | public void setFooterView(final View view, final int marginBottomInDp) { 57 | if (view.getHeight() == 0) { 58 | view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 59 | @Override 60 | public void onGlobalLayout() { 61 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 62 | view.getViewTreeObserver().removeOnGlobalLayoutListener(this); 63 | } 64 | footerViewHeight = view.getHeight(); 65 | footerMinTranslate = -footerViewHeight; 66 | } 67 | }); 68 | } else { 69 | footerViewHeight = view.getHeight(); 70 | footerMinTranslate = -footerViewHeight; 71 | } 72 | this.footerView = view; 73 | 74 | footerMaxTranslate = dpToPx(marginBottomInDp); 75 | footerTranslateY = footerMaxTranslate; 76 | 77 | view.setTranslationY(-footerMaxTranslate); 78 | } 79 | 80 | public void setHeaderView(final View view, final int marginTopInDp) { 81 | if (view.getHeight() == 0) { 82 | view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 83 | @Override 84 | public void onGlobalLayout() { 85 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 86 | view.getViewTreeObserver().removeOnGlobalLayoutListener(this); 87 | } 88 | headerViewHeight = view.getHeight(); 89 | headerMinTranslate = -headerViewHeight; 90 | } 91 | }); 92 | } else { 93 | headerViewHeight = view.getHeight(); 94 | headerMinTranslate = -headerViewHeight; 95 | } 96 | this.headerView = view; 97 | 98 | headerMaxTranslate = dpToPx(marginTopInDp); 99 | headerTranslateY = headerMaxTranslate; 100 | 101 | view.setTranslationY(headerMaxTranslate); 102 | } 103 | 104 | 105 | @Override 106 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 107 | super.onScrollChanged(l, t, oldl, oldt); 108 | 109 | this.t = t; 110 | deltaY = oldt - t; 111 | if (headerView != null && isHeaderConcealable) 112 | doHeaderTransition(); 113 | if (footerView != null && isFooterConcealable) 114 | doFooterTransition(); 115 | } 116 | 117 | private void doHeaderTransition() { 118 | 119 | if (shouldHeaderAutoHide) 120 | handlerHeader.removeCallbacks(headerAutoHideRunnable); 121 | 122 | int headerDelta = deltaY; 123 | 124 | // Going up 125 | if (headerDelta < 0 && headerTranslateY > headerMinTranslate) { 126 | if (isHeaderFastHide && t > headerMaxTranslate + headerViewHeight) 127 | headerDelta *= 2; 128 | headerTranslateY = headerTranslateY + headerDelta; 129 | if (headerTranslateY < headerMinTranslate) 130 | headerTranslateY = headerMinTranslate; 131 | headerView.setTranslationY(headerTranslateY); 132 | } 133 | 134 | // Going down 135 | else if (headerDelta > 0 && headerTranslateY < headerMaxTranslate) { 136 | if (t < headerMaxTranslate) 137 | headerDelta *= 3; 138 | headerTranslateY = headerTranslateY + headerDelta; 139 | if (t < 5) 140 | headerTranslateY = headerMaxTranslate; 141 | if (headerTranslateY > headerMaxTranslate) 142 | headerTranslateY = headerMaxTranslate; 143 | headerView.setTranslationY(headerTranslateY); 144 | } 145 | 146 | if (shouldHeaderAutoHide) { 147 | handlerHeader.postDelayed(headerAutoHideRunnable, 70); 148 | hasHeaderRunnableRun = false; 149 | } 150 | } 151 | 152 | private void doFooterTransition() { 153 | if (shouldFooterAutoHide) 154 | handlerFooter.removeCallbacks(footerAutoHideRunnable); 155 | 156 | int footerDelta = deltaY; 157 | 158 | // Going down 159 | if (footerDelta < 0 && footerTranslateY > footerMinTranslate) { 160 | if (isFooterFastHide && t > footerMaxTranslate + footerViewHeight) 161 | footerDelta *= 2; 162 | footerTranslateY = footerTranslateY + footerDelta; 163 | if (footerTranslateY < footerMinTranslate) 164 | footerTranslateY = footerMinTranslate; 165 | footerView.setTranslationY(-footerTranslateY); 166 | } 167 | 168 | // Going up 169 | else if (footerDelta > 0 && footerTranslateY < footerMaxTranslate) { 170 | if (t < footerMaxTranslate) 171 | footerDelta *= 3; 172 | footerTranslateY = footerTranslateY + footerDelta; 173 | if (t < 5) 174 | footerTranslateY = footerMaxTranslate; 175 | if (footerTranslateY > footerMaxTranslate) 176 | footerTranslateY = footerMaxTranslate; 177 | footerView.setTranslationY(-footerTranslateY); 178 | } 179 | 180 | if (shouldFooterAutoHide) { 181 | handlerFooter.postDelayed(footerAutoHideRunnable, 70); 182 | hasFooterRunnableRun = false; 183 | } 184 | } 185 | 186 | Runnable headerAutoHideRunnable = new Runnable() { 187 | @Override 188 | public void run() { 189 | hasHeaderRunnableRun = true; 190 | if (!isThereTouch) { 191 | if (headerTranslateY > (headerMinTranslate * (headerPercentageToHide / 100f))) {// should become visible 192 | headerTranslateY = headerMaxTranslate; 193 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 194 | headerView.animate().translationY(headerMaxTranslate).setDuration(headerAutoHideSpeed).withLayer(); 195 | else 196 | headerView.setTranslationY(headerMaxTranslate); 197 | } else { // should hide 198 | if (t > headerMaxTranslate + headerViewHeight) {// can hide 199 | headerTranslateY = headerMinTranslate; 200 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 201 | headerView.animate().translationY(headerMinTranslate).setDuration(headerAutoHideSpeed).withLayer(); 202 | else 203 | headerView.setTranslationY(headerMinTranslate); 204 | } else { // cant hide 205 | headerTranslateY = headerMaxTranslate; 206 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 207 | headerView.animate().translationY(headerMaxTranslate).setDuration(headerAutoHideSpeed).withLayer(); 208 | else 209 | headerView.setTranslationY(headerMaxTranslate); 210 | } 211 | } 212 | } 213 | } 214 | }; 215 | 216 | Runnable footerAutoHideRunnable = new Runnable() { 217 | @Override 218 | public void run() { 219 | hasFooterRunnableRun = true; 220 | if (!isThereTouch) { 221 | if (footerTranslateY > (footerMinTranslate * (footerPercentageToHide / 100f))) {// should become visible 222 | footerTranslateY = -footerMaxTranslate; 223 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 224 | footerView.animate().translationY(-footerMaxTranslate).setDuration(footerAutoHideSpeed).withLayer(); 225 | else 226 | footerView.setTranslationY(-footerMaxTranslate); 227 | } else { // should hide 228 | if (t > headerMaxTranslate + headerViewHeight) {// can hide 229 | footerTranslateY = footerMinTranslate; 230 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 231 | footerView.animate().translationY(-footerMinTranslate).setDuration(footerAutoHideSpeed).withLayer(); 232 | else 233 | footerView.setTranslationY(-footerMinTranslate); 234 | } else { // cant hide 235 | footerTranslateY = -footerMaxTranslate; 236 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 237 | footerView.animate().translationY(-footerMaxTranslate).setDuration(footerAutoHideSpeed).withLayer(); 238 | else 239 | footerView.setTranslationY(-footerMaxTranslate); 240 | } 241 | } 242 | } 243 | } 244 | }; 245 | 246 | 247 | @Override 248 | public boolean onTouchEvent(MotionEvent ev) { 249 | 250 | if (ev.getAction() == MotionEvent.ACTION_DOWN) 251 | isThereTouch = true; 252 | if (ev.getAction() == MotionEvent.ACTION_UP) { 253 | isThereTouch = false; 254 | 255 | if (shouldHeaderAutoHide && hasHeaderRunnableRun && isHeaderConcealable) 256 | handlerHeader.post(headerAutoHideRunnable); 257 | 258 | if (shouldFooterAutoHide && hasFooterRunnableRun && isFooterConcealable) 259 | handlerFooter.post(footerAutoHideRunnable); 260 | } 261 | 262 | return super.onTouchEvent(ev); 263 | } 264 | 265 | public void resetHeaderHeight() { 266 | if (headerView != null) 267 | headerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 268 | @Override 269 | public void onGlobalLayout() { 270 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 271 | headerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 272 | } 273 | headerViewHeight = headerView.getHeight(); 274 | headerMinTranslate = -headerViewHeight; 275 | } 276 | }); 277 | } 278 | 279 | public void resetFooterHeight() { 280 | if (footerView != null) 281 | footerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 282 | @Override 283 | public void onGlobalLayout() { 284 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 285 | footerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 286 | } 287 | footerViewHeight = footerView.getHeight(); 288 | footerMinTranslate = -footerViewHeight; 289 | } 290 | }); 291 | } 292 | 293 | 294 | public void setHeaderConcealable(boolean headerConcealable, boolean shouldHeaderBeVisible) { 295 | isHeaderConcealable = headerConcealable; 296 | 297 | if (shouldHeaderBeVisible) { 298 | headerTranslateY = headerMaxTranslate; 299 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 300 | headerView.animate().translationY(headerMaxTranslate).setDuration(headerAutoHideSpeed).withLayer(); 301 | else 302 | headerView.setTranslationY(headerMaxTranslate); 303 | } else { 304 | headerTranslateY = headerMinTranslate; 305 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 306 | headerView.animate().translationY(headerMinTranslate).setDuration(headerAutoHideSpeed).withLayer(); 307 | else 308 | headerView.setTranslationY(headerMinTranslate); 309 | } 310 | } 311 | 312 | public void setFooterConcealable(boolean footerConcealable, boolean shouldFooterBeVisible) { 313 | isFooterConcealable = footerConcealable; 314 | 315 | if (shouldFooterBeVisible) { 316 | footerTranslateY = -footerMaxTranslate; 317 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 318 | footerView.animate().translationY(-footerMaxTranslate).setDuration(footerAutoHideSpeed).withLayer(); 319 | else 320 | footerView.setTranslationY(-footerMaxTranslate); 321 | } else { 322 | footerTranslateY = footerMinTranslate; 323 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 324 | footerView.animate().translationY(-footerMinTranslate).setDuration(footerAutoHideSpeed).withLayer(); 325 | else 326 | footerView.setTranslationY(-footerMinTranslate); 327 | } 328 | } 329 | 330 | public void setHeaderFastHide(boolean isHeaderFastHide) { 331 | this.isHeaderFastHide = isHeaderFastHide; 332 | } 333 | 334 | public void setFooterFastHide(boolean isFooterFastHide) { 335 | this.isFooterFastHide = isFooterFastHide; 336 | } 337 | 338 | public void setHeaderAutoHide(boolean shouldHeaderAutoHide) { 339 | this.shouldHeaderAutoHide = shouldHeaderAutoHide; 340 | } 341 | 342 | public void setFooterAutoHide(boolean shouldFooterAutoHide) { 343 | this.shouldFooterAutoHide = shouldFooterAutoHide; 344 | } 345 | 346 | public void setHeaderAutoHideSpeed(int headerAutoHideSpeed) { 347 | this.headerAutoHideSpeed = headerAutoHideSpeed; 348 | } 349 | 350 | public void setFooterAutoHideSpeed(int footerAutoHideSpeed) { 351 | this.footerAutoHideSpeed = footerAutoHideSpeed; 352 | } 353 | 354 | public void setHeaderPercentageToHide(int headerPercentageToHide) { 355 | this.headerPercentageToHide = headerPercentageToHide; 356 | } 357 | 358 | public void setFooterPercentageToHide(int footerPercentageToHide) { 359 | this.footerPercentageToHide = footerPercentageToHide; 360 | } 361 | 362 | public static int dpToPx(int dp) { 363 | return (int) (dp * Resources.getSystem().getDisplayMetrics().density); 364 | } 365 | } 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/src/main/java/com/simmorsal/library/ConcealerRecyclerView.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.library; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.os.Build; 6 | import android.os.Handler; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.util.AttributeSet; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | import android.view.ViewTreeObserver; 13 | 14 | public class ConcealerRecyclerView extends RecyclerView { 15 | public ConcealerRecyclerView(Context context) { super(context); } 16 | public ConcealerRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } 17 | public ConcealerRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } 18 | 19 | 20 | private int y = 0, oldy = 0; 21 | 22 | private View headerView, footerView; 23 | private boolean isThereTouch = false; 24 | 25 | private int deltaY; 26 | private int t = 0; 27 | private int headerViewHeight = 0; 28 | private int headerTranslateY = 0; 29 | private int headerMaxTranslate; 30 | private int headerMinTranslate; 31 | private int footerViewHeight = 0; 32 | private int footerTranslateY = 0; 33 | private int footerMaxTranslate; 34 | private int footerMinTranslate; 35 | private boolean isHeaderFastHide = false, isFooterFastHide = false; 36 | 37 | private boolean shouldHeaderAutoHide = true, shouldFooterAutoHide = true; 38 | private int headerAutoHideSpeed = 200, footerAutoHideSpeed = 200; 39 | private int headerPercentageToHide = 40, footerPercentageToHide = 40; 40 | private Handler handlerHeader = new Handler(), handlerFooter = new Handler(); 41 | private boolean hasHeaderRunnableRun = false, hasFooterRunnableRun = false; 42 | 43 | private boolean isHeaderConcealable = true, isFooterConcealable = true; 44 | 45 | 46 | public void setFooterView(final View view, final int marginBottomInDp) { 47 | if (view.getHeight() == 0) { 48 | view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 49 | @Override 50 | public void onGlobalLayout() { 51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 52 | view.getViewTreeObserver().removeOnGlobalLayoutListener(this); 53 | } 54 | footerViewHeight = view.getHeight(); 55 | footerMinTranslate = -footerViewHeight; 56 | } 57 | }); 58 | } else { 59 | footerViewHeight = view.getHeight(); 60 | footerMinTranslate = -footerViewHeight; 61 | } 62 | this.footerView = view; 63 | 64 | footerMaxTranslate = dpToPx(marginBottomInDp); 65 | footerTranslateY = footerMaxTranslate; 66 | 67 | view.setTranslationY(-footerMaxTranslate); 68 | } 69 | 70 | public void setHeaderView(final View view, final int marginTopInDp) { 71 | if (view.getHeight() == 0) { 72 | view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 73 | @Override 74 | public void onGlobalLayout() { 75 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 76 | view.getViewTreeObserver().removeOnGlobalLayoutListener(this); 77 | } 78 | headerViewHeight = view.getHeight(); 79 | headerMinTranslate = -headerViewHeight; 80 | } 81 | }); 82 | } else { 83 | headerViewHeight = view.getHeight(); 84 | headerMinTranslate = -headerViewHeight; 85 | } 86 | this.headerView = view; 87 | 88 | headerMaxTranslate = dpToPx(marginTopInDp); 89 | headerTranslateY = headerMaxTranslate; 90 | 91 | view.setTranslationY(headerMaxTranslate); 92 | } 93 | 94 | 95 | @Override 96 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 97 | super.onScrollChanged(l, t, oldl, oldt); 98 | 99 | y = computeVerticalScrollOffset(); 100 | 101 | this.t = y; 102 | deltaY = oldy - y; 103 | 104 | if (headerView != null && isHeaderConcealable) 105 | doHeaderTransition(); 106 | if (footerView != null && isFooterConcealable) 107 | doFooterTransition(); 108 | 109 | oldy = y; 110 | } 111 | private void doHeaderTransition() { 112 | 113 | if (shouldHeaderAutoHide) 114 | handlerHeader.removeCallbacks(headerAutoHideRunnable); 115 | 116 | int headerDelta = deltaY; 117 | 118 | // Going up 119 | if (headerDelta < 0 && headerTranslateY > headerMinTranslate) { 120 | if (isHeaderFastHide && t > headerMaxTranslate + headerViewHeight) 121 | headerDelta *= 2; 122 | headerTranslateY = headerTranslateY + headerDelta; 123 | if (headerTranslateY < headerMinTranslate) 124 | headerTranslateY = headerMinTranslate; 125 | headerView.setTranslationY(headerTranslateY); 126 | } 127 | 128 | // Going down 129 | else if (headerDelta > 0 && headerTranslateY < headerMaxTranslate) { 130 | if (t < headerMaxTranslate) 131 | headerDelta *= 3; 132 | headerTranslateY = headerTranslateY + headerDelta; 133 | if (t < 5) 134 | headerTranslateY = headerMaxTranslate; 135 | if (headerTranslateY > headerMaxTranslate) 136 | headerTranslateY = headerMaxTranslate; 137 | headerView.setTranslationY(headerTranslateY); 138 | } 139 | 140 | if (shouldHeaderAutoHide) { 141 | handlerHeader.postDelayed(headerAutoHideRunnable, 70); 142 | hasHeaderRunnableRun = false; 143 | } 144 | } 145 | 146 | private void doFooterTransition() { 147 | if (shouldFooterAutoHide) 148 | handlerFooter.removeCallbacks(footerAutoHideRunnable); 149 | 150 | int footerDelta = deltaY; 151 | 152 | // Going down 153 | if (footerDelta < 0 && footerTranslateY > footerMinTranslate) { 154 | if (isFooterFastHide && t > footerMaxTranslate + footerViewHeight) 155 | footerDelta *= 2; 156 | footerTranslateY = footerTranslateY + footerDelta; 157 | if (footerTranslateY < footerMinTranslate) 158 | footerTranslateY = footerMinTranslate; 159 | footerView.setTranslationY(-footerTranslateY); 160 | } 161 | 162 | // Going up 163 | else if (footerDelta > 0 && footerTranslateY < footerMaxTranslate) { 164 | if (t < footerMaxTranslate) 165 | footerDelta *= 3; 166 | footerTranslateY = footerTranslateY + footerDelta; 167 | if (t < 5) 168 | footerTranslateY = footerMaxTranslate; 169 | if (footerTranslateY > footerMaxTranslate) 170 | footerTranslateY = footerMaxTranslate; 171 | footerView.setTranslationY(-footerTranslateY); 172 | } 173 | 174 | if (shouldFooterAutoHide) { 175 | handlerFooter.postDelayed(footerAutoHideRunnable, 70); 176 | hasFooterRunnableRun = false; 177 | } 178 | } 179 | 180 | Runnable headerAutoHideRunnable = new Runnable() { 181 | @Override 182 | public void run() { 183 | hasHeaderRunnableRun = true; 184 | if (!isThereTouch) { 185 | if (headerTranslateY > (headerMinTranslate * (headerPercentageToHide / 100f))) {// should become visible 186 | headerTranslateY = headerMaxTranslate; 187 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 188 | headerView.animate().translationY(headerMaxTranslate).setDuration(headerAutoHideSpeed).withLayer(); 189 | else 190 | headerView.setTranslationY(headerMaxTranslate); 191 | } else { // should hide 192 | if (t > headerMaxTranslate + headerViewHeight) {// can hide 193 | headerTranslateY = headerMinTranslate; 194 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 195 | headerView.animate().translationY(headerMinTranslate).setDuration(headerAutoHideSpeed).withLayer(); 196 | else 197 | headerView.setTranslationY(headerMinTranslate); 198 | } else { // cant hide 199 | headerTranslateY = headerMaxTranslate; 200 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 201 | headerView.animate().translationY(headerMaxTranslate).setDuration(headerAutoHideSpeed).withLayer(); 202 | else 203 | headerView.setTranslationY(headerMaxTranslate); 204 | } 205 | } 206 | } 207 | } 208 | }; 209 | 210 | Runnable footerAutoHideRunnable = new Runnable() { 211 | @Override 212 | public void run() { 213 | hasFooterRunnableRun = true; 214 | if (!isThereTouch) { 215 | if (footerTranslateY > (footerMinTranslate * (footerPercentageToHide / 100f))) {// should become visible 216 | footerTranslateY = -footerMaxTranslate; 217 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 218 | footerView.animate().translationY(-footerMaxTranslate).setDuration(footerAutoHideSpeed).withLayer(); 219 | else 220 | footerView.setTranslationY(-footerMaxTranslate); 221 | } else { // should hide 222 | if (t > headerMaxTranslate + headerViewHeight) {// can hide 223 | footerTranslateY = footerMinTranslate; 224 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 225 | footerView.animate().translationY(-footerMinTranslate).setDuration(footerAutoHideSpeed).withLayer(); 226 | else 227 | footerView.setTranslationY(-footerMinTranslate); 228 | } else { // cant hide 229 | footerTranslateY = -footerMaxTranslate; 230 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 231 | footerView.animate().translationY(-footerMaxTranslate).setDuration(footerAutoHideSpeed).withLayer(); 232 | else 233 | footerView.setTranslationY(-footerMaxTranslate); 234 | } 235 | } 236 | } 237 | } 238 | }; 239 | 240 | 241 | @Override 242 | public boolean onTouchEvent(MotionEvent ev) { 243 | 244 | if (ev.getAction() == MotionEvent.ACTION_DOWN) 245 | isThereTouch = true; 246 | if (ev.getAction() == MotionEvent.ACTION_UP) { 247 | isThereTouch = false; 248 | 249 | if (shouldHeaderAutoHide && hasHeaderRunnableRun && isHeaderConcealable) 250 | handlerHeader.post(headerAutoHideRunnable); 251 | 252 | if (shouldFooterAutoHide && hasFooterRunnableRun && isFooterConcealable) 253 | handlerFooter.post(footerAutoHideRunnable); 254 | } 255 | 256 | return super.onTouchEvent(ev); 257 | } 258 | 259 | public void resetHeaderHeight() { 260 | if (headerView != null) 261 | headerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 262 | @Override 263 | public void onGlobalLayout() { 264 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 265 | headerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 266 | } 267 | headerViewHeight = headerView.getHeight(); 268 | headerMinTranslate = -headerViewHeight; 269 | } 270 | }); 271 | } 272 | 273 | public void resetFooterHeight() { 274 | if (footerView != null) 275 | footerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 276 | @Override 277 | public void onGlobalLayout() { 278 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 279 | footerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 280 | } 281 | footerViewHeight = footerView.getHeight(); 282 | footerMinTranslate = -footerViewHeight; 283 | } 284 | }); 285 | } 286 | 287 | 288 | public void setHeaderConcealable(boolean headerConcealable, boolean shouldHeaderBeVisible) { 289 | isHeaderConcealable = headerConcealable; 290 | 291 | if (shouldHeaderBeVisible) { 292 | headerTranslateY = headerMaxTranslate; 293 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 294 | headerView.animate().translationY(headerMaxTranslate).setDuration(headerAutoHideSpeed).withLayer(); 295 | else 296 | headerView.setTranslationY(headerMaxTranslate); 297 | } else { 298 | headerTranslateY = headerMinTranslate; 299 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 300 | headerView.animate().translationY(headerMinTranslate).setDuration(headerAutoHideSpeed).withLayer(); 301 | else 302 | headerView.setTranslationY(headerMinTranslate); 303 | } 304 | } 305 | 306 | public void setFooterConcealable(boolean footerConcealable, boolean shouldFooterBeVisible) { 307 | isFooterConcealable = footerConcealable; 308 | 309 | if (shouldFooterBeVisible) { 310 | footerTranslateY = -footerMaxTranslate; 311 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 312 | footerView.animate().translationY(-footerMaxTranslate).setDuration(footerAutoHideSpeed).withLayer(); 313 | else 314 | footerView.setTranslationY(-footerMaxTranslate); 315 | } else { 316 | footerTranslateY = footerMinTranslate; 317 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 318 | footerView.animate().translationY(-footerMinTranslate).setDuration(footerAutoHideSpeed).withLayer(); 319 | else 320 | footerView.setTranslationY(-footerMinTranslate); 321 | } 322 | } 323 | 324 | public void setHeaderFastHide(boolean isHeaderFastHide) { 325 | this.isHeaderFastHide = isHeaderFastHide; 326 | } 327 | 328 | public void setFooterFastHide(boolean isFooterFastHide) { 329 | this.isFooterFastHide = isFooterFastHide; 330 | } 331 | 332 | public void setHeaderAutoHide(boolean shouldHeaderAutoHide) { 333 | this.shouldHeaderAutoHide = shouldHeaderAutoHide; 334 | } 335 | 336 | public void setFooterAutoHide(boolean shouldFooterAutoHide) { 337 | this.shouldFooterAutoHide = shouldFooterAutoHide; 338 | } 339 | 340 | public void setHeaderAutoHideSpeed(int headerAutoHideSpeed) { 341 | this.headerAutoHideSpeed = headerAutoHideSpeed; 342 | } 343 | 344 | public void setFooterAutoHideSpeed(int footerAutoHideSpeed) { 345 | this.footerAutoHideSpeed = footerAutoHideSpeed; 346 | } 347 | 348 | public void setHeaderPercentageToHide(int headerPercentageToHide) { 349 | this.headerPercentageToHide = headerPercentageToHide; 350 | } 351 | 352 | public void setFooterPercentageToHide(int footerPercentageToHide) { 353 | this.footerPercentageToHide = footerPercentageToHide; 354 | } 355 | 356 | public static int dpToPx(int dp) { 357 | return (int) (dp * Resources.getSystem().getDisplayMetrics().density); 358 | } 359 | 360 | } 361 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | library 3 | 4 | -------------------------------------------------------------------------------- /concealer_nested_scroll_view/src/test/java/com/simmorsal/library/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.simmorsal.library; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Mon Jan 15 20:53:38 PST 2018 16 | 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simmorsal/ConcealerNestedScrollView-ConcealerRecyclerView/b0b26b512e52fe67d3d5d7ed89e77b46a9d1d376/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun May 13 19:17:36 IRDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':concealer_nested_scroll_view' 2 | --------------------------------------------------------------------------------