Card Panes

Card panes are the most basic of the navigation components. They present a simple "card stack" of components, only one of which is visible at a time.

Unlike other navigation components, card panes don't include any "chrome" or intrinsic means of navigation - all selection management must be performed programmatically by the caller. While this imposes more responsibility on the developer, it is also the most flexible, giving the developer complete control over the navigation experience.

By default, the preferred size of a card pane is the maximum of the preferred widths and heights of the cards, and each card is sized to fill the entire space. However, card panes can also dynamically resize to match the preferred size of the currently selected card. Additionally, a transition effect can be applied to the selection change event, providing a more visually engaging experience when navigating between cards. CardPane currently supports the following transition effects:

  • Crossfade
  • Horizontal slide ("wipe" left/right)
  • Vertical slide ("wipe" up/down)
  • Horizonal Flip
  • Vertical Flip
  • Zoom

Note that, by default, "directional" transitions (such as slide and zoom) will appear to move forward when transitioning from a lower card index to a higher card index, and vice versa. This means that, when moving from the last card to the first card, for example, the transition will appear to move forward rather than backward. Since this may not always be the desired user experience, card panes can also be specified as circular, preserving the apparent direction when the selection wraps.

The following application demonstrates the behavior of CardPane:

The BXML source for the application is shown below. A set of buttons is provided to allow the user to control the styles that affect the card pane's presentation; specifically, "sizeToSelection" and "selectionChangeEffect". The card pane itself is contained within a FlowPane so that its response to changes in these styles is visible:

            
            <navigation:CardPanes title="Card Panes" maximized="true"
                xmlns:bxml="http://pivot.apache.org/bxml"
                xmlns:navigation="org.apache.pivot.tutorials.navigation"
                xmlns="org.apache.pivot.wtk">
                <TablePane styles="{padding:8, horizontalSpacing:6}">
                    <columns>
                        <TablePane.Column width="1*"/>
                        <TablePane.Column/>
                    </columns>

                    <TablePane.Row height="1*">
                        <Border styles="{padding:12}">
                            <TablePane styles="{verticalSpacing:6}">
                                <columns>
                                    <TablePane.Column width="1*"/>
                                </columns>

                                <TablePane.Row height="1*">
                                    <BoxPane styles="{horizontalAlignment:'center', verticalAlignment:'center',
                                        backgroundColor:'#cccccc'}">
                                        <Border styles="{padding:6}">
                                            <CardPane bxml:id="cardPane">
                                                <ImageView image="/org/apache/pivot/tutorials/IMG_0725_2.jpg"/>
                                                <ImageView image="/org/apache/pivot/tutorials/IMG_0735_2.jpg"/>
                                                <ImageView image="/org/apache/pivot/tutorials/IMG_0767_2.jpg"/>
                                            </CardPane>
                                        </Border>
                                    </BoxPane>
                                </TablePane.Row>

                                <TablePane.Row>
                                    <Separator/>
                                </TablePane.Row>

                                <TablePane.Row>
                                    <BoxPane styles="{horizontalAlignment:'center'}">
                                        <LinkButton bxml:id="previousButton" buttonData="Previous"/>
                                        <LinkButton bxml:id="nextButton" buttonData="Next"/>
                                    </BoxPane>
                                </TablePane.Row>
                            </TablePane>
                        </Border>

                        <Border styles="{padding:2}">
                            <BoxPane orientation="vertical" styles="{padding:4, spacing:6}">
                                <Checkbox bxml:id="sizeToSelectionCheckbox" buttonData="Size to selection"/>
                                <Label text="Selection change effect:"/>

                                <bxml:define>
                                    <ButtonGroup bxml:id="selectionChangeEffect"/>
                                </bxml:define>

                                <RadioButton bxml:id="crossfadeRadioButton" buttonData="Crossfade" selected="true"
                                    buttonGroup="$selectionChangeEffect"/>
                                <RadioButton bxml:id="horizontalSlideRadioButton" buttonData="Horizontal Slide"
                                    buttonGroup="$selectionChangeEffect"/>
                                <RadioButton bxml:id="verticalSlideRadioButton" buttonData="Vertical Slide"
                                    buttonGroup="$selectionChangeEffect"/>
                                <RadioButton bxml:id="horizontalFlipRadioButton" buttonData="Horizontal Flip"
                                    buttonGroup="$selectionChangeEffect"/>
                                <RadioButton bxml:id="verticalFlipRadioButton" buttonData="Vertical Flip"
                                    buttonGroup="$selectionChangeEffect"/>
                                <RadioButton bxml:id="zoomRadioButton" buttonData="Zoom"
                                    buttonGroup="$selectionChangeEffect"/>
                                <RadioButton bxml:id="noneRadioButton" buttonData="None"
                                    buttonGroup="$selectionChangeEffect"/>
                            </BoxPane>
                        </Border>
                    </TablePane.Row>
                </TablePane>
            </navigation:CardPanes>
            
        

The Java source is as follows. The updateCardPane() method applies the appropriate styles to the card pane based on the button state:

            
            package org.apache.pivot.tutorials.navigation;

            import java.net.URL;

            import org.apache.pivot.beans.Bindable;
            import org.apache.pivot.collections.Map;
            import org.apache.pivot.util.Resources;
            import org.apache.pivot.wtk.Button;
            import org.apache.pivot.wtk.ButtonPressListener;
            import org.apache.pivot.wtk.ButtonStateListener;
            import org.apache.pivot.wtk.CardPane;
            import org.apache.pivot.wtk.CardPaneListener;
            import org.apache.pivot.wtk.Checkbox;
            import org.apache.pivot.wtk.LinkButton;
            import org.apache.pivot.wtk.RadioButton;
            import org.apache.pivot.wtk.Window;
            import org.apache.pivot.wtk.skin.CardPaneSkin;

            public class CardPanes extends Window implements Bindable {
                private CardPane cardPane = null;
                private LinkButton previousButton = null;
                private LinkButton nextButton = null;
                private Checkbox sizeToSelectionCheckbox = null;
                private RadioButton crossfadeRadioButton = null;
                private RadioButton horizontalSlideRadioButton = null;
                private RadioButton verticalSlideRadioButton = null;
                private RadioButton horizontalFlipRadioButton = null;
                private RadioButton verticalFlipRadioButton = null;
                private RadioButton zoomRadioButton = null;
                private RadioButton noneRadioButton = null;

                @Override
                public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
                    cardPane = (CardPane)namespace.get("cardPane");
                    previousButton = (LinkButton)namespace.get("previousButton");
                    nextButton = (LinkButton)namespace.get("nextButton");
                    sizeToSelectionCheckbox = (Checkbox)namespace.get("sizeToSelectionCheckbox");

                    crossfadeRadioButton = (RadioButton)namespace.get("crossfadeRadioButton");
                    horizontalSlideRadioButton = (RadioButton)namespace.get("horizontalSlideRadioButton");
                    verticalSlideRadioButton = (RadioButton)namespace.get("verticalSlideRadioButton");
                    horizontalFlipRadioButton = (RadioButton)namespace.get("horizontalFlipRadioButton");
                    verticalFlipRadioButton = (RadioButton)namespace.get("verticalFlipRadioButton");
                    zoomRadioButton = (RadioButton)namespace.get("zoomRadioButton");
                    noneRadioButton = (RadioButton)namespace.get("noneRadioButton");

                    cardPane.getCardPaneListeners().add(new CardPaneListener.Adapter() {
                        @Override
                        public void selectedIndexChanged(CardPane cardPane, int previousSelectedIndex) {
                            updateLinkButtonState();
                        }
                    });

                    previousButton.getButtonPressListeners().add(new ButtonPressListener() {
                        @Override
                        public void buttonPressed(Button button) {
                            cardPane.setSelectedIndex(cardPane.getSelectedIndex() - 1);
                        }
                    });

                    nextButton.getButtonPressListeners().add(new ButtonPressListener() {
                        @Override
                        public void buttonPressed(Button button) {
                            cardPane.setSelectedIndex(cardPane.getSelectedIndex() + 1);
                        }
                    });

                    ButtonStateListener checkboxStateListener = new ButtonStateListener() {
                        @Override
                        public void stateChanged(Button button, Button.State previousState) {
                            updateCardPane();
                        }
                    };

                    sizeToSelectionCheckbox.getButtonStateListeners().add(checkboxStateListener);

                    ButtonStateListener radioButtonStateListener = new ButtonStateListener() {
                        @Override
                        public void stateChanged(Button button, Button.State previousState) {
                            if (button.isSelected()) {
                                updateCardPane();
                            }
                        }
                    };

                    crossfadeRadioButton.getButtonStateListeners().add(radioButtonStateListener);
                    horizontalSlideRadioButton.getButtonStateListeners().add(radioButtonStateListener);
                    verticalSlideRadioButton.getButtonStateListeners().add(radioButtonStateListener);
                    horizontalFlipRadioButton.getButtonStateListeners().add(radioButtonStateListener);
                    verticalFlipRadioButton.getButtonStateListeners().add(radioButtonStateListener);
                    zoomRadioButton.getButtonStateListeners().add(radioButtonStateListener);
                    noneRadioButton.getButtonStateListeners().add(radioButtonStateListener);

                    updateCardPane();
                    updateLinkButtonState();
                }

                private void updateCardPane() {
                    cardPane.getStyles().put("sizeToSelection", sizeToSelectionCheckbox.isSelected());

                    if (crossfadeRadioButton.isSelected()) {
                        cardPane.getStyles().put("selectionChangeEffect",
                            CardPaneSkin.SelectionChangeEffect.CROSSFADE);
                    } else if (horizontalSlideRadioButton.isSelected()) {
                        cardPane.getStyles().put("selectionChangeEffect",
                            CardPaneSkin.SelectionChangeEffect.HORIZONTAL_SLIDE);
                    } else if (verticalSlideRadioButton.isSelected()) {
                        cardPane.getStyles().put("selectionChangeEffect",
                            CardPaneSkin.SelectionChangeEffect.VERTICAL_SLIDE);
                    } else if (horizontalFlipRadioButton.isSelected()) {
                        cardPane.getStyles().put("selectionChangeEffect",
                            CardPaneSkin.SelectionChangeEffect.HORIZONTAL_FLIP);
                    } else if (verticalFlipRadioButton.isSelected()) {
                        cardPane.getStyles().put("selectionChangeEffect",
                            CardPaneSkin.SelectionChangeEffect.VERTICAL_FLIP);
                    } else if (zoomRadioButton.isSelected()) {
                        cardPane.getStyles().put("selectionChangeEffect",
                            CardPaneSkin.SelectionChangeEffect.ZOOM);
                    } else {
                        cardPane.getStyles().put("selectionChangeEffect", null);
                    }
                }

                private void updateLinkButtonState() {
                    int selectedIndex = cardPane.getSelectedIndex();
                    previousButton.setEnabled(selectedIndex > 0);
                    nextButton.setEnabled(selectedIndex < cardPane.getLength() - 1);
                }
            }
            
        

Next: Tab Panes