Skip to content

A native android navigation drawer that supports sliding from the left and right ends with parallax effect.

License

Notifications You must be signed in to change notification settings

IODevBlue/ParallaxNavigationDrawer

Repository files navigation

Parallax Navigation Drawer

Parallax Navigation Drawer is a custom Native Android navigation drawer that supports sliding from the left and right ends with parallax effect.

GitHub release (latest by date) Repository Size License GitHub Repository stars GitHub watchers Gradle version Kotlin version

Uses

Parallax Navigation Drawer can be used to provide left and right navigation drawers. Each drawer can contain dynamic content.

Parallax Navigation Drawer

Installation

There are several ways to install this library.

  1. Grab a JAR artefact from the Maven Central Repository:
  • On Gradle
implementation 'io.github.iodevblue:parallaxnavigationdrawer:1.1.1'
  • On Apache Maven
<dependency>
  <groudId> io.github.iodevblue </groudId>
  <artifactId> parallaxnavigationdrawer </artifactId>
  <version> 1.1.1 </version>
</dependency>

If it is a snapshot version, add the snapshot Maven Nexus OSS repository:

maven {   
  url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
}

Then retrieve a copy:

implementation 'io.github.iodevblue:parallaxnavigationdrawer:1.1.1-SNAPSHOT'
  1. Grab a JAR or AAR artifact from the release section.
  • Place it in libs folder in your project module and install in your project.
implementation fileTree(dir:' libs', include:'*jar')

If you do not prefer the compiled JAR and want access to the source files directly:

  1. Download the project zip file.
  • Create a new module with name parallaxnavigationdrawer in your project.
  • Copy the contents of the parallaxnavigationdrawer module from the downloaded project zip file to the new module parallaxnavigationdrawer.
  • This method makes the source code accessible. If you do make major or minor improvements to the source code, consider making a pull request or an issue to make a contribution.

Check the Contributing for more information.

Usage

To use ParallaxNavigationDrawer, you will need three layout files

  • One representing the left navigation drawer.
  • One representing the right navigation drawer.
  • One representing the main User Interface content.

Suppose these are the layouts...

  • The Left drawer content layout representing the left navigation drawer (drawer_left.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ImageView/>

</RelativeLayout>
  • The Right drawer content layout representing the right navigation drawer (drawer_right.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView/>
    
</LinearLayout>
  • Main content layout representing the Main User Interface (drawer_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout/>

    <ImageView/>

    <TextView/>

    <ImageButton/>

</RelativeLayout>

Navigate to your Activity or Fragment XML layout file (e.g activity_main.xml), add a ParallaxNavigationDrawer widget and include the layouts in the following order

  • Left navigation drawer layout
  • Right navigation drawer layout
  • Main content layout
<com.blueiobase.api.android.parallaxnavigationdrawer.ParallaxNavigationDrawer
        android:id="@+id/pnd"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        app:mainContentShadowAlpha="0.5"
        app:mainContentCloseOnTouch="true"
        app:drawerMode="both"
        app:parallax="true"
        >

        <include layout="@layout/drawer_left"/>

        <include layout="@layout/drawer_right"/>

        <include layout="@layout/drawer_main"/>

</com.blueiobase.api.android.parallaxnavigationdrawer.ParallaxNavigationDrawer>

NOTE: The layouts MUST be in the specified order (Left, Right, Main) and there MUST be no more than 3 layout files included in the ParallaxNavigationDrawer widget unless an IllegalStateException would be thrown.

To utilize one of either drawers, add the drawerMode attribute to the ParallaxNavigationDrawer widget specifying either left or right enum value. Then at least two layouts.

  • One layout representing the navigation drawer.
  • The other representing the main content User Interface.

NOTE: The layout included last would be considered the main User Interface content.

<com.blueiobase.api.android.parallaxnavigationdrawer.ParallaxNavigationDrawer
        android:id="@+id/pnd"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        app:mainContentShadowAlpha="0.5"
        app:mainContentCloseOnTouch="true"
        app:drawerMode="right"
        app:parallax="true"
        >
  
        <include layout="@layout/drawer_right"/>

        <include layout="@layout/drawer_main"/>

</com.blueiobase.api.android.parallaxnavigationdrawer.ParallaxNavigationDrawer>

Specifying none indicates that drawers are deactivated.

NOTE: By default, ParallaxNavigationDrawer has the drawerMode attribute set to none. This means it must be explicitly set either in the XML layout file or in the class file.

To open the left drawer:

parallaxNavigationDrawer.openLeftDrawer()

To close the close drawer:

parallaxNavigationDrawer.closeLeftDrawer()

To toggle between open and close states of the left drawer:

parallaxNavigationDrawer.toggleLeftDrawer()

You can utilize the onBackPressed() function which closes either the left or right drawer and returns a boolean indicating the closed state of both drawers. This can be implemented in the overridden onSupportNavigateUp() of an Activity class.

override fun onSupportNavigateUp(): Boolean {
  return if (parallaxNavigationDrawer.onBackPressed()) super.onNavigateUp() else false
}

Listen to drawer open and close events by using either one or both of these depending on your implementation:

  • OnDrawerStateChangedListener: Listens for open and close events for both drawers.
  • OnLeftDrawerStateChangedListener: Listens for open and close events for only the left drawer.
  • OnRightDrawerStateChangedListener: Listens for open and close events for only the right drawer.
private val parallaxNavigationDrawer: ParallaxNavigationDrawer by lazy { findViewById(R.id.pnd) }

parallaxNavigationDrawer.apply {
    setOnLeftDrawerStateChangedListener {
        if(it)
            Toast.makeText(this@MainActivity, "Left Drawer Open!", Toast.LENGTH_SHORT).show()
        else
            Toast.makeText(this@MainActivity, "Left Drawer Close!", Toast.LENGTH_SHORT).show()
    }
    setOnRightDrawerStateChangedListener {
        if(it)
            Toast.makeText(this@MainActivity, "Right Drawer Open!", Toast.LENGTH_SHORT).show()
        else
            Toast.makeText(this@MainActivity, "Right Drawer Close!", Toast.LENGTH_SHORT).show()
    }
}

Alternatively, you can specify a drawer state listener using Kotlin DSL:

parallaxNavigationDrawer.setOnLeftDrawerStateChangedListener {
    if(it)
        Toast.makeText(this@MainActivity, "Left Drawer Open!", Toast.LENGTH_SHORT).show()
    else
        Toast.makeText(this@MainActivity, "Left Drawer Close!", Toast.LENGTH_SHORT).show()
}

parallaxNavigationDrawer.setOnRightDrawerStateChangedListener {
    if(it)
        Toast.makeText(this@MainActivity, "Right Drawer Open!", Toast.LENGTH_SHORT).show()
    else
        Toast.makeText(this@MainActivity, "Right Drawer Close!", Toast.LENGTH_SHORT).show()
}

To use ParallaxNavigationDrawer in a declarative manner:

  • Create a ParallaxNavigationDrawer instance using a Context:
private val parallaxNavigationDrawer = ParallaxNavigationDrawer(context)
  • Inflate the layouts representing the drawers and main User Interface content to View objects and add them dynamically to the ParallaxNavigationDrawer instance:
val layoutInflater = LayoutInflater.from(context)
val leftDrawer = layoutInflater.inflate(R.layout.drawer_left, parallaxNavigationDrawer)
val rightDrawer = layoutInflater.inflate(R.layout.drawer_right, parallaxNavigationDrawer)
val mainContent = layoutInflater.inflate(R.layout.drawer_main, parallaxNavigationDrawer)

Or if the View objects representing the drawers already exist, then each can be added to the ParallaxNavigationDrawer instance.

NOTE: ParallaxNavigationDrawer can only host a maximum of 3 View objects. One for the left drawer, one for the right drawer and one for the main User Interface content.

parallaxNavigationDrawer.apply {
  addView(leftDrawerView)
  addView(rightDrawerView)
  addView(mainContentView)
}

The index in the addView() method signifies the position of each View added to the ParallaxNavigationDrawer.

  • 0 = Left Drawer
  • 1 = Right Drawer
  • 2 = Main User Interface content

NOTE: The View objects MUST be inflated with the ParallaxNavigationDrawer instance or added to it in this manner => Left, Right, Main. The order must be maintained for conformity.

Then you can set the ParallaxNavigationDrawer as the content view of your Activity

setContentView(parallaxNavigationDrawer)

A shorter syntax with Kotlin DSL involves creating an instance using an extension function on a Context class and performing the necessary preliminary setup.

private val parallaxNavigationDrawer = parallaxNavigationDrawer {
  drawerMode = ParallaxNavigationDrawer.DRAWER_MODE_BOTH
  addView(leftDrawerView)
  addView(rightDrawerView)
  addView(mainContentView)
}

See sample implementation for more details.

Java Interoperability

ParallaxNavigationDrawer is completely interoperable with Java projects.

Retrieve a ParallaxNavigationDrawer from an inflated layout:

ParallaxNavigationDrawer parallaxNavigationDrawer = findViewById(R.id.pnd);

Or create using an instance using a Context:

ParallaxNavigationDrawer parallaxNavigationDrawer = new ParallaxNavigationDrawer(context);

Make preliminary changes to the ParallaxNavigationDrawer instance:

parallaxNavigationDrawer.setDuration(400);
parallaxNavigationDrawer.setEnableSwiping(false);
parallaxNavigationDrawer.setParallax(true);

To implement a drawer state listener, say an OnLeftDrawerStateChangedListener:

parallaxNavigationDrawer.setOnLeftDrawerStateChangedListener(new ParallaxNavigationDrawer.OnLeftDrawerStateChangedListener() {
  @Override
  public void onDrawerStateChanged (boolean isOpen) {
	if(isOpen){
	  Toast.makeText(context,"Left Drawer Open!",Toast.LENGTH_SHORT).show();
	} else {
	  Toast.makeText(context, "Left Drawer Close!", Toast.LENGTH_SHORT).show();
	}
  }
});

Or you can default to using a lambda invocation:

parallaxNavigationDrawer.setOnLeftDrawerStateChangedListener(isOpen -> {
  if(isOpen){
	Toast.makeText(cont,"Left Drawer Open!",Toast.LENGTH_SHORT).show();
  } else {
	Toast.makeText(cont, "Left Drawer Close!", Toast.LENGTH_SHORT).show();
  }
});

A drawer state listener can also be implemented using a Function1 receiver function from the kotlin.jvm.functions package like so:

Function1<Boolean, Unit> function1 = (bool) -> {
  if (bool) {
	Toast.makeText(cont,"Left Drawer Open!",Toast.LENGTH_SHORT).show();
  } else {
	Toast.makeText(cont, "Left Drawer Close!", Toast.LENGTH_SHORT).show();
  }
  return null;
};

parallaxNavigationDrawer.setOnLeftDrawerStateChangedListener(function1);

Configurations:

Variable Default Use
drawerMode DRAWER_MODE_NONE Sets and retrieves the drawer mode.
duration 700 The time it takes for the ParallaxNavigationDrawer to complete its open and close animation.
enableSwiping true Enables swiping and dragging on the main content View to reveal either drawers.
mainContentPadding 1/4th of the screen's total width The padding applied to the main content View when the ParallaxNavigationDrawer is open.
mainContentShadowAlpha 0.5F The alpha value of the shadow applied to the main content View when the ParallaxNavigationDrawer is open.
mainContentShadowColor BLACK The color of the shadow applied to the main content View when the ParallaxNavigationDrawer is open.
mainContentCloseOnTouch false Enables the close-on-touch feature which closes the ParallaxNavigationDrawer when a widget on the main content View is touched.
parallax true Enables or disables the parallax feature.

Contributions

Contributors are welcome!

NOTE: This repository is split into two branches:

All developing implementations and proposed changes are pushed to the development branch and finalized updates are pushed to the main branch.

To note if current developments are being made, there would be more commits in the development branch than in the main branch.

Check the Contributing for more information.

Changelog

  • 1.1.1

    • Added inertia to make parallax and drawer dragging less sensitive and more smooth on screen.
    • Fixed bug where drawers do not snap back to closed or open state when released from dragging.
    • Fixed bug where drawers auto drag to open or closed state when first toggled then dragged.
  • 1.0.1

    • Fixed bug where listeners are invoked when any open drawer's content is touched.
    • Drawer open state now saves across configuration changes.

More version history can be gotten from the Change log file.

License

    Copyright 2023 IO DevBlue

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.