Activity & fragment lifecycle events

1. Start new activity

Activity, onCreate
Fragment, onAttach
Fragment, onAttach
Fragment, onCreateView
Fragment, onActivityCreated
Fragment, onStart
Activity, onStart
Activity, onResume
Fragment, onResume

2. Close activity (tap back button)

Fragment, onPause
Activity, onPause
Activity, onStop
Fragment, onDestroyView
Fragment, onDestroy
Fragment, onDetach
Activity, onDestroy

3. Hide activity (tap home button)

Fragment, onPause
Activity, onPause
Activity, onStop

4. Resume activity (from home screen)

Activity, onRestart
Fragment, onStart
Activity, onStart
Activity, onResume
Fragment, onResume

5. Go to other activity

Fragment, onPause
Activity, onPause
Activity, onStop

6. Resume from other activity (return back)

Activity, onRestart
Fragment, onStart
Activity, onStart
Activity, onResume
Fragment, onResume

Android BOOT_COMPLETED handler module

This Titanium Android module provide a place where you can run your JS script when the device receives BOOT_COMPLETED broadcast intent. By simply adding boot_completed.js into your Resources folder, this JS file will be run when the broadcast intent received.

Continue reading Android BOOT_COMPLETED handler module

Android Progress Notification module

Android Progress Notification module is a module to display notification in Android notification area which contains progress bar. In addition, it also support setting the number of notification, useful if you have a number of grouped notifications.

nc_progress_notif_1 nc_progress_notif_2

Continue reading Android Progress Notification module

Titanium app hex color value with alpha channel

Color values are represented in hexadecimal value:

#000000

  • 0 – red, 0 – green, 0 – blue
  • color = black

#ffffff

  • 255 – red, 255 – green, 255 – blue
  • color = white

‘ff’ in hexadecimal is 255 in decimal, (2 ^ 8) – 1 = 255.

In CSS, to represent color with alpha channel, we can use the rgba() syntax:

rgba(0, 0, 0, 0.6)

  • 0 – red, 0 – green, 0 – blue
  • black with 60% opacity

In Titanium, rgba() syntax only available on iOS, but hex value also can be used to represent alpha channel, and it supported by both iOS & Android

#ff000000

  • 0 – red, 0 – green, 0 – blue
  • ff – 255 / 100% opacity

#90ffcc00

  • 255 – red, 204 – green, 0 – blue
  • 90 – 144 / 56% opacity

To easily maintain colors in app, this function is to transform rgba value into Titanium hex color format

function rgbaToHex(r, g, b, a) {
    var toHex = function(n) {
        return ('00' + (n | 0).toString(16)).slice(-2);
    };
    return '#' + toHex(((a * 100) / 100) * 255) + toHex(r) + toHex(g) + toHex (b);
}

Explanation:

  • (n | 0) is shortcut for parseInt(), to ensure the value passed in is integer. We don’t want the hex color value to have decimal point, e.g: #2.4ccc
  • .toString(16) is Number object function (not Object.toString()) to convert number into hexadecimal format
  • ('00' + value).slice(-2) is to add string padding to the left of the string, so that it will always have 2 characters. We don’t want the value to have one character, #0ccff is invalid value

Usage:

var view = Ti.UI.createView({
    backgroundColor: rgbaToHex(255, 204, 0, 0.5)
});

ActionBarImplBase can only be used with a compatible window decor layout

ActionBarImplBase can only be used with a compatible window decor layout

This error occured in android 2.3 if you’re trying to use ActionBar feature on theme that disable ActionBar

Let’s say you define a style that enable actionbar overlay mode

<style name="MyTheme" parent="Theme.AppCompat">
     <item name="android:windowActionBarOverlay">true</item>
     <item name="windowActionBarOverlay">true</item>
</style>

Then one style extends this base style, but disable the actionbar

<style name="MyDifferentTheme" parent="MyTheme">
     <item name="android:windowActionBar">false</item>
     <item name="windowActionBar">false</item>
</style>

This will cause the crash. To fix it, the new style either need to extend different parent style, or disable the actionbar feature

<style name="MyDifferentTheme" parent="MyTheme">
     <item name="android:windowActionBar">false</item>
     <item name="windowActionBar">false</item>
     <item name="android:windowActionBarOverlay">false</item>
     <item name="windowActionBarOverlay">false</item>
</style>

ScrollableView in ListView

Using ListView is all about mapping collection data structure to UI structure. What if we have a UI structure that have nested collection in a single ListItem, such as a ScrollableView? We have to make sure our data structure can map to the ScrollableView.views property.

News (data) Ti.UI
previews[] ScrollableView.views
title Label.text
timestamp Label.text

The solution: we have to create each child view when setting items to ListView. Here’s how:

var win = Ti.UI.createWindow({
    exitOnClose: true,
    title: 'ScrollableView in ListView'
});

var newsTemplate = {
    childTemplates: [
        {
            type: 'Ti.UI.View',
            properties: {
                width: 300,
                backgroundColor: '#fff',
                borderColor: '#ccc',
                layout: 'vertical',
                top: 10
            },
            childTemplates: [
                {
                    type: 'Ti.UI.ScrollableView',
                    bindId: 'previews',
                    properties: {
                        width: 300,
                        height: 150
                    }
                },
                {
                    type: 'Ti.UI.Label',
                    bindId: 'title',
                    properties: {
                        color: '#333',
                        width: Ti.UI.FILL,
                        textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT,
                        left: 5,
                        right: 5,
                        wordWrap: false,
                        ellipsize: true,
                        font: {
                            fontSize: '14dp',
                            fontWeight: 'bold'
                        }
                    }
                },
                {
                    type: 'Ti.UI.Label',
                    bindId: 'published',
                    properties: {
                        color: '#aaa',
                        width: Ti.UI.FILL,
                        textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT,
                        left: 5,
                        right: 5,
                        wordWrap: false,
                        ellipsize: true,
                        font: {
                            fontSize: '12dp'
                        }
                    }
                }
            ]
        }
    ]
};

var sect = Ti.UI.createListSection();

var listView = Ti.UI.createListView({
    templates: {
        news: newsTemplate,
        foot: {
            properties: {
                height: 10
            }
        }
    },
    backgroundColor: '#eee',
    separatorColor: '#eee',
    sections: [sect]
});
win.add(listView);

var data = [
    {
        title: 'News A',
        timestamp: 1411789472624,
        previews: [
            'http://placehold.it/300x150/0099cc/ffffff',
            'http://placehold.it/300x150/9933cc/ffffff',
            'http://placehold.it/300x150/669900/ffffff',
            'http://placehold.it/300x150/ff8800/ffffff'
        ]
    },
    {
        title: 'News B',
        timestamp: 1411443872624,
        previews: [
            'http://placehold.it/300x150/cc0000/ffffff',
            'http://placehold.it/300x150/33b5e5/ffffff',
            'http://placehold.it/300x150/aa66cc/ffffff'
        ]
    },
    {
        title: 'News C',
        timestamp: 1411184672624,
        previews: [
            'http://placehold.it/300x150/99cc00/ffffff',
            'http://placehold.it/300x150/ffbb33/ffffff',
            'http://placehold.it/300x150/ff4444/ffffff',
            'http://placehold.it/300x150/0099cc/ffffff',
            'http://placehold.it/300x150/9933cc/ffffff',
        ]
    }
];

win.addEventListener('open', function() {
    var items = [];

    var getDate = function(timestamp) {
        var d = new Date(timestamp);
        var m = d.getMonth() + 1;
        if (m > 12) {
            m = 1;
        }
        return d.getDate() +'/'+ m +'/'+ d.getFullYear();
    };

    for (var i = 0; i < data.length; i++) {
        var d = data[i];

        var views = [];
        for (var j = 0; j < d.previews.length; j++) {
            views.push(Ti.UI.createImageView({
                image: d.previews[j],
                width: 300,
                height: 150
            }));
        }

        items.push({
            template: 'news',
            previews: {
                views: views
            },
            title: {
                text: d.title
            },
            published: {
                text: 'Published on '+ getDate(d.timestamp)
            }
        });
    }

    items.push({
        template: 'foot'
    });

    sect.setItems(items);
});

win.open();

Output:
scrollableview-in-listview