Application saving textbox state on orientation change? - android

I'm trying to understand the total flow of Android applications, and I'm running into what I consider to be a strange situation (note: Im VERY new to android programming).
I made a test application with just a multiline edit text field. I wrote 1234 on the field. Without anything else, with no other changes to the default Eclipse ADT made application and without overriding anything specifically in the backend, I changed orientation. 1234 remained. I then hit the home button, then opened it from recent apps. 1234 remained.
My understanding of the app lifecycle was that the application was stopped and started when the home button is pressed, and the application is destroyed and created when the application's orientation is changed. If this is correct, is there some form of automatic state keeping that takes place? I was under the assumption that I had to pull, from the state bundle, individual variables and restore them myself. Is that not correct?
Any explanation of this that a more seasoned Android dev could give would be great. I've been trying to find the appropriate answer, but to no avail.
MainActivity.java
package com.example.teststate;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Activity_Main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.teststate.MainActivity" >
<EditText
android:id="#+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="30dp"
android:ems="10"
android:inputType="textMultiLine" >
<requestFocus />
</EditText>
</RelativeLayout>
Snippet from Manifest.xml
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Thank you!

The reason this happens is because the view explicitly saves and restores its state on orientation changes, by overriding View#onSaveInstanceState() and View#onRestoreInstanceStance(Parcelable). Here's the of the implementation in TextView (super class of EditText)
#Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
boolean save = mFreezesText;
int start = 0;
int end = 0;
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
save = true;
}
}
if (save) {
SavedState ss = new SavedState(superState);
// XXX Should also save the current scroll position!
ss.selStart = start;
ss.selEnd = end;
if (mText instanceof Spanned) {
Spannable sp = new SpannableStringBuilder(mText);
if (mEditor != null) {
removeMisspelledSpans(sp);
sp.removeSpan(mEditor.mSuggestionRangeSpan);
}
ss.text = sp;
} else {
ss.text = mText.toString();
}
if (isFocused() && start >= 0 && end >= 0) {
ss.frozenWithFocus = true;
}
ss.error = getError();
return ss;
}
return superState;
}
[...]
#Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
// XXX restore buffer type too, as well as lots of other stuff
if (ss.text != null) {
setText(ss.text);
}
if (ss.selStart >= 0 && ss.selEnd >= 0) {
if (mText instanceof Spannable) {
int len = mText.length();
if (ss.selStart > len || ss.selEnd > len) {
String restored = "";
if (ss.text != null) {
restored = "(restored) ";
}
Log.e(LOG_TAG, "Saved cursor position " + ss.selStart +
"/" + ss.selEnd + " out of range for " + restored +
"text " + mText);
} else {
Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
if (ss.frozenWithFocus) {
createEditorIfNeeded();
mEditor.mFrozenWithFocus = true;
}
}
}
}
if (ss.error != null) {
final CharSequence error = ss.error;
// Display the error later, after the first layout pass
post(new Runnable() {
public void run() {
setError(error);
}
});
}
}
As you can see, it saves the state in a Parcelable object, which is then passed along to a view with the same android:id in the new instance, and onRestoreInstanceState() gets called on the new view. If you create a custom view, which doesn't just consists of other views, you might want to override these methods.

I am not able to give you a code level explanation. But it is one of those things android promised to do. You can check out this Recreating activity
Note: In order for the Android system to restore the state of the views in your activity, each view must have a unique ID, supplied by the android:id attribute.

Related

Is there an equivalent for shouldSelectViewController in android for BottomNavigationView?

I have a BottomNavigationView in my android activity, which consists of 4 menuItem. When I tap on the downloads menu I check if there are any downloaded contents available, if available I will allow the navigation to happen and if there is no downloaded content I will show a Toast stating the same and I want the previous tab to remain selected. In iOS I can use the delegate method shouldSelectViewController to determine whether navigation can be allowed or not.
The method signature is specified below:
- (BOOL)tabBarController:(UITabBarController *)tabBarController
shouldSelectViewController:(UIViewController *)viewController;
I tried reselecting the previously selected tab, as a result, the previous item is retained but the selected item color is still assigned to the downloads tab.
private void BottomNavigationItemSelected(object obj, BottomNavigationView.NavigationItemSelectedEventArgs args)
{
Android.Support.V4.App.Fragment fragment = null;
Android.Support.V4.App.Fragment currentFragment = SupportFragmentManager.FindFragmentById(Resource.Id.content_frame);
string title = "";
if (args.Item.ItemId == Resource.Id.menu_explore)
{
_selectedToolbarId = args.Item.ItemId;
title = Resources.GetString(Resource.String.shelf_title);
fragment = _exploreFragment;
_fragmentTag = "Home";
}
else
{
title = args.Item.TitleFormatted.ToString();
}
if (args.Item.ItemId == Resource.Id.menu_dashboard)
{
//COULD BE MADE CONFIGURABLE
//fragment = _dashboardFragment;
_selectedToolbarId = args.Item.ItemId;
fragment = _redesignDashboard;
_fragmentTag = "Dashboard";
}
else if (args.Item.ItemId == Resource.Id.menu_more)
{
_selectedToolbarId = args.Item.ItemId;
fragment = _moreFragment;
_fragmentTag = "More";
}
else if (args.Item.ItemId == Resource.Id.menu_report)
{
_selectedToolbarId = args.Item.ItemId;
fragment = _reportFragment;
_fragmentTag = "Report";
}
else if (args.Item.ItemId == Resource.Id.menu_downloads)
{
List<Product> _downloadProducts = DBService.GetDB().GetDownloadedProducts();
if (_downloadProducts == null || _downloadProducts.Count == 0)
{
_bottomNavigationView.SelectedItemId = _selectedToolbarId;
Toast.MakeText(this, "No downloaded products", ToastLength.Short).Show();
args.Item.SetChecked(false);
}
else
{
_downloadGalleryFragment = new DownloadGalleryFragment(_downloadProducts);
fragment = _downloadGalleryFragment;
_fragmentTag = "Downloads";
}
}
if (fragment != null)
{
_toolbarTitle.Text = title;
ToggleTitle(true);
SupportFragmentManager.BeginTransaction().SetCustomAnimations(Resource.Animation.fab_slide_in_from_right, Resource.Animation.fab_slide_out_to_left).Replace(Resource.Id.content_frame, fragment, _fragmentTag).Commit();
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/menu_explore"
android:enabled="true"
android:title="#string/explore"
android:icon="#drawable/explore_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_dashboard"
android:enabled="true"
android:title="#string/dashboard"
android:icon="#drawable/Dashboard_new_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_report"
android:enabled="true"
android:title="#string/reports"
android:icon="#drawable/dashboard_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_downloads"
android:enabled="true"
android:title="#string/menu_downloads"
android:icon="#drawable/download_icon"
app:showAsAction="always" />
<item
android:id="#+id/menu_more"
android:enabled="true"
android:title="#string/more_bottombar"
android:icon="#drawable/more_icon"
app:showAsAction="always" />
</menu>
There is no such delegate that works as shouldSelectViewController but what you can do is get the menu items of the bottom nav bar and disables these menu items:
Something like this:
var listMenuItems = new List<IMenuItem>();
for (int i = 0; i < bottomNav.Menu.Size(); i++)
{
listMenuItems.Add(bottomNav.Menu.GetItem(i));
}
Once you have them here you can manipulate them as you like, to enable or disable an item just use the SetEnabled method that takes a boolean as a parameter.
Seems like this is an issue with Android or the Bottom Navigation View. When I executed the reselection of the previous fragment after a small delay of 50milliseconds its working fine. ie the reselected fragment or the previous fragments icon gets highlighted as required.
if (args.Item.ItemId == Resource.Id.menu_downloads)
{
List<Product> _downloadProducts = DBService.GetDB().GetDownloadedProducts();
if (_downloadProducts == null || _downloadProducts.Count == 0)
{
_readProgressTimerTask = new Timer
{
Enabled = true,
Interval = 50,
AutoReset = false
};
_readProgressTimerTask.Elapsed += OnProgressCheckTimeElapsed;
Toast.MakeText(this, this.Resources.GetString(Resource.String.no_downloads), ToastLength.Short).Show();
}
else
{
_downloadGalleryFragment = new DownloadGalleryFragment(_downloadProducts);
fragment = _downloadGalleryFragment;
_fragmentTag = "Downloads";
}
}
private void OnProgressCheckTimeElapsed(System.Object source, ElapsedEventArgs args)
{
this.RunOnUiThread(() =>
{
_bottomNavigationView.SelectedItemId = _selectedToolbarId;
});
}
I recommend that you first read the guideline available here to implement bottom navigation for android.
That what you want doesn't make much sense, an empty screen should be shown saying something like "There is not content available" or the message you like.
In this link BottomNavigationView you can find all public methods and listeners you can set to bottom navigation. As for your case, you might be interested in adding these two.
setOnNavigationItemReselectedListener()
setOnNavigationItemSelectedListener()
Best regards, Pedro.

Why are all the indexes in my array being set to null after being set to another activity?

I have a game app where you can go into your inventory, which is another activity, and select items that you have picked up while playing the game then when you go back to the main game activity the items are used. To keep track of which of the 6 items the user wants to use I created a boolean array and set the corresponding index to true when an item is clicked then send it back to the main game activity. But when I go back to the main activity the app crashes when I try to check if an index is true. I used the debugger and found that all the indexes in my boolean array were set to null but they were all false in the inventory activity.
Game Activity
The app crashes once it gets to the if statement.
#Override
public void onResume(){
super.onResume();
//this code checks if a item was used and calls the items method
Intent Item= getIntent();
boolean[] usedItem = Item.getBooleanArrayExtra(Inventory.tools);
if (usedItem[0] == true) {
breadItem();
} else if (usedItem[1] == true) {
potionItem();
} else if (usedItem[2] == true) {
swordItem();
}
}
Inventory Activity
I have temporarily commented out working code that I believe has nothing to do with the problem
Intent Item= null;
public static final String tools= "an item was used";
boolean[] itemUsed = new boolean[6];
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inventory);
/*bread= (ImageButton) findViewById(R.id.ibBread);
potion =(ImageButton) findViewById(R.id.ibPotion);
sword =(ImageButton) findViewById(R.id.ibSword);
wings =(ImageButton) findViewById(R.id.ibWings);
bubble =(ImageButton) findViewById(R.id.ibBubble);
teleport =(ImageButton) findViewById(R.id.ibTeleport);*/
Item = new Intent(Inventory.this, MainGame.class);//data sent to MainGame activity
/*ImageButton[] items = {bread, potion, sword, wings, bubble, teleport};
Intent bag = getIntent();
boolean[] boolItems = bag.getBooleanArrayExtra(MainGame.booInventory);
for(int x =0; x <6; x +=1) {
if (boolItems[x] == false)
{
items[x].setImageDrawable(getDrawable(R.drawable.question_mark));
}
}*/
Item.putExtra(tools, itemUsed);
}
One of the six methods that set an index to true when the item is pressed. Also in Inventory activity
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void breadAction(View v)//lowers hunger by 2
{
bread= (ImageButton) findViewById(R.id.ibBread);
if(bread.getDrawable() != getDrawable(R.drawable.question_mark))
{
//set image back to question mark
bread.setImageDrawable(getDrawable(R.drawable.question_mark));
itemUsed[0] = true;
Item.putExtra(tools, itemUsed);
}
}

Android App Functionality Issue [duplicate]

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 8 years ago.
The functionality of my program is not working properly. It is not doing what I need it to do. I don't have any errors in the code but the app isn't doing what I want it to do.
**UPDATE: Okay thank you very much for the help. the button displays a value for TextView ce. However, the app does not seem to run my ifstatement to get a new value of CE1 which would be = 4*credit_1. The app is taking the initialized value of CE1 which is = 0, and converting that to a string CE1s. Hence dispalying 0.0 in TextView ce. Why is the app not taking my ifstatement if(grade_1.equals("A")) into consideration?
Here is the MainActivity.java:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText course1 = (EditText)findViewById(R.id.Course1);
EditText credit1 = (EditText)findViewById(R.id.editText7);
final EditText grade1 = (EditText)findViewById(R.id.editText13);
final Button b = (Button)findViewById(R.id.button1);
final TextView ce = (TextView)findViewById(R.id.textView5);
String grade_1 = grade1.getText().toString();
String text = credit1.getText().toString();
if (!TextUtils.isEmpty(text)) {
// call parseDouble in here
double credit_1 = Double.parseDouble(text);
double CE1=0;
if (grade_1 =="A")
CE1 = 4*credit_1;
final String CE1s = Double.toString(CE1);
b.setOnClickListener(new OnClickListener(){
public void onClick(View v){
ce.setText(CE1s);
}
});
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
}
}
Here is the AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rutgersgpacalculator"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.rutgersgpacalculator.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
make sure that you verify if text is not null and it's rode when you click it. You should also consider verify without TextUtils like if (text != null && text.length > 0) {...}
b.setOnClickListener(new OnClickListener(){
public void onClick(View v){
// get the text when you click it to make sure it's live while click
String text = credit1.getText().toString();
if (!TextUtils.isEmpty(text)) {
double credit_1 = Double.parseDouble(text);
ce.setText(Double.toString( (grade_1.equals("A")) ? 4*credit_1 : 0));
}
});

android - some views are not "visible" after activity restart

Hello Android developers,
i'm facing a strange behaviour of some views after activity restart - there are some views, which are not "visible", but they are layouted and react on Touch actions.
I'm trying to keep the application follow Android lifetime guide. I drop the Activity to background and let system to close my activity. Then I navigate back to my activity, which is recreated. There is no problem with data to be corrupted (saved in db with every change made), but the views are. There are some properly shown, but everything inside TableView, which is inside ScrollView, is not visible. If I call getVisibility() on any of not shown views, i get that it is visible. As I mentioned above, views are not "visible", but react on Touch and scroll events, like they were properly shown.
This is also hard (impossible) to debug, cause when the app is closed, debbuger is disconnected. But anyway, recreation follows the same methods callback - onCreate(), onStart(), onResume(), so once created, why there's problem other time? The only difference to me is that there is a null Bundle in onCreate(Bundle) when Activity is newly created and not null when it is recreated by system. If the activity is only stopped (in background) but not closed by system, everything works fine.
I also tryed to override onSaveInstanceState() and onRestoreInstanceState() with nothing to be saved and restored (no call to super implementation), but it had no effect.
I'm using Android 4.1.1 and emulator 2.1.
Does anyone have any idea?
Thanks Ales
Here are (links to) screenshots to figure it out better:
Before activity is closed by system
After activity is recreated
Here are the layout files:
<!-- Header -->
<LinearLayout
android:id="#+id/startlistHeaderLayout" xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView android:id="#+id/textStartlistName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:gravity="left"
android:text=""
/>
<TextView android:id="#+id/textStarttime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:gravity="right"
android:text=""
android:layout_weight="1"
/>
</LinearLayout>
<!-- Results -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#id/resultsScrollView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:saveEnabled="false"
>
<TableLayout
android:id="#id/timingLapTableLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="2"
android:shrinkColumns="2"
android:saveEnabled="false"
>
<!-- rows are added in code -->
</TableLayout>
</ScrollView>
</LinearLayout>
<!-- R.layout.timing_row -->
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#id/timingTableRow"
android:saveEnabled="false"
>
<TextView
android:id="#id/textTimingPosition"
android:text=""
android:gravity="right"
android:paddingLeft="4dip"
android:saveEnabled="false"
/>
<TextView
android:id="#id/textTimingBib"
android:text=""
android:gravity="right"
android:paddingLeft="1dip"
android:saveEnabled="false"
/>
<TextView
android:id="#id/textTimingName"
android:text=""
android:lines="1"
android:ellipsize="end"
android:gravity="left"
android:paddingLeft="1dip"
android:paddingRight="5dip"
android:saveEnabled="false"
/>
<TextView
android:id="#id/textTimingBehind1"
android:text=""
android:gravity="right"
android:paddingRight="5dip"
android:saveEnabled="false"
/>
<TextView
android:id="#id/textTimingBehind2"
android:text=""
android:gravity="right"
android:paddingRight="5dip"
android:saveEnabled="false"
/>
</TableRow>
Here is part of Activity code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
loadPreferencesTimingTheme();
if (ownTheme) {
this.setTheme(theme);
} else {
this.setTheme(MainActivity.theme);
}
}
protected void onStart() {
super.onStart();
View lapView;
View v;
int sid;
setContentView(R.layout.startlist_edit);
sid = getIntent().getIntExtra(TimingActivity.STARTLIST_ID, -1);
if (sid == -1) {
Toast.makeText(this, "Sorry, could not load startlist.", Toast.LENGTH_LONG);
this.finish();
return;
}
tdb = new TimingDB(this);
sl = tdb.getStartlist(sid); // get Startlist from db into memory
// hide unnecessary columns
v = findViewById(R.id.timingLapTableLayout);
((TableLayout) v).setColumnCollapsed(0, true);
loadStartlist(sl); // inflate rows with startlist data and set listeners
// load the preferences and set preferences listener
loadPreferences();
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(prefsChangeListener);
}
public void onStop() {
super.onStop();
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(prefsChangeListener);
if (tdb != null) tdb.close();
}
protected void onSaveInstanceState (Bundle outState) {
// super.onSaveInstanceState(outState);
}
protected void onRestoreInstanceState (Bundle savedInstanceState) {
// super.onRestoreInstanceState(savedInstanceState);
}
protected void onDestroy() {
super.onDestroy();
if (tdb != null) tdb.close();
}
private void loadStartlist(StartList sl) {
// set startlist name & time
View v = findViewById(R.id.textStarttime);
((TextView) v).setText(SimpleDateFormats.ddmmyyyyhhmmss.format(new Date(sl.getStartTime())));
v = findViewById(R.id.textStartlistName);
((TextView) v).setText(sl.getName());
// set header onclicklistener
v = findViewById(R.id.startlistHeaderLayout);
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// show dialog to change startlist name and time
Bundle args = new Bundle();
args.putString(KEY_NAME, ((TextView) findViewById(R.id.textStartlistName)).getText().toString());
args.putString(KEY_STTIME, ((TextView) v.findViewById(R.id.textStarttime)).getText().toString());
if (Build.VERSION.SDK_INT < 8) {
dialogBundle = args;
showDialog(DIALOG_EDIT_STARTLIST);
} else {
showDialog(DIALOG_EDIT_STARTLIST, args);
}
}
});
// add competitors to startlist
int cnt = sl.getCompetitorsCount();
for (int i = 0; i < cnt; i++) {
Competitor c = sl.getCompetitorByIndex(i);
appendStartlistRow(c);
}
}
public void appendStartlistRow(final Competitor c) {
TableRow inflatedView = (TableRow) getLayoutInflater().inflate(R.layout.timing_row, null);
inflatedView.setTag(TAG_COMPETITOR, c);
inflatedView.setTag(new Integer(c.getBib()));
inflatedView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
TableLayout tl = (TableLayout) findViewById(R.id.timingLapTableLayout);
Bundle args = new Bundle();
args.putInt(KEY_INDEX, tl.indexOfChild(v));
if (Build.VERSION.SDK_INT < 8) {
dialogBundle = args;
showDialog(DIALOG_EDIT_COMPETITOR);
} else {
showDialog(DIALOG_EDIT_COMPETITOR, args);
}
}
});
// set up texts
TextView text = (TextView) inflatedView.findViewById(R.id.textTimingBib);
text.setText(c.getBib() + "");
text = (TextView) inflatedView.findViewById(R.id.textTimingName);
text.setText(c.getName());
text = (TextView) inflatedView.findViewById(R.id.textTimingBehind1);
text.setText(getFormatedStartTime(sl, c, Settings.STARTTIME_ABSOLUTE));
text = (TextView) inflatedView.findViewById(R.id.textTimingBehind2);
text.setText(getFormatedStartTime(sl, c, Settings.STARTTIME_RELATIVE));
// append the row
appendStartlistRow((ViewGroup) inflatedView);
}
public void appendStartlistRow(ViewGroup row) {
// set background and text colors
if (((((Integer) row.getTag()).intValue()) % 2) == Defs.VIEW_EVEN) {
row.setBackgroundColor(Defs.COLOR_BACKGROUND_EVEN);
for (int j = 0; j < row.getChildCount(); j++) {
((TextView) (row.getChildAt(j))).setTextColor(Defs.COLOR_TEXT_EVEN);
}
} else {
row.setBackgroundColor(Defs.COLOR_BACKGROUND_ODD);
for (int j = 0; j < row.getChildCount(); j++) {
((TextView) (row.getChildAt(j))).setTextColor(Defs.COLOR_TEXT_ODD);
}
}
((TableLayout) findViewById(R.id.timingLapTableLayout)).addView(row);
}
Defs.java:
public class Defs {
protected static final int VIEW_EVEN = 0;
protected static final int VIEW_ODD = 1;
protected static int COLOR_BACKGROUND_EVEN;
protected static int COLOR_BACKGROUND_ODD;
protected static int COLOR_BACKGROUND_SELECTED;
protected static int COLOR_BACKGROUND_SPYED;
protected static int COLOR_TEXT_ODD;
protected static int COLOR_TEXT_EVEN;
protected static int COLOR_TEXT_SELECTED;
protected static int COLOR_TEXT_SPYED;
private static boolean isInitialized = false;
protected static void init(Context c) {
if (isInitialized) return;
COLOR_BACKGROUND_EVEN = c.getResources().getColor(R.color.background_darker);
COLOR_BACKGROUND_ODD = c.getResources().getColor(R.color.background_lighter);
COLOR_BACKGROUND_SELECTED = c.getResources().getColor(R.color.background_selected);
COLOR_BACKGROUND_SPYED = c.getResources().getColor(R.color.background_spyed);
COLOR_TEXT_ODD = c.getResources().getColor(R.color.text_lighter);
COLOR_TEXT_EVEN = c.getResources().getColor(R.color.text_darker);
COLOR_TEXT_SELECTED = c.getResources().getColor(R.color.text_selected);
COLOR_TEXT_SPYED = c.getResources().getColor(R.color.text_spyed);
isInitialized = true;
}
}
Without any code it's next to impossible to say much about the problem itself. But I did want to say (would fit better as a comment but can't do it yet) that for a long list (like yours seems to be based on the screenshots) using an actual ListView with a custom ArrayAdapter might be a better solution. ListView re-uses views when scrolled thus reducing unnecessary view creation. Also, once you learn how to use the ListView, you'll probably find it easier than adding table rows manually. If those aren't familiar to you, check a tutorial here:
http://www.vogella.com/articles/AndroidListView/article.html
And if you decide to change from tables to a ListView, maybe the problem (whatever it is) disappears as well. You never know. :)

Android, Autocomplettextview, force text to be from the entry list

Is there a way to force the entry in an autocompletetextview to be one of the elements in the entrylist?
I've found a method called "performValidation" but im not sure what it actually does, and i havent been able to find much documentation or any examples.
The AutoCompleteTextView has a method called setValidator() that takes an instance of the interface AutoCompleteTextView.Validator as parameter. AutoCompleteTextView.Validator contains isValid() with which you can check the value that has been entered, and you can "fix" this string by implementing fixText().
Seems this is the best you can get with AutoCompleteTextView, as the documentation for AutoCompleteTextView.Validator states the following:
"Since there is no foolproof way to
prevent the user from leaving this
View with an incorrect value in it,
all we can do is try to fix it
ourselves when this happens."
If your list of elements is not too long, you are probably better off using a Spinner.
****** Edit: ******
I wipped together a quick example of how you can use this, hope it helps!
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<AutoCompleteTextView
android:id="#+id/input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Focus me to validate above text"/>
</LinearLayout>
-
public class AutoCompleteTextViewActivity extends Activity {
String[] validWords = new String[]{"", "snowboard", "bobsleigh", "slalom"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AutoCompleteTextView view = (AutoCompleteTextView)findViewById(R.id.input);
view.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, validWords));
view.setValidator(new Validator());
view.setOnFocusChangeListener(new FocusListener());
}
class Validator implements AutoCompleteTextView.Validator {
#Override
public boolean isValid(CharSequence text) {
Log.v("Test", "Checking if valid: "+ text);
Arrays.sort(validWords);
if (Arrays.binarySearch(validWords, text.toString()) > 0) {
return true;
}
return false;
}
#Override
public CharSequence fixText(CharSequence invalidText) {
Log.v("Test", "Returning fixed text");
/* I'm just returning an empty string here, so the field will be blanked,
* but you could put any kind of action here, like popping up a dialog?
*
* Whatever value you return here must be in the list of valid words.
*/
return "";
}
}
class FocusListener implements View.OnFocusChangeListener {
#Override
public void onFocusChange(View v, boolean hasFocus) {
Log.v("Test", "Focus changed");
if (v.getId() == R.id.input && !hasFocus) {
Log.v("Test", "Performing validation");
((AutoCompleteTextView)v).performValidation();
}
}
}
}
Another alternate way(comments are mentioned inline) :
AutoCompleteTextView txt_site_name = findViewById(R.id.some_auto_text);
// Get the string array for the countries
String[] countries = getResources().getStringArray(R.array.ncr_parking_list_array);
// Create the adapter and set it to the AutoCompleteTextView
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_dropdown_item_1line, countries);
// txt_site_name is name of the AutoComplete text view. or AutoCompleteTextView txt_site_name
txt_site_name.setAdapter(adapter);
txt_site_name.setValidator(new AutoCompleteTextView.Validator() {
#Override
public boolean isValid (CharSequence text){
//some logic here returns true or false based on if the text is validated
if(text == "This is what I want")
return true;
else
return false;
}
#Override
public CharSequence fixText (CharSequence invalidText){
//If .isValid() returns false then the code comes here
//do whatever way you want to fix in the users input and return it
return "This is what I want"
}
});
Reference: AutoCompleteTextView.Validator

Categories

Resources