Last active
February 5, 2016 18:49
-
-
Save kiliman/dc2590b40a3bda9291bb to your computer and use it in GitHub Desktop.
Dynamic Layout in MvvmCross for Android
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) | |
{ | |
var tabConfig = ViewModel.TabConfig; | |
var tabId = tabConfig["id"].ToString(); | |
var ignored = base.OnCreateView(inflater, container, savedInstanceState); | |
_scrollView = new ScrollView(Activity); | |
var layout = BuildLayout(inflater, tabConfig); | |
_scrollView.AddView(layout); | |
return _scrollView; | |
} | |
private View BuildLayout(LayoutInflater inflater, JObject tabConfig) | |
{ | |
var layout = new LinearLayout(Activity) | |
{ | |
Orientation = Orientation.Vertical | |
}; | |
var layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MatchParent, LinearLayout.LayoutParams.MatchParent); | |
layout.LayoutParameters = layoutParams; | |
layout.Focusable = true; | |
layout.FocusableInTouchMode = true; | |
var set = this.CreateBindingSet<DynamicFragmentView, DynamicViewModel>(); | |
var bindingContext = new MvxAndroidBindingContext(Activity, new LayoutInflaterProvider(inflater), ViewModel); | |
var controls = new List<View>(); | |
foreach (var control in tabConfig["controls"]) | |
{ | |
var id = (string)control["id"]; | |
var type = (string)control["type"]; | |
var label = (string)control["label"]; | |
switch (type) | |
{ | |
case "text": | |
case "number": | |
var editText = BuildEditText(layout, label); | |
if (type == "number") | |
{ | |
editText.InputType = InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned; | |
} | |
set.Bind(editText).For(x => x.Text).To("Values['" + id + "']"); | |
break; | |
case "autocomplete": | |
var listkey = (string)control["list"]; | |
var autoitems = ViewModel.GetAutoCompleteList(listkey); | |
var autoComplete = BuildAutoComplete(layout, inflater, label, autoitems); | |
set.Bind(autoComplete).For(x => x.Text).To("Values['" + id + "']"); | |
autoComplete.FocusChange += ControlOnFocusChange; | |
break; | |
case "popup": | |
var url = (string)control["url"]; | |
var popupProperty = (string)control["property"]; | |
var popupButton = BuildPopup(layout, label); | |
set.Bind(popupButton).For("Click").To(vm => vm.ShowPopupCommand).WithConversion("CommandParameter", new PopupCommandInfo { Url = url, Property = popupProperty}); | |
break; | |
case "photo": | |
var photoLayout = BuildPhotoLayout(layout, inflater, id, label); | |
var cameraButton = photoLayout.FindViewById<ImageButton>(Resource.Id.camera_button); | |
var galleryButton = photoLayout.FindViewById<ImageButton>(Resource.Id.gallery_button); | |
var imageView = photoLayout.FindViewById<ImageView>(Resource.Id.photo_view); | |
set.Bind(cameraButton).For("Click").To(vm => vm.TakePhotoCommand).WithConversion("CommandParameter", id); | |
set.Bind(galleryButton).For("Click").To(vm => vm.OpenGalleryCommand).WithConversion("CommandParameter", id); | |
set.Bind(imageView).For("Click").To(vm => vm.ShowPictureCommand).WithConversion("CommandParameter", id); | |
SetImageView(id); | |
break; | |
case "dropdown": | |
var items = control["values"].ToObject<string[]>(); | |
var spinner = BuildSpinner(layout, label, items); | |
var position = Array.FindIndex(items, x => x == (string)ViewModel.Values[id]); | |
spinner.SetSelection(position); | |
spinner.ItemSelected += (sender, args) => | |
{ | |
ViewModel.Values[id] = spinner.GetItemAtPosition(args.Position).ToString(); | |
layout.RequestFocus(); | |
}; | |
spinner.FocusChange += ControlOnFocusChange; | |
break; | |
case "switch": | |
var @switch = BuildSwitch(layout, label, "Yes", "No"); | |
set.Bind(@switch).For(x => x.Checked).To("Values['" + id + "']"); | |
break; | |
} | |
} | |
set.Apply(); | |
return layout; | |
} | |
private const int TEXT_SIZE = 20; | |
private TextView BuildTextView(ViewGroup parentView, string label) | |
{ | |
var textView = new TextView(parentView.Context) | |
{ | |
Text = label, | |
TextSize = TEXT_SIZE - 4, | |
}; | |
textView.SetTextColor(new Color(59, 140, 212)); | |
textView.SetTypeface(null, TypefaceStyle.Bold); | |
var textViewParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); | |
textView.LayoutParameters = textViewParams; | |
var padding = textView.DipsToPixels(8); | |
textView.SetPadding(padding, padding, padding, 0); | |
parentView.AddView(textView); | |
return textView; | |
} | |
private EditText BuildEditText(ViewGroup parentView, string hint) | |
{ | |
BuildTextView(parentView, hint); | |
var editText = new EditText(parentView.Context) | |
{ | |
Hint = hint, | |
TextSize = TEXT_SIZE | |
}; | |
var editTextParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); | |
editText.LayoutParameters = editTextParams; | |
var padding = editText.DipsToPixels(8); | |
editText.SetPadding(padding, padding, padding, padding * 2); | |
parentView.AddView(editText); | |
return editText; | |
} | |
private AutoCompleteTextView BuildAutoComplete(ViewGroup parentView, LayoutInflater inflater, string hint, string[] items) | |
{ | |
BuildTextView(parentView, hint); | |
var autoComplete = new AutoCompleteTextView(parentView.Context) | |
{ | |
Hint = hint, | |
TextSize = TEXT_SIZE | |
}; | |
autoComplete.SetSingleLine(true); | |
var autoCompleteParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); | |
autoComplete.LayoutParameters = autoCompleteParams; | |
var padding = autoComplete.DipsToPixels(8); | |
autoComplete.SetPadding(padding, padding, padding, padding * 2); | |
var adapter = new AutoCompleteCustomAdapter(inflater, Android.Resource.Layout.SelectDialogItem, items); | |
autoComplete.Threshold = 1; | |
autoComplete.Adapter = adapter; | |
parentView.AddView(autoComplete); | |
return autoComplete; | |
} | |
private Button BuildPopup(ViewGroup parentView, string hint) | |
{ | |
var button = new Button(parentView.Context) | |
{ | |
Text = hint, | |
TextSize = TEXT_SIZE | |
}; | |
var buttonParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); | |
button.LayoutParameters = buttonParams; | |
var padding = button.DipsToPixels(8); | |
button.SetPadding(padding, padding, padding, padding * 2); | |
parentView.AddView(button); | |
return button; | |
} | |
private Switch BuildSwitch(ViewGroup parentView, string text, string textOn, string textOff) | |
{ | |
var @switch = new Switch(parentView.Context) | |
{ | |
Text = text, | |
TextOn = textOn, | |
TextOff = textOff, | |
TextSize = TEXT_SIZE | |
}; | |
var switchParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); | |
var padding = @switch.DipsToPixels(8); | |
@switch.SetPadding(padding, padding, padding, padding * 2); | |
@switch.LayoutParameters = switchParams; | |
parentView.AddView(@switch); | |
return @switch; | |
} | |
private Spinner BuildSpinner(ViewGroup parentView, string text, string[] items) | |
{ | |
BuildTextView(parentView, text); | |
var spinner = new Spinner(Activity); | |
var spinnerParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); | |
var padding = spinner.DipsToPixels(8); | |
spinner.SetPadding(padding, padding, padding, padding * 2); | |
spinner.LayoutParameters = spinnerParams; | |
var adapter = new ArrayAdapter(parentView.Context, Resource.Layout.SpinnerItem, items); | |
spinner.Adapter = adapter; | |
spinner.Focusable = true; | |
parentView.AddView(spinner); | |
return spinner; | |
} | |
private View BuildPhotoLayout(ViewGroup parentView, LayoutInflater inflater, string name, string caption) | |
{ | |
var photoLayout = inflater.Inflate(Resource.Layout.photolayout, null); | |
var photoCaption = photoLayout.FindViewById<TextView>(Resource.Id.photo_caption); | |
photoCaption.Text = caption; | |
var imageView = photoLayout.FindViewById<ImageView>(Resource.Id.photo_view); | |
_imageViews[name] = imageView; | |
parentView.AddView(photoLayout); | |
return photoLayout; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"tabs": [ | |
{ | |
"id": "tab1", | |
"title": "Tab #1", | |
"controls": [ | |
{ | |
"id": "text1", | |
"label": "Text #1", | |
"type": "text" | |
}, | |
{ | |
"id": "number1", | |
"label": "Number #1", | |
"type": "number" | |
}, | |
{ | |
"id": "photo1", | |
"label": "Photo #1", | |
"type": "photo" | |
}, | |
{ | |
"id": "autocomplete1", | |
"label": "Autocomplete #1", | |
"type": "autocomplete", | |
"list": "list1" | |
}, | |
{ | |
"id": "dropdown1", | |
"label": "Dropdown #1", | |
"type": "dropdown", | |
"values": [ | |
"", | |
"Value 1", | |
"Value 2", | |
"Value 3", | |
] | |
}, | |
{ | |
"id": "switch1", | |
"label": "Switch #1", | |
"type": "switch" | |
} | |
] | |
} | |
] | |
} |
The TabLayout.json file is a simple layout that lists the controls and their types. In our app, we have multiple tabs using a ViewPager.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is a simple gist demonstrating creating a dynamic layout based on layout defined in a JSON file.
I use manual binding by creating a BindingSet and adding bindings as needed. The ViewModel contains a property named
Values
of typeObservableDictionary<string, object>
.As shown in BuildPhotoLayout(), in addition to creating the controls manually like, new EditText(), you can also inflate a more complex layout like
var photoLayout = inflater.Inflate(Resource.Layout.photolayout, null);