Xamarin Component: ZXing.Net.Mobile Barcode Scanner

If you are looking for a free Xamarin component for Barcode Scanner, ZXing.Net.Mobile is what you are looking for.
Get it from: https://components.xamarin.com/view/zxing.net.mobile

Problem Encountered:
I was having 1 little problem when try using ZXing.Net.Mobile barcode scanner in my code. I got the following error:

Error	1	 cannot find symbol
symbol  : class OnActionExpandListener
location: class android.support.v4.view.MenuItemCompat
		android.support.v4.view.MenuItemCompat.OnActionExpandListener
	D:\Projects\Hybrid Dev\AnacleMobile\AnacleMobile.Droid\obj\Debug\android\src\mono\android\support\v4\view\MenuItemCompat_OnActionExpandListenerImplementor.java	8	41	AnacleMobile.Droid

It turns out the problem was because ZXing.Net.Mobile requires you to reference get and add Xamarin.Android.Support.V4 into reference. And coincidentally, I have added Mono.Android.Support.V4 into reference as well and apparently, you can’t have both Mono.Android.Support.V4 and Xamarin.Android.Support.V4 as in reference. So in this case I just remove Mono.Android.Support.V4 from reference and it compiled fine.

Solution Source: URL.

Xamarin/Android: Use ViewHolder In BaseAdapter Class

I was having a lot problem with Android ListView using Adapters lately. The original implementation was horrible causing the ListView to generate a lot of duplicate item.

Purpose of Code: I had this checklist page where it supposed to load a list of checklist item from server database and populate the item into ListView on the app.

1. Original code.
Problem Description: the display is weird and it’s causing display of duplicate items here and there.

...
public override View GetView(int position, View view, ViewGroup parent)
{
    GetChecklistItem cli = checklistItems[position];

    if (view != null)
        return view;
    if (view == null)
    {
        view = context.LayoutInflater.Inflate(AnacleAndroid.Resource.Layout.ChecklistRow, null);
    }

    TextView objectName = (TextView)view.FindViewById(AndroidApp.Resource.Id.lblObjectName);
    EditText remarks = (EditText)view.FindViewById(AndroidApp.Resource.Id.checklistRemarks);
    RadioGroup radio = (RadioGroup)view.FindViewById(AndroidApp.Resource.Id.radioGroup);
    objectName.Text = cli.ObjectName;
    if (cli.ChecklistType.HasValue)
    {
        if (cli.HasSingleTextboxField == 1 || cli.ChecklistType == ChecklistItemType.Remarks || cli.ChecklistType == ChecklistItemType.SingleLineFreeText)
        {
            remarks.Visibility = ViewStates.Visible;
            remarks.Text = cli.Remarks;
            remarks.TextChanged += delegate(object sender, Android.Text.TextChangedEventArgs e)
            {
                editChecklist.RemarksTextChangedClick(sender, e, cli);
            };
        }
        else
        {
            // otherwise hide the remark control
            remarks.Visibility = ViewStates.Gone;
        }
        if (cli.ChecklistType == ChecklistItemType.Choice)
        {
            // If checklist type is a choice. Split the ResponseName string and create radio button for each response for user to select
            //radio.
            string[] noOfRb = cli.ResponseName.Split(',');
            foreach (string tempRb in noOfRb)
            {
                RadioButton newRb = new RadioButton(this.context);
                newRb.Text = tempRb;
                newRb.TextSize = 16;
                newRb.Checked = (cli.SelectedResponse != null && cli.SelectedResponse.Equals(tempRb.Trim()));
                newRb.Click += delegate(object sender, EventArgs e)
                {
                    editChecklist.RadioButtonClick(sender, e, cli);
                };
                radio.AddView(newRb, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent));
            }
        }
        else
        {
            // Otherwise hide the RadioGroup control
            radio.Visibility = ViewStates.Gone;
        }
    }
    return view;
}
...

After Googling around, I found some answers. It has something to the Android always repainting the ListView. So I have to have a a way to store the control without the android having to look up for control every time it trigger the GetView(…). And the answer is ViewHolder. For more details please refer to this URL.

2. Fix 1 Using ViewHolder
Problem Description: 
The dynamic radio button keep duplicating like crazy every time user scroll up or down.

// ViewHolder Class
private class ViewHolderItem : Java.Lang.Object
{
	public TextView ObjectName;
	public EditText Remarks;
	public RadioGroup Options;
}
...
public override View GetView(int position, View view, ViewGroup parent)
{
	ViewHolderItem viewHolder;
	
	if (view == null)
	{
		view = context.LayoutInflater.Inflate(AnacleAndroid.Resource.Layout.ChecklistRow, null);
		viewHolder = new ViewHolderItem();
		viewHolder.ObjectName = (TextView)view.FindViewById(AnacleAndroid.Resource.Id.lblObjectName);
		viewHolder.Remarks = (EditText)view.FindViewById(AnacleAndroid.Resource.Id.checklistRemarks);
		viewHolder.Options = (RadioGroup)view.FindViewById(AnacleAndroid.Resource.Id.radioGroup);

		view.Tag = viewHolder;
	}
	else
	{
		viewHolder = (ViewHolderItem)view.Tag;
	}
	
	GetChecklistItem cli = checklistItems[position];
	
	if (cli != null)
	{
		viewHolder.ObjectName.Text = cli.ObjectName;
		viewHolder.ObjectName.Tag = cli.ObjectID;
		viewHolder.Remarks.Visibility = ViewStates.Visible;
		viewHolder.Options.Visibility = ViewStates.Visible;

		if (cli.ChecklistType.HasValue)
		{
			if (cli.HasSingleTextboxField == 1 || cli.ChecklistType == ChecklistItemType.Remarks || cli.ChecklistType == ChecklistItemType.SingleLineFreeText)
			{
				viewHolder.Remarks.Visibility = ViewStates.Visible;
				viewHolder.Remarks.Text = cli.Remarks;
				viewHolder.Remarks.TextChanged += delegate(object sender, Android.Text.TextChangedEventArgs e)
				{
					editChecklist.RemarksTextChangedClick(sender, e, cli);
				};
			}
			else
				viewHolder.Remarks.Visibility = ViewStates.Gone;
		}

		if (cli.ChecklistType == ChecklistItemType.Choice)
		{
			//radio.
			string[] noOfRb = cli.ResponseName.Split(',');

			foreach (string tempRb in noOfRb)
			{
				RadioButton newRb = new RadioButton(this.context);
				newRb.Text = tempRb;
				newRb.TextSize = 16;
				newRb.Checked = (cli.SelectedResponse != null && cli.SelectedResponse.Equals(tempRb.Trim()));
				newRb.Click += delegate(object sender, EventArgs e)
				{
					editChecklist.RadioButtonClick(sender, e, cli);
				};
				viewHolder.Options.AddView(newRb, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent));
			}
		}
		else
			viewHolder.Options.Visibility = ViewStates.Gone;
	}
	
	return view;
}
...

So then I tried moving the value and assignment and radio button creation into the view building if section.

3. Fix 2 Using ViewHolder
Problem Description: Yay! the radio button duplication problem disappears. However, another problem pops out right away. It turns out this fix causes the listview to display not in order of the item list stored. And the order keeps getting messed up every time I scroll up or down.

// ViewHolder Class
private class ViewHolderItem : Java.Lang.Object
{
	public TextView ObjectName;
	public EditText Remarks;
	public RadioGroup Options;
}
...
public override View GetView(int position, View view, ViewGroup parent)
{
	ViewHolderItem viewHolder;
	GetChecklistItem cli = checklistItems[position];
	if (view == null)
	{
		view = context.LayoutInflater.Inflate(AnacleAndroid.Resource.Layout.ChecklistRow, null);
		viewHolder = new ViewHolderItem();
		viewHolder.ObjectName = (TextView)view.FindViewById(AnacleAndroid.Resource.Id.lblObjectName);
		viewHolder.Remarks = (EditText)view.FindViewById(AnacleAndroid.Resource.Id.checklistRemarks);
		viewHolder.Options = (RadioGroup)view.FindViewById(AnacleAndroid.Resource.Id.radioGroup);
		
		if (cli != null)
		{
			viewHolder.ObjectName.Text = cli.ObjectName;
			viewHolder.ObjectName.Tag = cli.ObjectID;
			viewHolder.Remarks.Visibility = ViewStates.Visible;
			viewHolder.Options.Visibility = ViewStates.Visible;

			if (cli.ChecklistType.HasValue)
			{
				if (cli.HasSingleTextboxField == 1 || cli.ChecklistType == ChecklistItemType.Remarks || cli.ChecklistType == ChecklistItemType.SingleLineFreeText)
				{
					viewHolder.Remarks.Visibility = ViewStates.Visible;
					viewHolder.Remarks.Text = cli.Remarks;
					viewHolder.Remarks.TextChanged += delegate(object sender, Android.Text.TextChangedEventArgs e)
					{
						editChecklist.RemarksTextChangedClick(sender, e, cli);
					};
				}
				else
					viewHolder.Remarks.Visibility = ViewStates.Gone;
			}

			if (cli.ChecklistType == ChecklistItemType.Choice)
			{
				//radio.
				string[] noOfRb = cli.ResponseName.Split(',');

				foreach (string tempRb in noOfRb)
				{
					RadioButton newRb = new RadioButton(this.context);
					newRb.Text = tempRb;
					newRb.TextSize = 16;
					newRb.Checked = (cli.SelectedResponse != null && cli.SelectedResponse.Equals(tempRb.Trim()));
					newRb.Click += delegate(object sender, EventArgs e)
					{
						editChecklist.RadioButtonClick(sender, e, cli);
					};
					viewHolder.Options.AddView(newRb, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent));
				}
			}
			else
				viewHolder.Options.Visibility = ViewStates.Gone;
		}

		view.Tag = viewHolder;
	}
	else
	{
		viewHolder = (ViewHolderItem)view.Tag;
	}
	
	return view;
}
...

Solution Description: The problem again turns out to have something to do with the repainting of the android ListView during scrolling. The value setting and building of radio buttons has to be after the view initialization if-else. So then I figured it out! Since the view is stored means the radio button created dynamically added into the RadioGroup will also be stored. Then if the scrolling causing the repainting calling the GetView again it will trigger another another radio button creation and add into RadioGroup without removing the previous Radio button. So the magic was just to clear all child view in the Options RadioGroup before adding the Radio Button.

4. Final Fix Using ViewHolder. (Full Code)

public class ChecklistAdapter : BaseAdapter<GetChecklistItem>
{
    private class ViewHolderItem : Java.Lang.Object
    {
        public TextView ObjectName;
        public EditText Remarks;
        public RadioGroup Options;
    }

    List<GetChecklistItem> checklistItems;
    Activity context;
    EditChecklist editChecklist;
    
    public ChecklistAdapter(Activity context, List<GetChecklistItem> newList) : base()
    {
        this.context = context;
        this.checklistItems = newList;
        editChecklist = (EditChecklist)context;
    }
    public override long GetItemId(int position)
    {
        return position;
    }
    public override GetChecklistItem this[int position]
    {
        get { return checklistItems[position]; }
    }
    public override int Count
    {
        get { return checklistItems.Count; }
    }

    public override View GetView(int position, View view, ViewGroup parent)
    {
        ViewHolderItem viewHolder;

        if (view == null)
        {
            view = context.LayoutInflater.Inflate(AnacleAndroid.Resource.Layout.ChecklistRow, null);
            viewHolder = new ViewHolderItem();
            viewHolder.ObjectName = (TextView)view.FindViewById(AndroidApp.Resource.Id.lblObjectName);
            viewHolder.Remarks = (EditText)view.FindViewById(AndroidApp.Resource.Id.checklistRemarks);
            viewHolder.Options = (RadioGroup)view.FindViewById(AndroidApp.Resource.Id.radioGroup);

            view.Tag = viewHolder;
        }
        else
        {
            viewHolder = (ViewHolderItem)view.Tag;
        }

        GetChecklistItem cli = checklistItems[position];

        if (cli != null)
        {
            viewHolder.ObjectName.Text = cli.ObjectName;
            viewHolder.ObjectName.Tag = cli.ObjectID;
            viewHolder.Remarks.Visibility = ViewStates.Visible;
            viewHolder.Options.Visibility = ViewStates.Visible;

            if (cli.ChecklistType.HasValue)
            {
                if (cli.HasSingleTextboxField == 1 || cli.ChecklistType == ChecklistItemType.Remarks || cli.ChecklistType == ChecklistItemType.SingleLineFreeText)
                {
                    viewHolder.Remarks.Visibility = ViewStates.Visible;
                    viewHolder.Remarks.Text = cli.Remarks;
                    viewHolder.Remarks.TextChanged += delegate(object sender, Android.Text.TextChangedEventArgs e)
                    {
                        editChecklist.RemarksTextChangedClick(sender, e, cli);
                    };
                }
                else
                    viewHolder.Remarks.Visibility = ViewStates.Gone;
            }

            if (cli.ChecklistType == ChecklistItemType.Choice)
            {
                //radio.
                string[] noOfRb = cli.ResponseName.Split(',');
				
				// important to remove all vew under the view group before repopulating it
                if (viewHolder.Options != null && viewHolder.Options.ChildCount > 0)
                    viewHolder.Options.RemoveAllViews();

                foreach (string tempRb in noOfRb)
                {
                    RadioButton newRb = new RadioButton(this.context);
                    newRb.Text = tempRb;
                    newRb.TextSize = 16;
                    newRb.Checked = (cli.SelectedResponse != null && cli.SelectedResponse.Equals(tempRb.Trim()));
                    newRb.Click += delegate(object sender, EventArgs e)
                    {
                        editChecklist.RadioButtonClick(sender, e, cli);
                    };
                    viewHolder.Options.AddView(newRb, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent));
                }
            }
            else
                viewHolder.Options.Visibility = ViewStates.Gone;
        }
        
        return view;
    }

    public override void NotifyDataSetChanged()
    {
        base.NotifyDataSetChanged();
    }
}

CSS: Full Browser’s Width Bar

Source: http://css-tricks.com/full-browser-width-bars/

Below are pretty much the main CSS code. For more details, please refer to the source.
To change original color. You can just update the background.

h2 {
  position: relative;
  background: black; 
}
h2:before, h2:after {
  content: "";
  position: absolute;
  background: black;  /* Match the background */
  top: 0;
  bottom: 0;
  width: 9999px;   /* some huge width */
} 
h2:before {
  right: 100%; 
}
h2:after {
  left: 100%;
}

html, body {
    overflow-x: hidden;
}

JavaScript / JQuery: Create Label, TextBox, Select List Dynamically

Below are some JavaScript/JQuery function to create HTML Control dynamically on the fly.

1. Create Label

function CreateLabel(containerId, labelId, html) {
    if (containerId && labelId) {
        var label = document.createElement('label');
        label.innerHTML = html;
        label.id = labelId;
        $('#' + containerId).append(label).trigger('create');
    }
}

2. Create TextBox

function CreateTextBox(containerId, inputId) {
    if (containerId && inputId) {
        var input = document.createElement('input');
        input.type = 'text';
        input.id = inputId;
        input.name = inputId;
        $('#' + containerId).append(input).trigger('create');
    }
}

3. Create Select Menu

function CreateDropDownList(containerId, selectId, placeHolderText, optionTextArray, optionValueArray) {
    if (containerId) {
        var select = document.createElement('select');
        select.id = selectId;
        select.name = selectId;
        if (placeHolderText && placeHolderText != '') {
            var dfOpt = document.createElement('option');
            dfOpt.value = '';
            dfOpt.text = placeHolderText;
            select.options.add(dfOpt);
        }
        
        for (i = 0; i < optionTextArray.length && i < optionValueArray.length; i++) {
            var opt = document.createElement('option');
            opt.value = optionValueArray[i];
            opt.text = optionTextArray[i];
            select.options.add(opt);
        }
        $('#' + containerId).append(select).trigger('create');
    }
}

Create Mini Select Menu for JQuery Mobile

function CreateMiniDropDownList(containerId, selectId, placeHolderText, optionTextArray, optionValueArray) {
    if (containerId) {
        var select = document.createElement('select');

        var select = document.createElement('select');
        var attr = document.createAttribute("data-mini");
        attr.value = "true";
        select.setAttributeNode(attr);
        var attr = document.createAttribute("data-inline");
        attr.value = "true";
        select.setAttributeNode(attr);

        select.id = selectId;
        select.name = selectId;
        if (placeHolderText && placeHolderText != '') {
            var dfOpt = document.createElement('option');
            dfOpt.value = '';
            dfOpt.text = placeHolderText;
            select.options.add(dfOpt);
        }
        
        for (i = 0; i < optionTextArray.length && i < optionValueArray.length; i++) {
            var opt = document.createElement('option');
            opt.value = optionValueArray[i];
            opt.text = optionTextArray[i];
            select.options.add(opt);
        }
        $('#' + containerId).append(select).trigger('create');
    }
}

JavaScript / JQuery: Create Horizontal Radio Button

In my course of project, I find a need to use JavaScript/JQuery to dynamically add element to my HTML. Below is a function to create JQuery Mobile horizontal radio button.

.trigger(‘create’) is need to refresh JQuery Mobile UI otherwise the UI will appear in native HTML.
containerId: normally is the <div> ID.
elementId: the ID & name f the CheckBox
eventName: onchange event name for event handler to identify
textArray, valueArray: Text-Value pair of the list of radio box

function CreateHorizontalRadioButtons(containerId, elementId, eventName, textArray, valueArray) {
    var div = document.getElementById(containerId);

    var content = '<fieldset data-role="controlgroup" data-type="horizontal" >';

    for (i = 0; i < textArray.length && i < valueArray.length; i++) {
        content += '<input type="radio" name="' + elementId + '" id="' + elementId + String(i) + '" value="' + valueArray[i] + '" onchange="SelectedRadioIndexChanged(\'' + eventName + '\', this)" />';
        content += '<label for="' + elementId + String(i) + '">' + textArray[i] + "</label>";
    }

    content += '<input type="radio" name="' + elementId + '" id="' + elementId + String(valueArray.length) + '" value="" onchange="SelectedRadioIndexChanged(\'' + eventName + '\', this)" />';
    content += '<label for="' + elementId + String(valueArray.length) + '">Unknown</label>';
    content += '</fieldset>'
    $('#' + containerId).append(content).trigger('create');
}

Below is method to create horizontal radio button mini theme.

function CreateMiniHorizontalRadioButtons(containerId, elementId, eventName, textArray, valueArray) {
    var div = document.getElementById(containerId);

    var content = '<fieldset data-role="controlgroup" data-type="horizontal" data-mini="true" >';

    for (i = 0; i < textArray.length && i < valueArray.length; i++) {
        content += '<input type="radio" name="' + elementId + '" id="' + elementId + String(i) + '" value="' + valueArray[i] + '" onchange="SelectedRadioIndexChanged(\'' + eventName + '\', this)" />';
        content += '<label for="' + elementId + String(i) + '">' + textArray[i] + "</label>";
    }

    content += '</fieldset>'
    $('#' + containerId).append(content).trigger('create');
}

JavaScript / JQuery: Set element visibility

Below are functions to set visibility of an element through JavaScript for JQuery Element.

Set element’s visibility

function SetElementVisibility(id, visibility)
{
    if (visibility == 'visible')
        $('#' + id).show();
    else if (visibility == 'hidden')
        $('#' + id).hide();
}

Set visibility of element’s parent. Usually use to set the containing <div> tag’s visibility.

function SetContainerVisibility(id, visibility)
{
    if (visibility == 'visible')
        $('#' + id).parent().show();
    else if (visibility == 'hidden')
        $('#' + id).parent().hide();
}

JavaScript / JQuery: Check/Uncheck RadioBox

Below method allows you check/uncheck JQuery Mobile checkbox. The refresh line is needed for JQuery to re-render the JQuery Mobile UI otherwise it will native HTML UI.

function SetRadioChecked(id, v)
{
    var el = document.getElementById(id);

    if (el) {
        if (v == '0') {
            el.checked = false;
            $('#' + id).checkboxradio('refresh');
        }
        else if (v == '1') {
            el.checked = true;
            $('#' + id).checkboxradio('refresh');
        }
    }
}