Layouts

Presenter – Part 2

In Part 1 of this series we began our look at the code for the app that I used for my presentation on Android layouts at AndroidConf Brasil 2011. Previously we had begun our look at a custom layout named DisplayLayout which is the layout which holds the slides in the presentation. We’ll continue with a look at this class.

Let’s first have a look at the the class fields and constructor for DisplayLayout:

[java] public class DisplayLayout extends FrameLayout
{
private static final String TAG = StandaloneDisplayActivity.TAG;

private List slides = new ArrayList();

private int current = -1;

private SlideLayout currentSlide;

private FragmentManager fragMgr = null;

private int inAnim = -1;
private int outAnim = -1;

public DisplayLayout( Context context, AttributeSet attrs )
{
super( context, attrs );

if ( context instanceof FragmentActivity )
{
fragMgr = ( (FragmentActivity) context )
.getSupportFragmentManager();
}

TypedArray ta = context.obtainStyledAttributes( attrs,
R.styleable.DisplayLayout );
final String slidesStr = ta
.getString( R.styleable.DisplayLayout_slides );

inAnim = ta.getResourceId( R.styleable.DisplayLayout_inAnimation, -1 );
outAnim = ta.getResourceId( R.styleable.DisplayLayout_outAnimation, -1 );

ta.recycle();

new Thread( new Runnable()
{

@Override
public void run()
{
try
{
InputStream is = getResources().getAssets()
.open( slidesStr );
BufferedReader reader = new BufferedReader(
new InputStreamReader( is ) );
StringBuilder sb = new StringBuilder();
String line;
while ( ( line = reader.readLine() ) != null )
{
sb.append( line );
}
reader.close();

JSONArray arr = new JSONArray( sb.toString() );
for ( int i = 0; i < arr.length(); i++ ) { String slide = arr.optString( i ); if ( slide != null && !slide.isEmpty() ) slides.add( slide ); } } catch ( IOException e ) { Log.e( TAG, "Error reading slides", e ); } catch ( JSONException e ) { Log.e( TAG, "Error parsing slides", e ); } if ( !slides.isEmpty() ) { go( 0, false ); } } } ).start(); } . . . } [/java] The first thing to notice is that it extends FrameLayout. The reason for this is because, as we shall see, we’re going to use this as a Fragment container and use FragmentTransaction to switch between different Fragments which represent the individual slides in the presentation. We’ll see more of this later on, but FrameLayout is particularly well suited to the role of Fragment container and is relatively lightweight, which should help performance.

The fields are pretty straightforward:

  • String TAG
    A tag used for logging.
  • List slides
    A list of the slides in this presentation.
  • int current
    The index of the currently displayed slide.
  • SlideLayout currentSlide
    The current SlideLayout (more on this later).
  • FragmentManager fragMgr
    The FragmentManager that we’ll use to handle our Fragments and transitions.
  • int inAnim
    A default animation ID representing an animation to apply to an incoming slide.
  • int outAnim
    A default animation ID representing an animation to apply to an outgoing slide.

The constructor initialises three of these: inAnim, outAnim, and slides. The first two are simply loaded from the layout attributes from the XML file (see my post on Custom Controls for more information on obtaining attributes from the layout XML file). slides is initialised inside a new Thread as we shouldn’t be performing file I/O on the UI thread. Al we are doing here is reading a JSON file which contains a list of the slides in the presentation. The JSON file is stored in the assets folder, and its name is obtained from the attribute named slides in the layout XML. The file itself is simply a JSONArray of strings containing the fully qualified path to the individual layout resources representing each slide in the presentation:

[code] [
“com.stylingandroid.presenterlite:layout/title”,
“com.stylingandroid.presenterlite:layout/outline”,
“com.stylingandroid.presenterlite:layout/framelayout”,
“com.stylingandroid.presenterlite:layout/linearlayout”,
“com.stylingandroid.presenterlite:layout/relativelayout”,
.
.
.
“com.stylingandroid.presenterlite:layout/end”
] [/code]

Inside the thread in the constructor we are simply loading these strings in to our slides list. Finally, once these are loaded we make a call to our go() method which is the method that switches to another slide within the presentation:

[java] public void go( int pos, boolean animate )
{
int id = getResources().getIdentifier( slides.get( pos ), null, null );
SlideFragment slide = null;
if( id >= 0 )
{
slide = new SlideFragment( id );
}
if ( slide != null )
{
FragmentTransaction tx = fragMgr.beginTransaction();
if ( animate )
{
int in = inAnim;
int out = outAnim;
if ( currentSlide != null )
{
if ( currentSlide.getInAnimation() > 0 )
{
in = currentSlide.getInAnimation();
}
if ( currentSlide.getOutAnimation() > 0 )
{
out = currentSlide.getOutAnimation();
}
}
if ( in > 0 && out > 0 )
{
tx.setCustomAnimations( in, out );
}
}
tx.replace( getId(), slide );
tx.commit();
current = pos;
}
}
[/java]

go takes two arguments: an int representing the index of the new slide to display; and a boolean which determines whether we should apply an animation for the transition between the current slide and the new slide.

The first thing that it does is load the new slide as a SlideFragment which is a custom Fragment which we’ll look at in due course. Then we begin a fragment transation to replace the current fragment for the current layout (which is essentially a FrameLayout, remember) with the new one. If the animate boolean is set then we’ll try and get any animations defined by the slide itself, or else default to the animations defined locally, and set these transitions on the fragment transaction. Next we’ll call replace on the FragmentTransaction to tell it to repalce the current Fragment with the new one. Next we call commit to set everything in motion, and finally we set current to the index of the new slide.

Using FragmentTransation to switch between fragments is really useful. You can see how easy it is, and applying custom transitions is simplicity itself. Later on in the series we’ll look at some of the animations that we can apply here.

All that remains are some convenience methods which set the current SlideLayout, and perform some basic navigation function such as moving to the next or previous slide:

[java] public void setCurrentSlide( SlideLayout slide )
{
this.currentSlide = slide;
}

public void next()
{
next( false );
}

public void next( boolean animate )
{
if ( current < slides.size() - 1 ) { go( current + 1, animate ); } } public void previous() { if ( current > 0 )
{
go( current – 1, false );
}
}

public void advance()
{
if ( currentSlide.hasMorePhases() )
{
currentSlide.nextPhase();
}
else
{
next( true );
}
}
[/java]

These should be pretty self-explanatory with the exception of the advance method. I wanted to be able to support slides which have multiple “display phases”. Consider a slide which consists of a number bullet points, and I wish the individual points to appear gradually as I’m speaking about them (and when I click to advance). While I could easily achieve this by having a series of static slides, and simply transitioning between them, there were some more advanced use-cases (of which we’ll see more later) where it made sense to manage this from within a slide. To achieve this, I implemented the concept of a multi-phase display of individual child elements within the slide by attaching those children to specific phases to control when they are visible.

The advance method interrogates the current slide to see if it has more phases, if it does, then it advances the phase, otherwise it moves on to the next slide.

At the moment, the currentSlide field is not being set. In the next article we’ll look at the SlideFragment class, which is responsible for setting this, and then look at SlideLayout to see how we go about implementing these multi-phase slides, and applying transition animations phase changes.

The source code for this article can be found here.

© 2011, Mark Allison. All rights reserved.

Copyright © 2011 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

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