Android Core

Android SlidingPaneLayout: Tutorial

In this post, we want to show how to use SlidingPaneLayout. This is an interesting component that can be used when we want to have a multi-pane horizontal layout. This component is divided in two different parts:

  • left side: The master part. It usually contains a list of values (i.e. Contacts and so on)
  • right side: The detail part. It contains the details of the values in the left side.

This component helps us to divide the available screen space in two different sides that doesn’t overlap and can be  sledded horizontally.  It is smart enough to know when it is the time to enable the sliding feature or when the screen width is enough to hold both parts.

In this post, we will show how to use this component implementing a bookmark list in one side and the corresponding web content view in the other side. SlidingPaneLayout can be used with fragment or with other standard components, we will show how to use it with fragments. One thing we have to remember is that in our main layout it has to be the root. At the end, we want to have something like the picture shown below:

android_slidingpanelayout_page_loaded_thumb[5]

Let’s start.

SlidingPaneLayout set up

The first thing we need is creating our main layout:

<android.support.v4.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/sp" >
    <!-- Left side pane. (Master) -->
    <fragment android:id="@+id/leftpane"
              android:name="com.survivingwithandroid.slidingpanelayout.ListBookmarkFragment"
              android:layout_width="190dp"
              android:layout_height="match_parent"
              android:layout_gravity="left"  />

    <!-- Right side page. (Slave)  -->
    <fragment android:id="@+id/rightpane"
              android:name="com.survivingwithandroid.slidingpanelayout.ViewBookmarkFragment"
              android:layout_width="350dp"
              android:layout_height="match_parent"
              android:layout_gravity="right"
              android:layout_weight="1" 
          />

</android.support.v4.widget.SlidingPaneLayout>

Notice we have two fragment: one on the left side called ListBookmarkFragment and the other one on the right side called ViewBookmarkFragment. At line 1, we have our SlidingPaneLayout as the root element. One thing we have to set up is the width of this two fragment, at line 9 and 16. If the screen width is more than the sum of the two fragments width then the two fragments are visible at the same time, otherwise we have to slide left and right to enable one of them.

I won’t spend much time talking about fragment implementation details because is out of the topic, if you are interested you can give a look at the source code.

The main activity that holds the two fragments and handle the sliding pane is shown below:

public class MainActivity extends Activity {
    SlidingPaneLayout pane;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
...
}

Everything is very simple, but we need to implement more thing is we want to “use” the SlidingPaneLayout.

SlidingPaneLayout Listener

The api gives us the capability to listen when the user slide our panes left and right so that we can react in the right way and implement here our business logic. We have to implements an interface called SlidingPaneLayout.PanelSlideListener.

private class PaneListener implements SlidingPaneLayout.PanelSlideListener {

    @Override
    public void onPanelClosed(View view) {
        System.out.println("Panel closed");
     }

    @Override
    public void onPanelOpened(View view) {
       System.out.println("Panel opened");    
    }

    @Override
    public void onPanelSlide(View view, float arg1) {
        System.out.println("Panel sliding");
    }

}

So in our Activity let’s add this piece of code. As you can notice at line 1 we implement that interface and we get notified when the left panel is closed, is opened and when the user is sliding. We are more interested on the first two events (closed and opened). Then we have to register our listener:

...
@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 pane = (SlidingPaneLayout) findViewById(R.id.sp);
 pane.setPanelSlideListener(new PaneListener());
}
....

Running the code we can notice that when the left pane is closed or opened in the log we get the message. Now we can a way to get notified about the main two events. We can use it to set up correctly the action bar, for example.

SlidingPaneLayout and Actionbar

Using the listener, we can setup correctly the action bar. This aspect is very important because we can change the action bar icons according to the opened panel. In other words, when the left panel is open we can show some icons and when it is closed we can show other icons. To do it, we have simply implement our logic inside the listener methods:

private class PaneListener implements SlidingPaneLayout.PanelSlideListener {

    @Override
    public void onPanelClosed(View view) {
        System.out.println("Panel closed");

        getFragmentManager().findFragmentById(R.id.leftpane).setHasOptionsMenu(false);
        getFragmentManager().findFragmentById(R.id.rightpane).setHasOptionsMenu(true);
    }

    @Override
    public void onPanelOpened(View view) {
        System.out.println("Panel opened");    
        getFragmentManager().findFragmentById(R.id.leftpane).setHasOptionsMenu(true);
        getFragmentManager().findFragmentById(R.id.rightpane).setHasOptionsMenu(false);
    }

    @Override
    public void onPanelSlide(View view, float arg1) {
        System.out.println("Panel sliding");
    }

}

We use setHasOptionMenu to turn on and off the fragment menu (line 7,8,14,15). Running the code we have:


android_slidingpanelayout_sliding

android_slidingpanelayout_webview

As you can notice when you open the left panel the action bar icons change.

Inter fragment communication

One more thing we need to explain: how to exchange information between fragments. In other word, we want when user clicks on a bookmark list on the left side the corresponding web page is opened on the right side.

To get it, we can simply create our interface that acts as a listener and let our main activity implement it. So that when user select an item in the list on the left panel we notify the event to the main activity, that in turns call the right method in fragment that handles the right panel. So we have:

public interface BookmarkListener {
    public void onChangeBookmark(String bookmark);
}

The interface. In the fragment that handles the left panel we have:

....
@Override
public void onAttach(Activity activity) {        

    // It's time we check if our activity implements the right inteface
    if (! (activity instanceof BookmarkListener) )
        throw new ClassCastException();

    super.onAttach(activity);

}
...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {        

        View v = inflater.inflate(R.layout.leftside_layout, container, true);
        ListView lView = (ListView) v.findViewById(R.id.bookList);
        LinkAdapter la = new LinkAdapter(bookmarkList, getActivity());
        lView.setAdapter(la);
        lView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position,
                    long id) {
                ( (BookmarkListener) getActivity()).onChangeBookmark( bookmarkList.get(position).link );
            }
        });
        setHasOptionsMenu(true);
        return v;
 }
...

In the main activity, we have:

public class MainActivity extends Activity implements BookmarkListener{

....
@Override
public void onChangeBookmark(String bookmark) {
    // We get notified when user clicks on a bookmark in the ListBookmarkFragment
    System.out.println("Bookmark ["+bookmark+"]");
    ViewBookmarkFragment f = (ViewBookmarkFragment) getFragmentManager().findFragmentById(R.id.rightpane);
    f.setBookmark(bookmark);

}
....
}

At the end we have:

android_slidingpanelayout_page_loaded_thumb[8]

The last thing we need to take care is to set up correctly the icons the first time the app is launched. In this case we need to add this piece of code to the onCreate method:

public class MainActivity extends Activity implements BookmarkListener{
 SlidingPaneLayout pane;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  pane = (SlidingPaneLayout) findViewById(R.id.sp);
  pane.setPanelSlideListener(new PaneListener());

  if (!pane.isSlideable()) {
   getFragmentManager().findFragmentById(R.id.leftpane).setHasOptionsMenu(false);
   getFragmentManager().findFragmentById(R.id.rightpane).setHasOptionsMenu(true);
  }
 }
  • Source code available @ github

 

Reference: Android SlidingPaneLayout: Tutorial from our JCG partner Francesco Azzola at the Surviving w/ Android blog.

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Jorge
Jorge
10 years ago

Nice tutorial but, is the SlidingPaneLayout still experimental??

Matías Dumrauf
10 years ago

Great tuto!! Just if someone is wondering, you can modify MainActivity to start with the pane opened, this way:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pane = (SlidingPaneLayout) findViewById(R.id.sp);
pane.setPanelSlideListener(new PaneListener());

if (!pane.isSlideable()) {
getFragmentManager().findFragmentById(R.id.leftpane).setHasOptionsMenu(false);
getFragmentManager().findFragmentById(R.id.rightpane).setHasOptionsMenu(true);
}

pane.openPane();
}

logan
logan
9 years ago

Great! it works fine only when invoke the openPane() method. thanks a lot

Back to top button