├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── iammert │ │ └── com │ │ └── view │ │ └── scalinglayout │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── iammert │ │ │ └── com │ │ │ └── view │ │ │ └── scalinglayout │ │ │ ├── CollapsingDemoActivity.java │ │ │ ├── FABDemo.java │ │ │ ├── MainActivity.java │ │ │ └── SearchBarDemoActivity.java │ └── res │ │ ├── drawable │ │ ├── ic_arrow_back_white_24dp.xml │ │ ├── ic_date_range_white_24dp.xml │ │ ├── ic_sort_white_24dp.xml │ │ ├── ic_star_white_24dp.xml │ │ ├── ic_tv_white_24dp.xml │ │ ├── ic_watch_later_white_24dp.xml │ │ └── img_cover.png │ │ ├── layout │ │ ├── activity_collapsing.xml │ │ ├── activity_fab.xml │ │ ├── activity_main.xml │ │ └── activity_search.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 │ └── iammert │ └── com │ └── view │ └── scalinglayout │ └── ExampleUnitTest.java ├── art ├── cover_scaling.png ├── gif_behavior.gif ├── gif_coordinator.gif ├── gif_fab.gif └── gif_searchbar.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── scalinglib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── iammert │ │ └── com │ │ └── view │ │ └── scalinglib │ │ ├── ScalingLayout.java │ │ ├── ScalingLayoutBehavior.java │ │ ├── ScalingLayoutListener.java │ │ ├── ScalingLayoutOutlineProvider.java │ │ ├── ScalingLayoutSettings.java │ │ └── State.java │ └── res │ ├── animator │ └── sl_state_animator.xml │ └── values │ ├── attrs.xml │ ├── dimens.xml │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .idea 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScalingLayout 2 | Scale your layout on user interaction. [Live Demo](https://www.youtube.com/watch?v=wA41H0UMoHQ) 3 | 4 | Motivated by layout in [Blinkist](https://play.google.com/store/apps/details?id=com.blinkslabs.blinkist.android) app and search bar in [Spotify](https://play.google.com/store/apps/details?id=com.spotify.music) app. 5 | 6 | 7 | 8 | ## Demo 9 | 10 | 11 | ## Fab Demo 12 | 13 | 14 | ## Spotify Search Demo 15 | 16 | 17 | ## Usage 18 | ```xml 19 | 24 | 25 | 26 | 27 | 28 | ``` 29 | 30 | ```java 31 | scalingLayout.expand(); //use this if you want to expand all 32 | scalingLayout.collapse(); //user this if you want to collapse view to initial state. 33 | scalingLayout.setProgress(float progress); //1 is fully expanded, 0 is initial state. 34 | ``` 35 | 36 | ## Listener 37 | ```java 38 | scalingLayout.setListener(new ScalingLayoutListener() { 39 | @Override 40 | public void onCollapsed() {} 41 | 42 | @Override 43 | public void onExpanded() {} 44 | 45 | @Override 46 | public void onProgress(float progress) {} 47 | }); 48 | ``` 49 | 50 | ## Attribute 51 | ```app:radiusFactor``` value is between 0 and 1 float value. 1 = full rounded corner. 0 = no rounded corner. 52 | 53 | ## ScalingLayoutBehaviour 54 | 55 | 56 | ```xml 57 | 63 | 64 | 65 | 66 | 67 | ``` 68 | 69 | ## Download 70 | Buy Me a Coffee at ko-fi.com 71 | 72 | 73 | ```gradle 74 | maven { url 'https://jitpack.io' } 75 | ``` 76 | 77 | ```gradle 78 | dependencies { 79 | compile 'com.github.iammert:ScalingLayout:1.2.1' 80 | } 81 | ``` 82 | License 83 | -------- 84 | 85 | 86 | Copyright 2017 Mert Şimşek. 87 | 88 | Licensed under the Apache License, Version 2.0 (the "License"); 89 | you may not use this file except in compliance with the License. 90 | You may obtain a copy of the License at 91 | 92 | http://www.apache.org/licenses/LICENSE-2.0 93 | 94 | Unless required by applicable law or agreed to in writing, software 95 | distributed under the License is distributed on an "AS IS" BASIS, 96 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 97 | See the License for the specific language governing permissions and 98 | limitations under the License. 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.2" 6 | defaultConfig { 7 | applicationId "iammert.com.view.scalinglayout" 8 | minSdkVersion 15 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:26.1.0' 28 | testCompile 'junit:junit:4.12' 29 | compile project(':scalinglib') 30 | } 31 | -------------------------------------------------------------------------------- /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/mertsimsek/Library/Android/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/iammert/com/view/scalinglayout/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package iammert.com.view.scalinglayout; 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 | * Instrumentation 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("iammert.com.view.scalinglayout", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/iammert/com/view/scalinglayout/CollapsingDemoActivity.java: -------------------------------------------------------------------------------- 1 | package iammert.com.view.scalinglayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | 8 | /** 9 | * Created by mertsimsek on 01/10/2017. 10 | */ 11 | 12 | public class CollapsingDemoActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_collapsing); 18 | 19 | Toolbar toolbar = findViewById(R.id.toolbar); 20 | setSupportActionBar(toolbar); 21 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 22 | getSupportActionBar().setDisplayShowHomeEnabled(true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/iammert/com/view/scalinglayout/FABDemo.java: -------------------------------------------------------------------------------- 1 | package iammert.com.view.scalinglayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.view.ViewCompat; 6 | import android.support.v4.view.ViewPropertyAnimatorListener; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | 12 | import iammert.com.view.scalinglib.ScalingLayout; 13 | import iammert.com.view.scalinglib.ScalingLayoutListener; 14 | import iammert.com.view.scalinglib.State; 15 | 16 | /** 17 | * Created by mertsimsek on 01/10/2017. 18 | */ 19 | 20 | public class FABDemo extends AppCompatActivity { 21 | 22 | @Override 23 | protected void onCreate(@Nullable Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_fab); 26 | 27 | final ImageView fabIcon = findViewById(R.id.fabIcon); 28 | final LinearLayout filterLayout = findViewById(R.id.filterLayout); 29 | final ScalingLayout scalingLayout = findViewById(R.id.scalingLayout); 30 | 31 | scalingLayout.setListener(new ScalingLayoutListener() { 32 | @Override 33 | public void onCollapsed() { 34 | ViewCompat.animate(fabIcon).alpha(1).setDuration(150).start(); 35 | ViewCompat.animate(filterLayout).alpha(0).setDuration(150).setListener(new ViewPropertyAnimatorListener() { 36 | @Override 37 | public void onAnimationStart(View view) { 38 | fabIcon.setVisibility(View.VISIBLE); 39 | } 40 | 41 | @Override 42 | public void onAnimationEnd(View view) { 43 | filterLayout.setVisibility(View.INVISIBLE); 44 | } 45 | 46 | @Override 47 | public void onAnimationCancel(View view) { 48 | 49 | } 50 | }).start(); 51 | } 52 | 53 | @Override 54 | public void onExpanded() { 55 | ViewCompat.animate(fabIcon).alpha(0).setDuration(200).start(); 56 | ViewCompat.animate(filterLayout).alpha(1).setDuration(200).setListener(new ViewPropertyAnimatorListener() { 57 | @Override 58 | public void onAnimationStart(View view) { 59 | filterLayout.setVisibility(View.VISIBLE); 60 | } 61 | 62 | @Override 63 | public void onAnimationEnd(View view) { 64 | fabIcon.setVisibility(View.INVISIBLE); 65 | } 66 | 67 | @Override 68 | public void onAnimationCancel(View view) { 69 | 70 | } 71 | }).start(); 72 | } 73 | 74 | @Override 75 | public void onProgress(float progress) { 76 | if (progress > 0) { 77 | fabIcon.setVisibility(View.INVISIBLE); 78 | } 79 | 80 | if(progress < 1){ 81 | filterLayout.setVisibility(View.INVISIBLE); 82 | } 83 | } 84 | }); 85 | 86 | scalingLayout.setOnClickListener(new View.OnClickListener() { 87 | @Override 88 | public void onClick(View view) { 89 | if (scalingLayout.getState() == State.COLLAPSED) { 90 | scalingLayout.expand(); 91 | } 92 | } 93 | }); 94 | 95 | 96 | findViewById(R.id.rootLayout).setOnClickListener(new View.OnClickListener() { 97 | @Override 98 | public void onClick(View view) { 99 | if (scalingLayout.getState() == State.EXPANDED) { 100 | scalingLayout.collapse(); 101 | } 102 | } 103 | }); 104 | 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/iammert/com/view/scalinglayout/MainActivity.java: -------------------------------------------------------------------------------- 1 | package iammert.com.view.scalinglayout; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_main); 14 | 15 | 16 | findViewById(R.id.demo1).setOnClickListener(new View.OnClickListener() { 17 | @Override 18 | public void onClick(View view) { 19 | startActivity(new Intent(MainActivity.this, CollapsingDemoActivity.class)); 20 | } 21 | }); 22 | 23 | findViewById(R.id.demo2).setOnClickListener(new View.OnClickListener() { 24 | @Override 25 | public void onClick(View view) { 26 | startActivity(new Intent(MainActivity.this, FABDemo.class)); 27 | } 28 | }); 29 | 30 | findViewById(R.id.demo3).setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View view) { 33 | startActivity(new Intent(MainActivity.this, SearchBarDemoActivity.class)); 34 | } 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/iammert/com/view/scalinglayout/SearchBarDemoActivity.java: -------------------------------------------------------------------------------- 1 | package iammert.com.view.scalinglayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.view.ViewCompat; 6 | import android.support.v4.view.ViewPropertyAnimatorListener; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.RelativeLayout; 10 | import android.widget.TextView; 11 | 12 | import iammert.com.view.scalinglib.ScalingLayout; 13 | import iammert.com.view.scalinglib.ScalingLayoutListener; 14 | import iammert.com.view.scalinglib.State; 15 | 16 | /** 17 | * Created by mertsimsek on 01/10/2017. 18 | */ 19 | 20 | public class SearchBarDemoActivity extends AppCompatActivity { 21 | 22 | @Override 23 | protected void onCreate(@Nullable Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_search); 26 | 27 | final TextView textViewSearch = findViewById(R.id.textViewSearch); 28 | final RelativeLayout searchLayout = findViewById(R.id.searchLayout); 29 | 30 | final ScalingLayout scalingLayout = findViewById(R.id.scalingLayout); 31 | scalingLayout.setListener(new ScalingLayoutListener() { 32 | @Override 33 | public void onCollapsed() { 34 | ViewCompat.animate(textViewSearch).alpha(1).setDuration(150).start(); 35 | ViewCompat.animate(searchLayout).alpha(0).setDuration(150).setListener(new ViewPropertyAnimatorListener() { 36 | @Override 37 | public void onAnimationStart(View view) { 38 | textViewSearch.setVisibility(View.VISIBLE); 39 | } 40 | 41 | @Override 42 | public void onAnimationEnd(View view) { 43 | searchLayout.setVisibility(View.INVISIBLE); 44 | } 45 | 46 | @Override 47 | public void onAnimationCancel(View view) { 48 | 49 | } 50 | }).start(); 51 | } 52 | 53 | @Override 54 | public void onExpanded() { 55 | ViewCompat.animate(textViewSearch).alpha(0).setDuration(200).start(); 56 | ViewCompat.animate(searchLayout).alpha(1).setDuration(200).setListener(new ViewPropertyAnimatorListener() { 57 | @Override 58 | public void onAnimationStart(View view) { 59 | searchLayout.setVisibility(View.VISIBLE); 60 | } 61 | 62 | @Override 63 | public void onAnimationEnd(View view) { 64 | textViewSearch.setVisibility(View.INVISIBLE); 65 | } 66 | 67 | @Override 68 | public void onAnimationCancel(View view) { 69 | 70 | } 71 | }).start(); 72 | } 73 | 74 | @Override 75 | public void onProgress(float progress) { 76 | 77 | } 78 | }); 79 | scalingLayout.setOnClickListener(new View.OnClickListener() { 80 | @Override 81 | public void onClick(View view) { 82 | if (scalingLayout.getState() == State.COLLAPSED) { 83 | scalingLayout.expand(); 84 | } 85 | } 86 | }); 87 | 88 | findViewById(R.id.rootLayout).setOnClickListener(new View.OnClickListener() { 89 | @Override 90 | public void onClick(View view) { 91 | if (scalingLayout.getState() == State.EXPANDED) { 92 | scalingLayout.collapse(); 93 | } 94 | } 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_date_range_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sort_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_star_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_tv_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_watch_later_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iammert/ScalingLayout/f19164d4c4e08f93ddeb9f40aaa39e2025d1b866/app/src/main/res/drawable/img_cover.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_collapsing.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 20 | 21 | 28 | 29 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 50 | 51 | 56 | 57 | 64 | 65 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 87 | 88 | 96 | 97 | 102 | 103 | 109 | 110 | 121 | 122 | 123 | 124 | 129 | 130 | 136 | 137 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_fab.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 24 | 25 | 31 | 32 | 41 | 42 | 47 | 48 | 54 | 55 | 66 | 67 | 68 | 69 | 74 | 75 | 81 | 82 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |