0

Basically

  • I have two layouts activity_main_layout.xml as well as layout2.xml

  • I have created two tabs on the ActionBar using the getActionbar().newTab() method

  • I have a Button and a text box (EditText) in activity_main_layout.xml (first tab)

  • Both the button and the text box have a dynamically entered text: "Test 123"

  • When Tab 2 is selected, I have a statement of code that says: setContentView(R.layout.layout2) so that it shows the second layout and similarly for Tab 1: setContentView(R.layout.activity_main)

    When Tab 1 is reselected, the dynamically entered text "Test 123" is gone and I am left with the text that was set in design time in the XML. So I am currently forced the save the contents of the Buttons and EditTexts before a tab is selected and restore them after it was selected.

I don't think this is the right way to do it. I am already utilizing onSaveInstanceState() and onRestoreInstanceState() with savedInstanceState.putXX and getXX etc to save the contents when the screen rotates but it doesn't seem to work with the current issue. I have been unable to find an answer so far. Does anyone have any clues please? Any responses will be appreciated.

Community
  • 1
  • 1
rwx
  • 573
  • 1
  • 7
  • 22

4 Answers4

0

If you have only 2 tabs, why don't you use a pageadapter? You won't lose any information until you change the position of the mobile or quit the app.

Basically is something like that (I put you a scracth of one of my codes).

NOTE: I use packet ShelockActivity to have tabsmenu for android > 2.0

MAINCLASS:

public class MainClass extends SherlockActivity implements ActionBar.TabListener {
ViewPager myPager;
AdapterClass adapter;
Context con = null;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    con = this.getBaseContext();
    //Restore info in case flip mobile      
    final Myobject data = (Myobject) getLastNonConfigurationInstance();     
    Bundle extras = getIntent().getExtras(); 

   //Tab bar
    getSupportActionBar().setDisplayShowHomeEnabled(false);
    getSupportActionBar().setDisplayShowTitleEnabled(false);
    getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    //loop for add more
    ActionBar.Tab tab = getSupportActionBar().newTab();
    tab.setText("text tab");

            if (data != null) {
            getdatatoyourobject(data);
        }
    myPager = (ViewPager) findViewById(R.id.panelpager);    
    myPager.setAdapter(adapter);    
    myPager.setOnPageChangeListener(new OnPageChangeListener() {
          @Override
          public void onPageSelected(int index) {
              getSupportActionBar().setSelectedNavigationItem(index);            
          }
    adapter = new AdapterClass(Parameters for your adapterclass);


        @Override
        public void onPageScrollStateChanged(int arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
            // TODO Auto-generated method stub

        }

    });
}
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    // TODO Auto-generated method stub
        myPager.setCurrentItem(tab.getPosition(),true);         
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    // TODO Auto-generated method stub

}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
    // TODO Auto-generated method stub

}
@Override
public Object onRetainNonConfigurationInstance() {
    final Myobject a = new Myobject();
    return a;       
}

public class Myobject {
    Type variable = adapter.variable; //do for all variables to restore     
}

public void getdatatoyourobject(Myobject a){
    adapter.variable = a.variable;    //do for all variables to restore     
}
}

ADAPTERCLASS:

  public class Adapterclass extends PagerAdapter {

  public int getCount() {
      return 2; //NUMBER OF PAGES
  }  

  public Adapterclass () {      
  }  

 public View view = null; //create rest of variables to restore as public

 public Object instantiateItem(View collection, int position) {

  LayoutInflater inflater = (LayoutInflater) collection.getContext()
          .getSystemService(Context.LAYOUT_INFLATER_SERVICE);


  switch (position) {
  case 0:
      view = inflater.inflate(R.layout.page1, null);
       //bla bla bla
       break;
//do more cases (pages)
  }
      ((ViewPager) collection).addView(view, 0);

     return view;
 }

 @Override
 public void destroyItem(View arg0, int arg1, Object arg2) {
      ((ViewPager) arg0).removeView((View) arg2);

  }

  @Override
  public boolean isViewFromObject(View arg0, Object arg1) {
      return arg0 == ((View) arg1);

  }

  @Override
  public Parcelable saveState() {
      return null;
  }
}

MAIN.XML:

<?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">

<android.support.v4.view.ViewPager
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/panelpager"/>

Learning from masters
  • 2,032
  • 3
  • 29
  • 42
  • I actually need more then two tabs, but I used two tabs for this example only to simplify the problem. I posted the code. – rwx Dec 30 '12 at 22:00
  • hi, would you be able to provide me a fully working example please based on the code I posted above but based on your solution. Create a button, set its text in run time, have a code that changes the views/layouts and when the tab is reselected, the button text remains. – rwx Dec 30 '12 at 23:38
0

Apart from as 'Learning from masters' states, there are better ways to build this structure up (page adapter / page viewer / fragments...) it should still work fine using your Bundle saveInstanceState correctly.

Can you post your code?

edit after seeing code:

You probably need to check your Bundle savedInstanceState in onCreate() instead. This looks a bit like

    @Override
    public void onCreate(Bundle savedInstanceState) {
         [...]
     if(savedInstanceState != null){
       //restore your stuff from the Bundle
      }else{
       // there is nothing to restore, so (down)load your data.
       }
    }

A good detailed explanation you can find around https://stackoverflow.com/a/6722854/1868384

Community
  • 1
  • 1
Stefan de Bruijn
  • 6,289
  • 2
  • 23
  • 31
0

here is the code I have:

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >

<Button
    android:id="@+id/btn_One"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
    android:layout_weight="1.70"
    android:text="Activity_main.xml - Design Time Text"
    android:textSize="12sp"
    android:onClick="btnOne_OnClick"
    android:textStyle="normal" />

layout2.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >

            <Button
                android:id="@+id/btn_Two"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Layout2.xml - Design Time Text"
                android:textSize="12sp"
                android:textStyle="normal" />

In the main code, pay attention to the comments please:

public class MainActivity extends Activity implements ActionBar.TabListener {

ActionBar.Tab Tab_1 = null, Tab_2 = null;
Button btn_One = null, btnTwo = null;

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ///////////////////////////////////////////////////////////////////
    ActionBar actionbar = (ActionBar) getActionBar();
    actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    Tab_1 = actionbar.newTab().setText("Tab 1");
    Tab_2 = actionbar.newTab().setText("Tab 2");

    Tab_1.setTag(1);
    Tab_2.setTag(2);


    Tab_1.setTabListener(this);
    Tab_2.setTabListener(this);        

    actionbar.addTab(Tab_1);
    actionbar.addTab(Tab_2);
    ///////////////////////////////////////////////////////////////////

    btn_One = (Button)this.findViewById(R.id.btn_One);
}


public void btnOne_OnClick(View v) {
    ((Button)v).setText("Clicked");
    btn_One = ((Button)v);
}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {}

// Here is what the problem is:
// After you click btn_one, the text changes to "Clicked" - good! - however
// after Tab 1 gets reselected, the text off btn_One (btn_One) gets reset back to the one
// set in design time: "Activity_main.xml - Design Time Text", so the "Clicked" text disappears
// The interesting thing is, when you rotate the screen, the text gets re-initialized to "Clicked" and that's
// because of onRestoreInstanceState(). So it looks like when a tab gets reselected, the design time screen
// gets re-drawn and onRestoreInstanceState doesn't get recalled.
// My "workaround" for the above issue would be to create a global 
// "Bundle" poiner and pass the address of the one from onSaveInstanceState(Bundle savedInstanceState)
// to the global Bundle pointer and then call onRestoreInstanceState(*the_global_bundle_pointer*) 
// when a tab gets reselected so that it restores the state.

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {

    int selectedTab = (Integer) tab.getTag();

    if (selectedTab == 1) this.setContentView(R.layout.activity_main);
    if (selectedTab == 2) this.setContentView(R.layout.layout2);

}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) 
{
  if (btn_One != null) savedInstanceState.putString("btn_One", btn_One.getText().toString());

  super.onSaveInstanceState(savedInstanceState);  
}  

// When the instance is restored, reload all data
@Override  
public void onRestoreInstanceState(Bundle savedInstanceState) 
{  
    super.onRestoreInstanceState(savedInstanceState);

    if (btn_One != null) btn_One.setText(savedInstanceState.getString("btn_One").toString());  
}       

}

Also, the reason I say two tabs is so that I can demonstrate the problem in its simplest way possible so people can understand what I am trying to solve. My real project uses more then two tabs.

My "workaround" for the above issue would be to create a global "Bundle" poiner and pass the address of the one from onSaveInstanceState(Bundle savedInstanceState) to the global Bundle pointer and then call onRestoreInstanceState(*the_global_bundle_pointer*) when a tab gets reselected so that it restores the state.

What do you think?

rwx
  • 573
  • 1
  • 7
  • 22
0

Thank you both. It turns out with Android, once a new content layout is set to an Activity, the existing UI elements such as a text box, button of the previous layout etc are no longer available so trying to access them will cause an access violation. Once you set a content layout, it looks like the UI elements are rebuilt from scratch hence for losing all of the data. Well this is what I personally noticed and it certainly seems to be a default behavior. I don't know if this behavior can be changed.

I made my own "custom solution". All I did is create a new class, with a bunch of data members in it. Each time I set a new content to an Activity such as when I apply a layout when a tab is clicked or the device is rotated (onSaveInstance), I save/restore values from this class. I used to use the Bundle putInt, putString, putParcelable etc but I decided to take a different route. With my way, I am in-control and I know exactly what is going on with the code.

rwx
  • 573
  • 1
  • 7
  • 22