How to set selected item in MvxSpinner - android

I have an MvxSpinner that is bound to a List<PhotoCategory> thus:
<Mvx.MvxSpinner
style="#style/Spinners"
android:id="#+id/photoCategorySpinner"
android:prompt="#string/photoCategory_prompt"
local:MvxBind="ItemsSource PhotoCategories; SelectedItem SelectedPhotoCategory; Visibility ShowPhotoFields, Converter=Visibility"
local:MvxDropDownItemTemplate="#layout/spinner_photocategories"
local:MvxItemTemplate="#layout/item_photocategory" />
The SelectedPhotoCategory that the SelectedItem is bound to is also a PhotoCategory. When this screen is in "update mode", the ViewModel sets the SelectedPhotoCategory to the PhotoCategory whose PhotoCategoryId matches the one in the SQLite database. However, when the spinner is displayed, the default value (which I add to the PhotoCategories property, PhotoCategory = 0, CategoryName="[Choose a Category]") is shown. The only fix I've found is this (which works ok) code added to the View:
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
SetContentView(Resource.Layout.PhotoView);
//If we're in Update mode, select the relevant photo category in the spinner:
PhotoViewModel photoViewModel = (PhotoViewModel)ViewModel;
if (photoViewModel.ScreenMode == Constants.ScreenMode.Update) {
MvxSpinner photoCategorySpinner = FindViewById<MvxSpinner>(Resource.Id.photoCategorySpinner);
int itemPosition = 0;
int selectedPhotoCategoryId = photoViewModel.SelectedPhotoCategory.PhotoCategoryId;
foreach (PhotoCategory photoCategory in photoViewModel.PhotoCategories) {
if (photoCategory.PhotoCategoryId == selectedPhotoCategoryId) {
photoCategorySpinner.SetSelection(itemPosition);
}
itemPosition++;
}
}
I've also tried using the GetPosition method of the MvxSpinner.Adapter but this always returns -1 for PhotoCategoryId, CategoryName or SelectedPhotoCategory as the parameter value.
What am I missing??

The binding
SelectedItem SelectedPhotoCategory
should set this for you - and should use Equals to find the correct item to select in the spinner.
This certainly seems to work in the very latest code when testing using the SpinnerViewModel in https://github.com/slodge/MvvmCross-Tutorials/tree/master/ApiExamples
I know there was a bug reported recently on the use of == versus Equals in one of the bindings - but I don't think this effects the spinner (see https://github.com/slodge/MvvmCross/issues/309).

Related

Anko ListItem setOnClickListener

I'm trying to play around with some Kotlin and Anko (more familiar with iOS) and taking from their example, there is this code:
internal open class TextListWithCheckboxItem(val text: String = "") : ListItem {
protected inline fun createTextView(ui: AnkoContext<ListItemAdapter>, init: TextView.() -> Unit) = ui.apply {
textView {
id = android.R.id.text1
text = "Text list item" // default text (for the preview)
isClickable = true
setOnClickListener {
Log.d("test", "message")
}
init()
}
checkBox {
id = View.generateViewId()
setOnClickListener {
Log.d("hi", "bye")
}
init()
}
}.view
My row appears how I want with a checkbox and textview. But I want to bind an action to the row selection not the checkbox selection. Putting a log message in both, I see that I get a log message when the row is selected which flips the checkbox. It does not, however, log my "test:message" from the textView click handler. Is there a way to get around this?
Apparently your issue has been addressed here. As the checkbox is consuming all the focus of ListItem you should set the CheckBox's focusable flag to false:
checkBox {
focusable = View.NOT_FOCUSABLE
}
Unfortunately setFocusable call requires at least API 26, but you could define view .xml and inflate the view manually as described here:
<CheckBox
...
android:focusable="false" />
Alternatively you could try setting a onTouchListener returning false which means the touch event will be passed to underlying views.
Let me know if it works ;)

Setting Text Based on values held in ArrayList

This is driving me a little mad since I know this should be very simple but I am not getting the desired affect.
I have the following arraylist
private List<String> tagStringArray = new ArrayList<>();
Then later I have a method that creates dynamic buttons, based on ID values pulled across from my Retrofit instance.
In my method, I have a count to help me set the title of the button but I also add the values of count to an ArrayList for use in another method.
I have taken a snip of relevant information from the method mentioned.
count = 1;
if (!questionNumber.equals("") && !questionNumber.equals(null)) {
for (final Object value : list) {
try {
/*Dynamically create new Button which includes the question number
*/
final AppCompatButton btn_question = new AppCompatButton(getActivity());
/*LayoutParams (int width, int height,float weight)
As LayoutParams is defaulted in px, I have called a method called dpToPX to make sure
the dynamically added EditText is the same size on all devices.
*/
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpToPx(280), dpToPx(45), 1);
btn_question.setBackgroundColor(Color.parseColor("#3B5998"));
btn_question.setTextColor(Color.WHITE);
btn_question.setText("Question "+count);
//set the Tag based on its position in the XML
tagStringArray.add(String.valueOf((count)));
count++;
If a user clicks on say Question 1 Button, I want my fragment to say Question 1, so to try and achieve that, I have tried doing the following:
String tags = String.valueOf(tagStringArray);
tags = tags.substring(1, tags.length() -1);
String[] currentTag = tags.split(",");
if (currentTag[0].contains("1")) {
tv_setQuestions_edit.setText("Question 1");
}else if(currentTag[1].contains("2")) {
tv_setQuestions_edit.setText("Question 2");
}
But this will always set the title to Question 1 and I am not sure what is going wrong.......
If I use the following toast Toast.makeText(getActivity(), Arrays.toString(currentTag), Toast.LENGTH_LONG).show(); it shows [1,2] so I know they are being added ok.
I did look into using tags by doing:
public static int KEY_COUNT=0; public static int KEY_VALUE=1;
btn_question.setTag(KEY_VALUE,value);
btn_question.setTag(KEY_COUNT,count);
But for some reason, when I add more than one tag (as I need a minimum of 2), my dynamic button is missing from the layout. But for some reason when only 1 tag - like this btn_question.setTag(value); is used, it shows up fine (I have a feeling its some issue with my fragment). Therefore I am trying to think of a workaround in the meantime.
Any help or guidance would be really appreciated.
It's because
currentTag[0].contains("1")
is always true. The first item of currentTag always contains "1".
Instead of doing this, why don't you just do String titleForFragment = myButton.getText() in the onClick method for the button? That way, you can set the same onClickListener on all the buttons, and it will reduce the amount of code you need to write.

Two-way data-binding infinite loop

I have a list of items. In each item's row I have 2 EditTexts side-by-side. EditText-2 depends on EditText-1's value. This list is bound with data-binding values in HashMap<String, ItemValues>
For Example:
Total _____1000____
Item A __1__ __200__
Item B __1__ __200__
Item C __1__ __200__
Item D __2__ __400__
First EditText is the share and the second value is its value calculated based on total and share. So, in example if I change any 1 share, all the values will be changed. So, shown in example total no of shares are = 1+1+1+2 = 5. So amount per share = 1000/5 = 200 and is calculated and shown in next EditText.
I have bound this values with two-way data binding like this:
As, this is a double value, I have added 2 binding adapters for this like this:
#BindingAdapter("android:text")
public static void setShareValue(EditText editText, double share) {
if (share != 0) {
editText.setText(String.valueOf(share));
} else {
editText.setText("");
}
}
#InverseBindingAdapter(attribute = "android:text")
public static double getShareValue(EditText editText) {
String value = editText.getText().toString();
if (!value.isEmpty()) {
return Double.valueOf(value);
} else
return 0;
}
Now, to calculate new values, I need to re-calculate whole thing after any share value is changed. So, I added android:onTextChagned method to update Calculations. But it gets me an infinite loop.
<EditText
android:text="#={items[id].share}"
android:onTextChanged="handler.needToUpdateCalculations"
.... />
public void needToUpdateCalculations(CharSequence charSequence, int i, int i1, int i2) {
updateCalculations();
}
This gets an infinete loop because when data changes, it is rebound to the EditText, and each EditText has an onTextChanged attached it will fire again and it will get really large - infinite loop.
It also updates the value of itself, ended up loosing the cursor as well.
I have also tried several other methods like adding TextWatcher when on focus and removing when losses focus. But at least it will update it self and will loose the cursor or infinite loop.
Unable to figure this problem out. Thank you for looking into this problem.
EDIT:
I have tried with the below method. But, it doesn't allow me to enter . (period).
#BindingAdapter("android:text")
public static void setDoubleValue(EditText editText, double value) {
DecimalFormat decimalFormat = new DecimalFormat("0.##");
String newValue = decimalFormat.format(value);
String currentText = editText.getText().toString();
if (!currentText.equals(newValue)) {
editText.setText("");
editText.append(newValue);
}
}
The reason you stated is correct and it will make a infinite loop definitely. And there is a way to get out from the infinite loop of this problem, android official provided a way to do so (But it is not quite obvious.)(https://developer.android.com/topic/libraries/data-binding/index.html#custom_setters)
Binding adapter methods may optionally take the old values in their
handlers. A method taking old and new values should have all old
values for the attributes come first, followed by the new values:
#BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
if (oldPadding != newPadding) {
view.setPadding(newPadding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
}
You can use the old value and new value comparison to make the setText function called conditionally.
#BindingAdapter("android:text")
public static void setShareValue(EditText editText, double oldShare,double newShare) {
if(oldShare != newShare)
{
if (newShare!= 0) {
editText.setText(String.valueOf(newShare));
} else {
editText.setText("");
}
}
}

checking/comparing imagebutton resources

I need to know how to check how to compare or check for an image resource on an image button
First I setup the button.
button1 = (ImageButton) findViewById(R.id.ib1);
button1.setImageResource(R.drawable.smiley);
if( currentTime%2==0 ) {
button1.setImageResource(R.drawable.smiley);
}
else {
button1.setImageResource(R.drawable.smileyhit);
}
Later at some point I need to check if the image resource of the button is the smiley drawable and increase the score.
something like
if( button1.getImageResource() == R.drawable.smiley ) {
score = score + 1;
}
What should I do to compare that? I do not want to use tags. Please help me out!
Use ImageButton.setTag and ImageButton.getTag to identify which image is currently in ImageButton background as:
if( currentTime%2==0 ) {
button1.setImageResource(R.drawable.smiley);
button1.setTag(R.drawable.smiley);
}
else {
button1.setImageResource(R.drawable.smileyhit);
button1.setTag(R.drawable.smileyhit);
}
use button1.getTag to check current image:
if(Integer.parseInt(button1.getTag().toString()) == R.drawable.smiley ) {
score = score + 1;
}
using setTag and getTag you can easily differentiate images.
if( currentTime%2==0 ) {
button1.setImageResource(R.drawable.smiley);
button1.setTag("smiley");
}
else {
button1.setImageResource(R.drawable.smileyhit);
button1.setTag("smileyhit");
}
if(button1.getTag().toString().equalsIgnoreCase("smiley")){
score = score + 1;
}
Maintain an array with currentTime%2==0 in it for each button. One should not depend on the UI to retrieve the state of the app since the UI is recreated at several different points in the life-cycle of an app. All your data which determines the state of the app should be seperated from the UI.
EDIT
Okie.. as you say you have more images, i still would go through array or list rather than depending on UI to get the data.My method would be as follows,
Create constants for each Image resource with an Int value
eg: public static final int image1=1;
create the int array for the number of images and maintain them with initial values.
Each time to you change the image change the corresponding array element accordingly.
To find out which resource is used, check the corresponding array element.
While restoring UI(like onResume) use the array to set the corresponding draw able resource.

Mono for Android: Spinner ItemSelected event triggers on load but shouldn't?

I have a spinner with a few values and I fill it from my webservice.
Filling the spinner
int i = 0;
var dropItems = new List<SpinItem2>();
DataRow[] result = myOPTvalues.Tables[0].Select("FieldValue=" + item.FieldValue);
foreach (DataRow row in result)
{
var optItem = new PrevzemSpin();
optItem.FieldValue = row["FieldValue"].ToString();
if (optItem.FieldValue.Equals(""))
optItem.FieldValue = null;
optItem.FieldTextValue = row["FieldTextValue"].ToString();
if (optItem.FieldTextValue.Equals(""))
optItem.FieldTextValue = null;
dropItems.Add(new SpinItem2(i, optItem.FieldValue.ToString(), optItem.FieldTextValue.ToString()));
}
i = 1;
foreach (DataRow row in myOPTvalues.Tables[0].Rows)
{
var optItem = new PrevzemSpin();
optItem.FieldValue = row["FieldValue"].ToString();
if (optItem.FieldValue.Equals(""))
optItem.FieldValue = null;
optItem.FieldTextValue = row["FieldTextValue"].ToString();
if (optItem.FieldTextValue.Equals(""))
optItem.FieldTextValue = null;
if (optItem.FieldValue != item.FieldValue)
{
dropItems.Add(new SpinItem2(i, optItem.FieldValue.ToString(), optItem.FieldTextValue.ToString()));
}
++i;
}
For some reason it acts like the item that was inserted first is "selected" on default and then triggers the ItemSelected event which I use to send the selected but I don't want that.
Since there's quite a number of these spinners on my screen it really slows down the activity plus it also sends the incorrect values to the field and since I use the ItemSelect to detect if everything went OK (let's say the service fell or the values themselves changed on server (someone added a new field on the server application) while the user is completing the form etc.)
Is there someway to tell the app not to trigger that on activity load but on actual user interaction?
I can't speak for Android specifically, but I have encountered this many times with Windows.
The solution I usually use is to simply add a boolean loading variable. Set it to true at the beginning of your initialisation and then clear it at the end.
In your event handlers like ItemSelected you can simply check if this is being triggered as the result of the initial load.
private void onItemSelected(....)
{
if(loading)
{
return; //Ignore as form is still loading
}
//Normal event handling logic goes here
....
}
Before I declared GetView:
int LastSpinnerSelectedPosition;
Inside my spinner definition:
LastSpinnerSelectedPosition = 0;
My spinner ItemSelected event:
var CurrentSelectedIndex = SpinnerValue.SelectedItemPosition;
if (CurrentSelectedIndex != LastSpinnerSelectedPosition)
{
// WHATEVER I WANTED TO DO ON ITEM SELECT ANYWAY
// Fix the LastSpinnerSelectedPosition ;)
LastSpinnerSelectedPosition = CurrentSelectedIndex;
}
Simple ;D
Just for clarification, the event fires when an item is selected. The semantics are obviously flawed, but technically the item IS selected when it initially loads since you can then immediately ask the spinner for which item is selected, so as the other answers say, just ignore the first time it is selected since it's guaranteed to be the loading select, and then proceed as normal after that.

Categories

Resources