Android: Damn that Sliding Drawer !


C’mon…Android’s SlidingDrawer is a pretty neat & handy component. It does what its supposed to: open/close a drawer revealing with it tucked away icons or additional view components.

Its a pretty smart way of:

  • Intuitive use of a well-known archetype everybody knows: a drawer which open/close..simple!
  • Saves and maximizes use of screen real estate
  • Visually appealing.

The Problem:
BUT…..yes you had it coming, sometimes its default behavior is not what you would expect! The default behaviour of the SlidingDrawer component is to maximize to a height of the position of the last component on the screen. But if the last component is at the very bottom, then the SlidingDrawer will not be apparently visible!

In the screenshot immediately below,  I want the Help SlidingDrawer to overlap the ListView when maximized, except the ListView is blocking its visibility!..I know annoying Droid!

The Solution:

Manipulating Android’s View layer to hide/reveal components!
listview – a reference to the ListView component declared in your XML-layout file.
helpDrawer – a reference to the SlidingDrawer component in our XML-layout file.

         // SlidingDrawer
	helpDrawer = (SlidingDrawer) this.findViewById(R.id.helpdrawer);

        // Open Handler
	helpDrawer.setOnDrawerOpenListener(new OnDrawerOpenListener()
	{
		@Override
		public void onDrawerOpened()
		{
			 listView.setVisibility(ListView.GONE);
		}
	});

	// Closing Handler
	helpDrawer.setOnDrawerCloseListener(new OnDrawerCloseListener()
	{
		@Override
		public void onDrawerClosed()
			{
				 listView.setVisibility(ListView.VISIBLE);
			}
	});

Initially Minimized and Closed state:

Open and Maximized State:

For making the example more clear:

Download: TestSlidingDrawerOverList.zip

File: main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">

	<ListView
		android:id="@+id/list_journal"
		android:layout_width="fill_parent"
		android:layout_height="365dip" />

	<SlidingDrawer
		android:id="@+id/slidingDrawer"
		android:handle="@+id/drawerHandle"
		android:content="@+id/contentLayout"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content">

		<ImageView
			android:id="@+id/drawerHandle"
			android:src="@drawable/help_tab_selector"
			android:layout_width="fill_parent"
			android:layout_height="wrap_content">
		</ImageView>

		<LinearLayout
			xmlns:android="http://schemas.android.com/apk/res/android"
			android:id="@+id/contentLayout"
			android:gravity="center"
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			android:background="@drawable/bg">
			<ImageView
				android:src="@drawable/icon"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content"
				android:layout_gravity="center">
			</ImageView>
		</LinearLayout>
	</SlidingDrawer>
</LinearLayout>

File: list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/list_item"
	android:gravity="center"
	android:textColor="@color/white"
	android:textAppearance="?android:attr/textAppearanceMedium"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">
</TextView>

File: Main.java

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.SlidingDrawer;
import android.widget.SlidingDrawer.OnDrawerCloseListener;
import android.widget.SlidingDrawer.OnDrawerOpenListener;

public class Main extends Activity implements OnDrawerOpenListener,
		OnDrawerCloseListener {

	private ListView listView;
	private SlidingDrawer slidingDrawer;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Set the View Layer
		setContentView(R.layout.main);
		
                // Generate some data
		List<String> data= getData();

		// Get reference to ListView
		listView = (ListView) this.findViewById(R.id.list_journal);
                
                 // Create a adapter for the ListView
		ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, R.layout.list_item, data);
 
                //Set the listview adapter
		listView.setAdapter(arrayAdapter);

		// Get reference to SlidingDrawer
		slidingDrawer = (SlidingDrawer) this.findViewById(R.id.slidingDrawer);

                //Listen for open event
		slidingDrawer.setOnDrawerOpenListener(this);

                // Listen for close event
		slidingDrawer.setOnDrawerCloseListener(this);
	}
	
	/**
	 * Get some data
	 * @return
	 */
	private List<String> getData()
	{
		List<String> data= new ArrayList<String>();
		for( int i= 0; i < 20; i++)
		{
			data.add( String.valueOf(i));
		}
		return data;
	}

	@Override
	public void onDrawerOpened() {
                // Hide listview 
		listView.setVisibility(ListView.GONE);
	}

	@Override
	public void onDrawerClosed() {
                // now make it visible again           
		listView.setVisibility(ListView.VISIBLE);
	}
}

State: Close - ListView over SlidingDrawer


State Open: SlidingDrawer over ListView

Viola!!!

It’s always helpful to have an alternate solution depending on how
your views are layed out.

An alternate solution has been provided by Mendelt Siebenga:

“The reason for the strange behavior with the sliding drawer isn’t the sliding drawer itself but the linearlayout. LinearLayouts can’t display overlapping views. If you use a framelayout as the parent of the listview and the slidingdrawer your problem is solved too.”

Download: http://www.filefactory.com/file/cb4f522/n/TestSlidingDrawerOverList.zip
File : main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">
	
	<LinearLayout
		android:orientation="vertical"
		android:layout_width="fill_parent"
		android:layout_height="fill_parent">		

		<TextView
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			android:text="Some extra text" />

		<ListView
			android:id="@+id/list_journal"
			android:layout_width="fill_parent"
			android:layout_height="365dip" />
	
	</LinearLayout>

	<SlidingDrawer
		android:id="@+id/slidingDrawer"
		android:handle="@+id/drawerHandle"
		android:content="@+id/contentLayout"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content">

		<ImageView
			android:id="@+id/drawerHandle"
			android:src="@drawable/help_tab_selector"
			android:layout_width="fill_parent"
			android:layout_height="wrap_content">
		</ImageView>

		<LinearLayout
			xmlns:android="http://schemas.android.com/apk/res/android"
			android:id="@+id/contentLayout"
			android:gravity="center"
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			android:background="@drawable/bg">
			<ImageView
				android:src="@drawable/icon"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content"
				android:layout_gravity="center">
			</ImageView>
		</LinearLayout>
	</SlidingDrawer>
</FrameLayout>

File: Main.java

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.SlidingDrawer;
import android.widget.SlidingDrawer.OnDrawerCloseListener;
import android.widget.SlidingDrawer.OnDrawerOpenListener;

public class Main extends Activity {

	private ListView listView;
//	private SlidingDrawer slidingDrawer;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Set the View Layer
		setContentView(R.layout.main);
		
		List<String> data= getData();

		// Get reference to ListView
		listView = (ListView) this.findViewById(R.id.list_journal);
		ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, R.layout.list_item, data);
		listView.setAdapter(arrayAdapter);

		// Get reference to SlidingDrawer
//		slidingDrawer = (SlidingDrawer) this.findViewById(R.id.slidingDrawer);
//		slidingDrawer.setOnDrawerOpenListener(this);
//		slidingDrawer.setOnDrawerCloseListener(this);
	}
	
	/**
	 * Get some data
	 * @return
	 */
	private List<String> getData()
	{
		List<String> data= new ArrayList<String>();
		for( int i= 0; i < 20; i++)
		{
			data.add( String.valueOf(i));
		}
		return data;
	}

//	@Override
//	public void onDrawerOpened() {
//		listView.setVisibility(ListView.GONE);
//	}
//
//	@Override
//	public void onDrawerClosed() {
//		listView.setVisibility(ListView.VISIBLE);
//	}

}

23 thoughts on “Android: Damn that Sliding Drawer !

    • Hi Wang Peng,

      Just noticed that its part of a larger project The essential parts: Get a reference to the SliderDrawer and intercept the : OnDrawerOpenListener OnDrawerCloseListener

      Setting visibility flag to GONE..(all view components have this flag).

      . I will post and example soon!

  1. Just stumbled onto your blog. Thanks for all the android info.

    The reason for the strange behavior with the sliding drawer isn’t the sliding drawer itself but the linearlayout. Listviews can’t display overlapping views. If you use a framelayout as the parent of the listview and the slidingdrawer your problem is solved too.

    • Oops, made a typo, I meant linearlayout where I typed listview. My comment should be:

      The reason for the strange behavior with the sliding drawer isn’t the sliding drawer itself but the linearlayout. LinearLayouts can’t display overlapping views. If you use a framelayout as the parent of the listview and the slidingdrawer your problem is solved too.

      • I know that Wagied Davids spent a lot of time on this post. So I apologize in advance for what I am about say.

        I think that the original part of this post should be deleted. People like me will stumble on it when looking up the SlidingDrawer and may even read through it before they read the manual.

        http://developer.android.com/reference/android/widget/SlidingDrawer.html

        The documentation clearly says “SlidingDrawer should only be used inside of a FrameLayout or a RelativeLayout for instance.”

        Beside this one little oversight, I think that Davids did a great job with this post.

  2. Hi! :) I am wondering how do you add 2 sliding drawers? I can’t seem to add 2, everytime I do that, the application crashes. :/ Changing the position of the handler also seems to cause a problem.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s