Dynamic Ticket Analysis UI using Data Binding in Open Event Android Orga App

Any event manager application has the responsibility to show the analytics about the event to the organiser and in Open Event Android Orga App (Github Repo), we wanted to achieve a way to display the analytics of total and sold tickets with the data present to us.
To analyse, we have a list of tickets, which are divided into 3 categories:

  • Free
  • Paid
  • Donation

Our goal was to show information about total tickets and the amount of sold tickets per category. This blog will focus on the dynamic UI creation for the ticket analysis component of the Event Details Dashboard using Android Layout Data Binding. By using Data Binding, we not only reduced the amount of Java Boilerplate code we would have to write, but also accomplished UI reuse in just XML which wouldn’t have been possible without it. You’ll see in a moment what I mean.

Properties

So first, we’d need to define some properties which will be bound in the UI. These properties are declared in the Event model and their type is ObservableLong provided by the Android DataBinding package. The reason why we are using these instead of primitives is because these fields being Observable, will update the UI as soon as they are updated, without requiring the programmer to set the View Property at all.

There are six fields, 3 for total tickets of each type and 3 for sold tickets

public final ObservableLong freeTickets = new ObservableLong();
public final ObservableLong paidTickets = new ObservableLong();
public final ObservableLong donationTickets = new ObservableLong();

public final ObservableLong soldFreeTickets = new ObservableLong();
public final ObservableLong soldPaidTickets = new ObservableLong();
public final ObservableLong soldDonationTickets = new ObservableLong();

Some more advantages we get from using these are the batch view update and the use of computed properties in UI. Imagine having a TextView display the amount of free tickets and a progress bar showing the percentage of free tickets sold. Traditionally, you’d have to set the text and compute the percentage and set the progress bar as the data changes, whereas you can just use the fields in layout as is in both TextView and ProgressBar with the computations required and they’ll work in harmony.

We have leveraged this feature to show the analytics component of tickets with a

  • Ticket Type
  • Circular Progress Bar
  • Sold Tickets
  • Total Tickets

All using the XML layout and databinding

Ticket Component

For each ticket component, we have 4 variables, namely

  • Ticket Type Name
  • Total Amount
  • Completed Amount (Sold Tickets)
  • Color

First 3 are fairly self explanatory, the color attribute we used in our component needs a little bit of description. We decided to give each ticket category its own color for circular progress bar for aesthetics. So, we need each component to have its own color attribute too. But this is not a normal android color ID or a hex. We needed 2 variants of the same color to show in the circular progress to discern the total and completed part. As we are using Material Color Palette, which has a color divided by intensities, we used 500 variant for completed portion and 100 (lighter) variant for the background of circular progress.

Let’s look at the layout now:

<data>
    <variable name="color" type="String" />
    <variable name="ticketName" type="String" />
    <variable name="total" type="long" />
    <variable name="completed" type="long" />
</data>

<LinearLayout
     android:orientation="vertical">
    <TextView
        android:text="@{ticketName}" />
    <FrameLayout>
        <com.mikhaellopez.circularprogressbar.CircularProgressBar
            app:circular_progress_color="@{color}"
            app:progress_with_animation="@{total == 0 ? 0 : (int) ((completed*100)/total)}" />

        <LinearLayout
            android:orientation="horizontal">

            <TextView
                android:text="@{completed}" />
            <TextView
                android:text='@{"/" + total}' />

        </LinearLayout>
    </FrameLayout>

    <TextView
        android:text='@{(total == 0 ? 0 : (int) ((completed*100)/total)) + "%"}' />

</LinearLayout>

Note: The layout snippet is not complete. Only attribute names to be discussed in the blog are shown for brevity

As you can see, after the data variable declarations, we have a CardView first showing the ticket name on top, and then we have a FrameLayout wrapping the circular progress and a textview showing the Sold/Total tickets.

Circular Progress Bar

Let’s discuss the circular progress first, we have used this library to create a circular progress bar, the two other attributes circular_progress_color and progress_with_animation are specific to Open Event Orga Application and we have created custom adapters for them:

@BindingAdapter("progress_with_animation")
public static void bindCircularProgress(CircularProgressBar circularProgressBar, int progress) {
    circularProgressBar.setProgressWithAnimation(progress, 500);
}

@BindingAdapter("circular_progress_color")
public static void bindCircularProgressColor(CircularProgressBar circularProgressBar, String colorName) {
    Context context = circularProgressBar.getContext();
    Resources resources = circularProgressBar.getResources();

    int color = ContextCompat.getColor(context, resources.getIdentifier(colorName + "_500", "color", context.getPackageName()));
    int bgColor = ContextCompat.getColor(context, resources.getIdentifier(colorName + "_100", "color", context.getPackageName()));

    circularProgressBar.setColor(color);
    circularProgressBar.setBackgroundColor(bgColor);
}
  • progress_with_animation sets the provided integer value as the progress of the circular progress bar with an animation of 500 ms
  • circular_progress_color finds the 100 and 500 variant of the color name string provided and sets them as background and foreground color of the progress bar

These are the color definitions we have used in the app:

<color name="light_blue_100">#B3E5FC</color>
<color name="light_blue_500">#03A9F4</color>
<color name="purple_100">#E1BEE7</color>
<color name="purple_500">#9C27B0</color>
<color name="red_100">#ffcdd2</color>
<color name="red_500">#f44336</color>

As you can that if we pass purple as the color name, it’ll load purple_100 and purple_500 and set it as corresponding background and foreground color

Other Properties

Now, let’s talk about other properties like the progress value :

  • total == 0 ? 0 : (int) ((completed*100)/total)The conditional is used to prevent divide by zero error.
    The same expression is used to display the circular progress and percentage text in the TextView at the bottom of the layout
  • completed and “/” + total are used to in TextViews of different sizes to create a nice design with completed/total format

This completes our ticket component design and now we’ll see how to reuse this component to display different ticket types.

Composite Layout

To use the ticket component, we just include the layout and bind specific variables from Event model to create a dynamic layout like this:

<data>
    <variable
        name="event"
        type="org.fossasia.openevent.app.data.models.Event" />
</data>

<LinearLayout
    android:orientation="vertical">

    <TextView
        android:text="@string/tickets" />

    <LinearLayout
        android:orientation="horizontal">

        <include
            layout="@layout/ticket_analytics_item"
            bind:color='@{"light_blue"}'
            bind:completed="@{event.soldFreeTickets}"
            bind:ticketName="@{@string/ticket_free}"
            bind:total="@{event.freeTickets}" />

        <include
            layout="@layout/ticket_analytics_item"
            bind:color='@{"purple"}'
            bind:completed="@{event.soldPaidTickets}"
            bind:ticketName="@{@string/ticket_paid}"
            bind:total="@{event.paidTickets}" />

        <include
            layout="@layout/ticket_analytics_item"
            bind:color='@{"red"}'
            bind:completed="@{event.soldDonationTickets}"
            bind:ticketName="@{@string/ticket_donation}"
            bind:total="@{event.donationTickets}" />
    </LinearLayout>

</LinearLayout>

The layout consists of a horizontal with 3 equally divided ticket components,

  • Free Ticket Component -> Light Blue
  • Paid Ticket Component -> Purple
  • Donation Ticket Component -> Red

This is how it looks on a device

 

So this is how data binding made us accomplish easily which would have been a very convoluted solution using traditional ID based view binding. For more info about data binding, refer to these sites:

https://developer.android.com/topic/libraries/data-binding/index.html

http://www.vogella.com/tutorials/AndroidDatabinding/article.html

Data Binding in Android