Creating the User Interface

Create the user interface for the SUP101Sample application.

Note: This procedure includes code snippets you can copy and paste for your project. Click SUP_BB_Custom_Dev_Tutorial_code.zip to access the text files that include the code snippets for the CustomerSample, CustomerList, and CustomerSampleScreen java files.

If you are viewing this guide as a PDF, you can locate the referenced document at http://infocenter.sybase.com/. Go to Sybase Unwired Platform 1.5.2 > Tutorial: BlackBerry Application Development using Custom Development to launch this PDF.

  1. In the Java perspective, right-click SUP101Sample, and select New > Package.
  2. In the New Java Package dialog, in Name, enter com.sybase.sup.samples.objectapi and click Finish.
  3. Right-click the com.sybase.sup.samples.objectapi package and select New > Class.
  4. In the New Java Class wizard, in Name, enter CustomerSample, and click Finish.

    bb_new_java_class
  5. In the CustomerSample.java file, enter this code after the package com.sybase.sup.samples.objectapi; line:
    This creates the main Customer application.
    import java.util.Vector;
    
    import net.rim.device.api.system.Characters;
    import net.rim.device.api.ui.Graphics;
    import net.rim.device.api.ui.UiApplication;
    import net.rim.device.api.ui.XYRect;
    import net.rim.device.api.ui.component.BasicEditField;
    import net.rim.device.api.ui.component.KeywordFilterField;
    import SUP101.Customer;
    import SUP101.SUP101DB;
    
    import com.sybase.collections.ObjectList;
    
    /**
     * @author bdeng
     * 
     * Customer sample main application.
     * 
     * Note: For the sake of simplicity, this sample application may not leverage
     * resource bundles and resource strings.  However, it is STRONGLY recommended
     * that application developers make use of the localization features available
     * within the BlackBerry development platform to ensure a seamless application
     * experience across a variety of languages and geographies.  For more information
     * on localizing your application, please refer to the BlackBerry Java Development
     * Environment Development Guide.
     */
    
    public final class CustomerSample extends UiApplication
    {
        private KeywordFilterField _keywordFilterField;
        private CustomerList       _customerList;
        private Vector             _customers;
    
        // sync lock for synchronization
        private static Object      _syncLock = new Object();
    
        /**
         * Entry point for application.
         * 
         * @param args
         *            Command line arguments (not used).
         */
        public static void main(String[] args)
        {
            // login to sync
            SUP101DB.loginToSync("supAdmin", "s3pAdmin");
    
            // Create a new instance of the application
            CustomerSample app = new CustomerSample();
    
            // Make the currently running thread the application's event
            // dispatch thread and begin processing events.
            app.enterEventDispatcher();
        }
    
        /**
         * Constructor
         */
        public CustomerSample()
        {
            // Create an instance of KeywordFilterField class.
            _keywordFilterField = new KeywordFilterField();
    
            // Refresh the data
            refreshData();
    
            // Add our list to a KeywordFilterField object.
            _keywordFilterField.setSourceList(_customerList, _customerList);
    
            // We're providing a customized edit field for
            // the KeywordFilterField.
            CustomKeywordField customSearchField = new CustomKeywordField();
            _keywordFilterField.setKeywordField(customSearchField);
    
            // Create main screen.
            CustomerSampleScreen screen = new CustomerSampleScreen(this);
    
            // We need to explicitly add the search/title field via
            // MainScreen.setTitle().
            screen.setTitle(_keywordFilterField.getKeywordField());
    
            // Add our KeywordFilterField to the screen and push the screen
            // onto the stack.
            screen.add(_keywordFilterField);
            pushScreen(screen);
    
        }
    
        /**
         * Refreshes the data.
         */
        public void refreshData()
        {
            // Populate vector with data.
            if ( _customers != null )
            {
                _customers.removeAllElements();
                _customers = null;
            }
    
            _customers = loadData();
    
            // Create an instance of our SortedReadableList class.
            if ( _customerList == null )
            {
                _customerList = new CustomerList();
            }
    
            _customerList.loadFrom(_customers.elements());
        }
    
        /**
         * Method to access the KeywordFilterField object.
         * 
         * @return This applications KeywordFilterField.
         */
        KeywordFilterField getKeywordFilterField()
        {
            return _keywordFilterField;
        }
    
        /**
         * Method populates and returns a vector of Customer objects containing data
         * read from data source.
         * 
         * @return A vector containing Customer objects.
         */
        private Vector loadData()
        {
            ObjectList customers = Customer.findAll();
            Vector customerVec = new Vector(customers.size());
    
            int size = customers.count();
            for (int i = 0; i < size; i++)
            {
                customerVec.addElement(customers.elementAt(i));
            }
    
            return customerVec;
        }
    
        /**
         * Returns the sync lock object
         * 
         * @return Object
         */
        public Object getSyncLock()
        {
            return _syncLock;
        }
    
        /**
         * Inner Class: A custom keyword input field for the KeywordFilterField. We
         * want to prevent a save dialog from being presented to the user when
         * exiting the application as the ability to persist data is not relevent to
         * this application. We are also using the paint() method to customize the
         * appearance of the cursor in the input field.
         */
        final static class CustomKeywordField extends BasicEditField
        {
            // Contructor
            CustomKeywordField()
            {
                // Custom style.
                super(USE_ALL_WIDTH | NON_FOCUSABLE | NO_LEARNING | NO_NEWLINE);
    
                setLabel("Search: ");
            }
    
            /**
             * Intercepts ESCAPE key.
             * 
             * @see net.rim.device.api.ui.component.TextField#keyChar(char,int,int)
             */
            protected boolean keyChar(char ch, int status, int time)
            {
                switch (ch)
                {
                    case Characters.ESCAPE:
                        // Clear keyword.
                        if ( super.getTextLength() > 0 )
                        {
                            setText("");
                            return true;
                        }
                }
                return super.keyChar(ch, status, time);
            }
    
            /**
             * Overriding super to add custom painting to our class.
             * 
             * @see net.rim.device.api.ui.Field#paint(Graphics)
             */
            protected void paint(Graphics graphics)
            {
                super.paint(graphics);
    
                // Draw caret.
                getFocusRect(new XYRect());
                drawFocus(graphics, true);
            }
        }
    }
    
  6. Save the CustomerSample.java file.
  7. In the Java perspective, right-click the com.sybase.sup.samples.objectapi package, and select New > Class.
  8. In the New Java Class wizard, in Name, enter CustomerList, and click Finish.
  9. In the CustomerList.java file, enter this code after the package com.sybase.sup.samples.objectapi; line:
    import net.rim.device.api.collection.util.SortedReadableList;
    import net.rim.device.api.ui.component.KeywordProvider;
    import net.rim.device.api.util.Comparator;
    import net.rim.device.api.util.StringUtilities;
    import SUP101.Customer;
    
    public class CustomerList extends SortedReadableList
            implements KeywordProvider
    {
        /**
         * Creates a customer list based on a Vector of customers.
         */
        public CustomerList()
        {
            super(new CustomerListComparator());
        }
    
        /**
         * @see net.rim.device.api.ui.component.KeywordProvider#getKeywords(Object
         *      element)
         */
        public String[] getKeywords(Object element)
        {
            if ( element instanceof Customer )
            {
                Customer customer = (Customer) element;
                return StringUtilities.stringToWords(customer.getFname() + " " + customer.getLname());
            }
            return null;
        }
    
        /**
         * A Comparator class used for sorting our Customer objects by name.
         */
        final static class CustomerListComparator
                implements Comparator
        {
            /**
             * Compares two customers by comparing their names' in alphabetical
             * order.
             * 
             * @see net.rim.device.api.util.Comparator#compare(Object, Object)
             */
            public int compare(Object o1, Object o2)
            {
                if ( o1 == null || o2 == null ) throw new IllegalArgumentException("Cannot compare null customers");
    
                return o1.toString().compareTo(o2.toString());
            }
        }
    }
    
  10. Save the CustomerList.java file.
  11. In the Java perspective, right-click the com.sybase.sup.samples.objectapi package, and select New > Class.
  12. In the New Java Class wizard, in Name, enter CustomerSampleScreen, and click Finish.
  13. In the CustomerSampleScreen.java file, enter this code after the package com.sybase.sup.samples.objectapi; line:
    import net.rim.device.api.system.Characters;
    import net.rim.device.api.ui.Field;
    import net.rim.device.api.ui.Font;
    import net.rim.device.api.ui.Graphics;
    import net.rim.device.api.ui.MenuItem;
    import net.rim.device.api.ui.UiApplication;
    import net.rim.device.api.ui.component.BasicEditField;
    import net.rim.device.api.ui.component.ButtonField;
    import net.rim.device.api.ui.component.GaugeField;
    import net.rim.device.api.ui.component.KeywordFilterField;
    import net.rim.device.api.ui.container.HorizontalFieldManager;
    import net.rim.device.api.ui.container.MainScreen;
    import SUP101.Customer;
    import SUP101.SUP101DB;
    
    import com.sybase.persistence.ObjectSyncStatusData;
    import com.sybase.persistence.SyncStatusListener;
    import com.sybase.persistence.SyncStatusState;
    
    /**
     * @author bdeng
     *
     * This class represents the main screen for the Customer Sample application.
     * 
     * Note: For the sake of simplicity, this sample application may not leverage
     * resource bundles and resource strings.  However, it is STRONGLY recommended
     * that application developers make use of the localization features available
     * within the BlackBerry development platform to ensure a seamless application
     * experience across a variety of languages and geographies.  For more information
     * on localizing your application, please refer to the BlackBerry Java Development
     * Environment Development Guide.
     */
    public final class CustomerSampleScreen extends MainScreen
            implements SyncStatusListener
    {
        private CustomerSample     _app;
        private KeywordFilterField _keywordFilterField;
    
        // last sync status
        private int                _lastSyncState = -1;
        // synchronization indicator
        private GaugeField         _syncProgressField;
        // if a synchronization is happening so that we display an indicator
        private boolean            _isSynchronizing;
        // whether there are pending changes
        private boolean            _changed;
    
        /**
         * Creates a new CustomerSampleScreen.
         * 
         * @param app
         *            The UiApplication creating an instance of this class.
         */
        public CustomerSampleScreen(CustomerSample app)
        {
            // A reference to the UiApplication instance for use in this class.
            _app = app;
    
            // We need a reference to the UiApplication's KeywordFilterField.
            _keywordFilterField = _app.getKeywordFilterField();
    
            // Add menu item to the screen's menu.
            addMenuItem(syncItem);
    
            if ( !SUP101DB.isSynchronized("default") )
            {
                synchronize();
            }
        }
    
        /**
         * Intercepts the ENTER key and displays info screen.
         * 
         * @see net.rim.device.api.ui.Screen#keyChar(char,int,int)
         */
        protected boolean keyChar(char key, int status, int time)
        {
            if ( key == Characters.ENTER )
            {
                displayInfoScreen();
                return true; // We've consumed the event.
            }
            return super.keyChar(key, status, time);
        }
    
        /**
         * Handles a trackball click.
         * 
         * @see net.rim.device.api.ui.Screen#invokeAction(int)
         */
        public boolean invokeAction(int action)
        {
            switch (action)
            {
                case ACTION_INVOKE: // Trackball click.
                    displayInfoScreen();
                    return true; // We've consumed the event.
            }
            return super.invokeAction(action);
        }
    
        /**
         * Creates an InfoScreen instance and pushes it onto the stack for
         * rendering.
         */
        private void displayInfoScreen()
        {
            // Retrieve the selected Customer and use it to invoke a new InfoScreen.
            Customer customer = (Customer) _keywordFilterField.getSelectedElement();
            if ( customer != null )
            {
                InfoScreen infoScreen = new InfoScreen(this, customer);
                _app.pushScreen(infoScreen);
            }
        }
    
        /**
         * Prevent the save dialog from being displayed.
         * 
         * @see net.rim.device.api.ui.container.MainScreen#onSavePrompt()
         */
        public boolean onSavePrompt()
        {
            return true;
        }
    
        // Inner classes------------------------------------------------------------
        /**
         * Synchronizes the mbos
         */
        private final MenuItem syncItem = new MenuItem("Synchronize", 0, 0)
                                        {
    
                                            public void run()
                                            {
                                                synchronize();
                                            }
                                        };
    
        /**
         * A MainScreen class to display/update secondary information for a selected
         * customer.
         */
        private final static class InfoScreen extends MainScreen
        {
            private CustomerSampleScreen _sampleScreen;
            private Customer             _customer;
    
            private BasicEditField       _fnameField;
            private BasicEditField       _lnameField;
            private BasicEditField       _companyField;
            private BasicEditField       _addressField;
            private BasicEditField       _stateField;
            private BasicEditField       _cityField;
            private BasicEditField       _phoneField;
            private BasicEditField       _zipField;
    
            /**
             * Constructs a screen to display
             * 
             * @param customer
             *            The customer to display secondary information about
             */
            InfoScreen(CustomerSampleScreen sampleScreen, Customer customer)
            {
                _sampleScreen = sampleScreen;
                _customer = customer;
    
                // Set up and display UI elements.
                setTitle("Update Customer");
                _fnameField = new BasicEditField("First name: ", customer.getFname(), BasicEditField.DEFAULT_MAXCHARS,
                        Field.FOCUSABLE);
                _lnameField = new BasicEditField("Last name: ", customer.getLname(), BasicEditField.DEFAULT_MAXCHARS,
                        Field.FOCUSABLE);
                _companyField = new BasicEditField("Company: ", customer.getCompany_name(),
                        BasicEditField.DEFAULT_MAXCHARS, Field.FOCUSABLE);
                _addressField = new BasicEditField("Address: ", customer.getAddress(), BasicEditField.DEFAULT_MAXCHARS,
                        Field.FOCUSABLE);
                _stateField = new BasicEditField("State: ", customer.getState(), BasicEditField.DEFAULT_MAXCHARS,
                        Field.FOCUSABLE);
                _cityField = new BasicEditField("City: ", customer.getCity(), BasicEditField.DEFAULT_MAXCHARS,
                        Field.FOCUSABLE);
                _phoneField = new BasicEditField("Phone: ", customer.getPhone(), BasicEditField.DEFAULT_MAXCHARS,
                        Field.FOCUSABLE);
                _zipField = new BasicEditField("Zip: ", customer.getZip(), BasicEditField.DEFAULT_MAXCHARS, Field.FOCUSABLE);
                add(_fnameField);
                add(_lnameField);
                add(_companyField);
                add(_addressField);
                add(_stateField);
                add(_cityField);
                add(_phoneField);
                add(_zipField);
    
                HorizontalFieldManager hfm = new HorizontalFieldManager(Field.FIELD_HCENTER);
    
                ButtonField submitButton = new ButtonField("submit", Field.FIELD_RIGHT)
                {
                    protected boolean navigationClick(int status, int time)
                    {
                        submit();
                        return true;
                    }
                };
    
                ButtonField cancelButton = new ButtonField("cancel", Field.FIELD_LEFT)
                {
                    protected boolean navigationClick(int status, int time)
                    {
                        close();
                        return true;
                    }
                };
    
                hfm.add(submitButton);
                hfm.add(cancelButton);
    
                add(hfm);
            }
    
            protected void submit()
            {
                _customer.setFname(_fnameField.getText().trim());
                _customer.setLname(_lnameField.getText().trim());
                _customer.setCompany_name(_companyField.getText().trim());
                _customer.setAddress(_addressField.getText().trim());
                _customer.setState(_stateField.getText().trim());
                _customer.setCity(_cityField.getText().trim());
                _customer.setPhone(_phoneField.getText().trim());
                _customer.setZip(_zipField.getText().trim());
                _customer.save();
                _sampleScreen._changed = true;
                _sampleScreen._keywordFilterField.updateList();
                close();
            }
        }
    
        // synchronizes the mbo
        private void synchronize()
        {
            Runnable runnable = new Runnable()
            {
                public void run()
                {
                    synchronized (_app.getSyncLock())
                    {
                        setSynchronizing(true);
    
                        try
                        {
                            if ( _changed )
                            {
                                _changed = false;
                                SUP101DB.submitPendingOperations();
                            }
                            SUP101DB.synchronize(CustomerSampleScreen.this);
                        }
                        catch (Exception e)
                        {
    
                        }
                        finally
                        {
                            setSynchronizing(false);
                        }
                    }
                }
            };
            new Thread(runnable).start();
        }
    
        /**
         * Sets sync indicator and updates the sync status message.
         * 
         * @param isSynchronizing  whether synchronization is happending
         */
        public void setSynchronizing(boolean isSynchronizing)
        {
            this._isSynchronizing = isSynchronizing;
    
            if ( this._isSynchronizing )
            {
                _syncProgressField = new GaugeField("", SyncStatusState.SYNC_STARTING, SyncStatusState.SYNC_DONE, 0,
                        GaugeField.NO_TEXT | GaugeField.LABEL_AS_PROGRESS)
                {
                    protected void paint(Graphics g)
                    {
                        super.paint(g);
                        int indicatorHeigth = 20;
                        int fontSize = 12;
                        int y = getHeight() - indicatorHeigth;
    
                        // setting up font style
                        Font f = Font.getDefault().derive(Font.PLAIN, fontSize);
                        g.setFont(f);
                        g.setColor(0x00000000);
                        g.setDrawingStyle(Graphics.DRAWSTYLE_AALINES, true);
                        // paint the synchronizing indicator as it can't be
                        // displayed in the progress bar using
                        // LABEL_AS_PROGRESS style
                        g.drawText(getLabel(), 1, y + (indicatorHeigth - fontSize) / 2);
                        g.setColor(0x00BEBEBE);
                        g.drawRect(0, 0, getWidth(), getHeight());
                    }
                };
    
                _app.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        setStatus(_syncProgressField);
                    }
                });
            }
            else
            {
                _app.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        _syncProgressField = null;
                        setStatus(null);
                    }
                });
    
                _app.refreshData();
            }
        }
    
        /**
         * Called when there is information to report about the status of a
         * synchronization that is taking place.
         * 
         * @param data
         *            A {@link ObjectSyncStatusData} object that contains
         *            information about the status of the running synchronization.
         * @return Return true to cancel the synchronization or false to continue.
         */
        public boolean objectSyncStatus(ObjectSyncStatusData data)
        {
            if ( !_isSynchronizing )
            {
                return false;
            }
    
            final int syncState = data.getSyncStatusState();
    
            if ( _lastSyncState != syncState )
            {
                _lastSyncState = syncState;
    
                String syncMsg = null;
                if ( syncState == SyncStatusState.SYNC_STARTING )
                {
                    syncMsg = "Connecting...";
                }
                else if ( syncState == SyncStatusState.APPLICATION_DATA_UPLOADING )
                {
                    syncMsg = "Metadata Uploading...";
                }
                else if ( syncState == SyncStatusState.APPLICATION_DATA_DOWNLOADING )
                {
                    syncMsg = "Metadata Downloading...";
                }
                else if ( syncState == SyncStatusState.SYNC_DONE )
                {
                    syncMsg = "Completed!";
                }
    
                if ( syncMsg != null )
                {
                    final StringBuffer strLabel = new StringBuffer(256);
    
                    strLabel.append("Retrieving synchronization group:").append("default").append(" - ").append(syncMsg);
    
                    UiApplication.getUiApplication().invokeLater(new Runnable()
                    {
                        public void run()
                        {
                            _syncProgressField.setValue(syncState);
    
                            _syncProgressField.setLabel(strLabel.toString());
                        }
                    });
                }
            }
            return false;
        }
    }
    
  14. Save the CustomerSampleScreen.java file.