setNextFocusDownId(...) Not Doing as Expected - android

I am attempting to add multiple EditTexts to a GridLayout and want to be able to specify which EditText is focused when the user clicks the "Next" button on the keyboard.
I thought this was going to be as simple as setting the setNextFocusDownId(...) method on the each EditText but unfortunately this doesn't seem to work.
My Layout file simply consists of a GridLayout with an EditText at the bottom:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto"
android:id="#+id/Grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
grid:columnCount="2">
</android.support.v7.widget.GridLayout>
<EditText
android:id="#+id/Last"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Last"/>
</LinearLayout>
And the code in my MainActivity.java file dynamically adds 10 EditTexts and attempts to assign the 'NextFocus' appropriately:
public class MainActivity extends AppCompatActivity
{
final Context context = this;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridLayout grid = (GridLayout) findViewById(R.id.Grid);
int N=10;
grid.setRowCount((N+1)/2);
EditText prev = null;
for (int i=0 ; i<N ; i++)
{
EditText t = new EditText(context);
t.setInputType(InputType.TYPE_CLASS_TEXT);
t.setMaxLines(1);
t.setHint("" + i);
grid.addView(t);
if (prev != null)
prev.setNextFocusDownId(t.getId());
prev = t;
}
}
}
This does indeed create a grid with two columns of 5 elements however, clicking in the top cell and clicking Next arrow (">") on the keyboard takes you down a row rather then to the expected "NextFocused" ExitText.
Starting in the '0th' cell the "next" button takes you successively through the 'default' sequence:
'0' > '2' > '4' > '6' > '8' > "Last" (rather than the expected '0' > '1' > '2' > ... > '9' > "last").
Interestingly though, setting the NextFocused to findViewById(R.id.Last).getId() (instead of t.getId()) does cause focus to jump to "Last" from every cell in the grid. Any idea why this isn't working for t.getId().
The answer to this question seems to suggest simply setting 'setNextFocusDownId(...)' should do the trick however it's not working for me. Is there something different about accessing ID's of dynamically created View's that is different from XML defined Views that I don't quite understand?
Thanks,
Slarti.

Huh-harr, worked it out!
I didn't realise it but when a view is created dynamically it's ID is always created with a value if '-1'. One can however assign an ID to a view using the View's setId(int). From API level 17 onward one can generate an ID to use for this purpose using generateViewId() but prior to API 17 you have to come up with your own ID. Fortunately, according to this post, R.id... values are all greater than 0x00FFFFFF, so simply creating dynamic ID's by counting up from '0' should keep you well clear of the previously defined values.
Therefor, adding the line t.setId(i); as soon as the TextView is created in the example above resolves the problem nicely.
For another well answered question regarding dynamically assigned ID check out this question.
Cheers,
Slarti.

Related

EditText value is getting replaced with another edit text on backpress

I am in a situation here, I've two edit text in a fragment, on back press the value of first edit text is getting replaced by second edit text. Now both the edit text are having same values.
<EditText
android:id="#+id/edit_text"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="20dp"
android:hint="hint" />
Above is my EditText, this EditText is inside a layout and I've included the layout in my fragment two times to get the EditText, on click of submit I replace this fragment with another one but on backpress when I come back to this fragment, the value of first edit box is getting replaced by second one. Now both has same values.
This is how I am replacing fragment.
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.layout_fragment, fragment, tag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commitAllowingStateLoss();
getSupportFragmentManager().executePendingTransactions();
Both of your EditTexts are using the same id. In your XML files you need to make sure that the id for both edit texts are different. Even if there are in separate XML files, they must have different ids.
For instance, you might change their declarations to...
<EditText
android:id="#+id/edit_text1"
<EditText
android:id="#+id/edit_text2"
You should use different id for the EditText.
Assume the EditText is defined in edit_text_layout.xml, when including the layout, specify different id.
In fragment_layout.xml:
<include
layout="#layout/edit_text_layout"
android:id="#+id/edit_text_1" />
<include
layout="#layout/edit_text_layout"
android:id="#+id/edit_text_2" />
Explain:
Android save a view's state in a SparseArray, the key is the view's id. When restoring the state, views have the same id will be get the same state value.
If the view doesn't have an id (NO_ID), its state won't be saved or restored.
android.view.View.java
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
......
if (state != null) {
......
container.put(mID, state);
}
}
}
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID);
if (state != null) {
......
onRestoreInstanceState(state);
......
}
}
}
I know this is an old question but I ran into a very similar issue and although the current answers are correct (its a duplicate ID issue), the actual answers given weren't working or quite in the right area.
I had a Fragment, and in that Fragment I had 2 custom controls, both with unique ID's, but internally in those controls they included the same common Layout, and so that Layout of course had an EditText with the same ID in both.
#wrkwrk is right in that its an ID issue, but changing the ID on the "include" isn't enough if the included layout contains the EditText, and if you want to include the same layout multiple times, you can't give it a different ID in the axml.
So for my work around, in the code for my custom control that loads the EditText in, after loading it in I simply changed the ID afterwards using the View.GenerateViewId() function.
So something like (this is in C#/Xamarin)
var editText = _root.FindViewById<EditText>(Resource.Id.entryEditText);
editText.Id = GenerateViewId();
That seemed to fix the issues for me. So I can have one common ID in the layout so my control can find it fine, but then it changes so multiple instances of that control in a fragment will all save off their EditText state fine when going back.
Alternatively, you could give it no ID in the layout but a Tag instead and do FindViewByTag and then assign it an ID after loading.

Correct way to save and restore state of multiple versions of the one layout

Consider the following overly simplified example. I have a LinearLayout with a list of items (EditTexts). The number of items is not known at compile time (for example it might be a game menu screen where the user has just selected the number of players and on this screen they are entering each individual player's name).
The obvious way to do this is to have a container layout defined in xml (R.layout.list), and individual item layouts defined in xml (R.layout.item); which would look something like this:
public class Main extends Activity {
private static final String FRAG_TAG = "ITEMS_FRAGMENT";
private static final int NUM_ITEMS = 3;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.list, null, false);
for (int i = 0; i<NUM_ITEMS; i++) {
ll.addView(getLayoutInflater().inflate(R.layout.item, null, false));
}
setContentView(ll);
}
}
With the R.layout.list and R.layout.item layouts respectively:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/list" />
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/item"
android:singleLine="true"/>
The problem is that on an orientation change, the fact that all items have the same ID (R.id.item) confuses the restoration process. The end result is that the state of the last item is restored to all items in the list (i.e. if the last item's EditText has "hi" in it, every EditText will have "hi" in it after the orientation chance).
In the instance of this in my code, each item has a CheckBox that I can get the state of with a isCheckboxChecked(LinearLayout list, int itemNumber) function that relies on the fact that each item has a view with ID R.id.checkbox. Is there a proper way to do view state restoration that accepts there may be multiple items inflated from the same XML resource (and consequently will have the same ID)?
(ListView is not appropriate for this case, but it handles this correctly. How does it restore state correctly when all of it's items typically have the same ID?)
Android stores states by associating them with element's ID. If all elements have same ID, then a state associated with this ID will be restored to all elements with this ID. LisView does it very differently. It always reads values from Adapter by element position. Because views with the same ID will have different position in the list, they will have different values too.
If your case you have couple of options.
1) You need to assign different ID'S to your items before adding them to the LinearLayout by calling setId() method. You need to define those ID's in Android resources firs (e.g. <item type="id" name="item_01" /> etc.).
2) You need to override onSaveInstanceState(Bundle outState), go through the children of LinearLayout and store their texts into outState bundle associating text with children's index. Then in onRestoreInstanceState() you need to read states from savedInstanceState and set them to your views correspondingly.
3) You use ListView and when user enters text, your adapter persists data (e.g. into a file or database). When configuration changes, or on the next start of Activity your adapter will read those stored values, and they will be restored properly.
Hope one of the options can help you.

editTexts changing their value when rotating the device

The Context
My application architecture is very similar to the default called "Swipe Views" when you create a new android project.
One activity, 3 Fragments, you can swipe through them. It uses the support library.
everything is up to date.
The device I'm testing on is a galaxy nexus running android 4.1.2
Support library r11, and SDK versions min=14 target=16
The Problem
The second Fragment holds a bunch of EditText which contain various numerical values read from sharedPreferences. When the Activity is first created, everything is fine the EditViews all have their correct value, but if I modify one of the values to, for instance 555, and then I rotate the device, all the EditText are now displaying 555 !
There is only one setText in the whole program and it is displayed later in this post, I tried to remove it, as expected when starting the activity all the EditTexts display the default value (666), I modify one and rotate the device, they all display the new value!
The details
One activity with a SectionsPagerAdapter, a ViewPager and several fragments for each page.
In one of the fragments (ConfigurationFragment) in the onViewCreated method I dynamically create lines with and EditText and an ImageButton.
those lines are actually in a layout (xml file) which I inflate for each line to add, then I get the editText and call setText to set it's value to what it should be.
onViewCreated method:
public void onViewCreated(View view, Bundle savedInstanceState)
{
// populate ports list on view creation
ArrayList<String> listenPorts = mServerManagerThread.getListenPorts();
for (String port : listenPorts)
{
EditText v = ((EditText) addListenPortItem().getChildAt(0));
v.setText(port);
}
super.onViewCreated(view, savedInstanceState);
}
naturally the method getListenPorts returns the same list each time it is called (on startup and when the device is rotated)
what addListenPortItem basically does:
// Instantiate a new "row" view.
final ViewGroup newView = (ViewGroup) LayoutInflater.from(viewGroup.getContext()).inflate(itemResource,
viewGroup, false);
// Set a click listener for the "X" button in the row that will remove
// the row.
newView.findViewById(R.id.delete_button).setOnClickListener(
new OnRemoveClickListener(mContainer, viewGroup, newView, emptyListTextResource));
viewGroup.addView(newView);
viewGroup is a linearLayout which will hold the newly created views
itemResource is the following layout
layout of one line:
<?xml version="1.0" encoding="utf-8"?>
<!--
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:layout_marginTop="8dp"
android:divider="?android:dividerVertical"
android:dividerPadding="8dp"
android:gravity="right"
android:orientation="horizontal"
android:showDividers="middle" >
<EditText
android:id="#+id/editPort"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="666" >
</EditText>
<ImageButton
android:id="#+id/delete_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/action_remove_item"
android:src="#drawable/content_remove" />
</LinearLayout>
any idea?
I've been staring at this bug for a couple hours now and I really have no clue what's going on ... I'm starting to suspect a bug in the SDK or the support library, but I know from experience the bug is most often in my brain :)
I found the solution: I moved the code from onViewCreated to onViewStateRestored(..) so that filling the EditTexts with their value is done after the state has been restored.
The problem was that all the EditTexts have the same id so when restoring the state it was messing it up.
That's not a bug, so don't be worried. The problem is that if you rotate the screen the Activity and the Fragments will be recreated. onCreateView() will be called again and this mixes things up and therefore you have such a weird behaviour. You can fix this by overriding these thwo methods:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Read values from the "savedInstanceState"-object and put them in your textview
}
#Override
protected void onSaveInstanceState(Bundle outState) {
// Save the values you need from your textview into "outState"-object
super.onSaveInstanceState(outState);
}

unable to set background image properly in getView method

Following shows the code and screenshot for displaying data in listview containing 3 textview.
I tried the following code in getView() method of Adapter class to give background image to alternate rows i.e. row no. 0,2,4,etc.
When I try to implement this, I get the output as shown in screen-shots i.e. row are not getting effected appropriately.
Also, refer to below xml in which alignTop and alignBottom parameters are used, still on Index button click, 2nd and 3rd textview are not getting background color appropriately.
EDIT - If I use colour instead of image for background, it works well for all cases except for Index button.
if (count % 2 == 0) {
try {
InputStream is = ctx.getAssets().open("cellbg.png");
Drawable d = Drawable.createFromStream(is, "cellbg");
holder.txtFirst.setBackgroundDrawable(d);
holder.txtSecond.setBackgroundDrawable(d);
holder.txtThird.setBackgroundDrawable(d);
Log.v("count=", "" + (count++));
} catch (Exception e) {
throw new Error(" exception in TableListAdapter " + e.getMessage());
}
} else {
Log.v("else count==", "" + (count++));
}
list structure(xml)
<TextView
android:id="#+id/FirstText"
android:layout_width="80dp"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/SecondText"
android:layout_width="55dp"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/FirstText"
android:layout_alignBottom="#+id/FirstText"/>
<TextView
android:id="#+id/ThirdText"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/FirstText"
android:layout_alignBottom="#+id/FirstText"/>
1st screen shows the list structure at the time of application launch, where by default, Gainers button is pressed.
2nd shows an instance of pressing Index button.
3rd shows an instance of pressing Gainers button again after pressing index button.
4th shows the layout when List item is clicked.
What am I doing wrong?
ANY HELP WILL BE LIFE-SAVER !!!
Couple of things.... not sure what the different buttons are and what they do, but it should not matter...
You should set the background also when (count % 2 != 0) because the views can be reused in the list, so better be safe.
Also, instead of setting the background for the different TextViews individually, why not set it only for the ViewGroup that contains all those TextViews (I imagine a LinearLayout or something like that) ?
Just to double check, count is the argument you get when getView gets called ?
Maybe you could post a bit more code of your getView method, that would probably help (to get a better answer).
This looks the a view reuse problem to be but I can't be sure because I don't see that part of the code. In your getView do you reuse views (i.e. do you make use of convertView parameter)? If you do, then that's your answer. You have to always set the background to the color you want. Something like this:
if (count % 2 == 0) {
...
holder.txtFirst.setBackgroundDrawable(d);
} else {
holder.txtFirst.setBackgroundDrawable(myDefaultRowColor);
}
you are using setbackgrounddrawable but i think you have to use below code to set image
holder.txtFirst.setImageResource(R.drawable.Imagename);

Android button id

If I have created a button in xml and i want to use this button multiple times but give them unique id's, how do i do that? I don't know how many buttons i will have, so i can't create a number of buttons in my xml-file. I want to be able to change the buttons id while running the program. I've tried to do button.setId() but then everything breaks and wont work.
You can make an independent button.xml file (or even do it as a style), then inflate that in your code as needed.
For instance, let's say you have an array of strings representing a list of countries, and you want a button for each country. This is nice because if you add or remove any, you only have to modify the array, not your xml or the for loop.
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
String[] countries = {"US", "Canada", "UK", "Australia"};
String country;
// Don't forget to add the following layout to your xml.
LinearLayout buttonLayout = (LinearLayout) findViewById(R.id.buttonLayout);
buttonLayout.removeAllViews();
for (int i = 0; i < countries.length(); i++) {
country = countries.getString(i);
Button temp = (Button)inflater.inflate(R.layout.button);
// Don't forget that id is an int, not a string.
button.setId(id);
button.setText(country);
buttonLayout.addView(temp);
}
Bonus: you can also include this button in another xml file, and update the id in the include statement as follows:
<include layout="#layout/button"
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1"
/>
The question is not very clear, but I think you can use dynamic button creation.
Button button = new Button();
// init it here
layout.add(button, new LayoutParams(...));
give id in xml like
<Button android:id="#+id/close"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="#string/title_close" />
When you are not sure of the instances you need in your application of a certain widget, then go for dynamic creation.
XML is mostly for static creation.

Categories

Resources