android

Dynamically Changing Android Views with XML Layouts

| 10 Comments

Dynamically changing Android views that are defined in code is fairly trivial – you already have references to the views in your class and you created them once. Changing a view created in code can be as simple as calling viewGroup.removeAllViews(). However, changing a view that’s defined completely as an XML layout can be a bit trickier.

Goal

The project I’m working on has a High Scores activity that, among other things, presents an EditText field for a player to enter his name when a game is done and submit his score.  Once the name is submitted, I want to remove the TextView label, the EditText field and the Submit Button, and replace them with a “New Game” button.

Justification

Because layouts can be created either in XML or in code, you could probably make do without ever having to do dynamic XML layout loading. That being said, there are some clear advantages as to why I think one may want to do so:

  1. Code cleanliness.  Doing anything more than basic layouts in code can get very messy, very fast.
  2. Code re-use.  It’s extremely easy to inflate an XML layout into a specified view with one or two lines of code
  3. Performance.  Creating the objects necessary for an in-code layout leads to unnecessary garbage collection.  As per the Android Designing for Performance article, “avoid creating short-term temporary objects if you can.”
  4. Attribute availability.  Defining Views in an XML layout exposes attributes that are not always available by object methods.

Possible disadvantages:

  1. It make take more time to do an XML layout versus defining the layout in code, especially if there are only one or two interface elements that need to be changed.

After thinking about what I want to accomplish, it makes sense for me to use XML layouts for the dynamic view changes I need.  It would be more than just a few lines of code to do my layout changes without utilizing XML layouts.

Planning

My plan for doing the layouts in XML is to create three separate layouts:

  1. score_submit.xml that will contain the label, EditText field, and “Submit” Button
  2. score_submitted.xml that will contain the “New Game” button and possibly an “End Game” button – this is what we want to be displayed when the Submit button from the previous layout is clicked
  3. The main score.xml that will contain the layout of the activity and include one of the other two layouts at a time

Creating XML Layouts

First, I created the score.xml layout.  Add a few elements, build, run.  Add a few more, build, run.  The resulting code is as follows:

score.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:background="@drawable/game_bg"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">

	<View
		android:layout_width="fill_parent"
		android:layout_height="20dp" />

	<View
		android:layout_width="fill_parent"
		android:layout_height="1px"
		android:background="#000"
		android:paddingTop="8dp" />

	<TextView
		android:id="@+id/score_result"
		android:layout_width="fill_parent"
		android:layout_height="50dp"
		android:gravity="center"
		android:textSize="30sp"
		android:textColor="@color/gameTitleColor"
		android:background="@drawable/translucent_blue"
		android:textStyle="bold"
		android:text="Score: 205" />

	<View
		android:layout_width="fill_parent"
		android:layout_height="1px"
		android:background="#000"
		android:paddingBottom="8dp" />

	<!-- This is where the include for the score_submit.xml will go -->

	<View
		android:layout_width="fill_parent"
		android:layout_height="1px"
		android:background="#000" />

	<TextView
		android:id="@+id/lbl_overview"
		android:layout_width="fill_parent"
		android:layout_height="30dp"
		android:textSize="15sp"
		android:textColor="@color/gameTitleColor"
		android:background="@drawable/translucent_blue"
		android:text="High Scores"
		android:gravity="center" />

	<View
		android:layout_width="fill_parent"
		android:layout_height="1px"
		android:background="#000" />

	<LinearLayout
		xmlns:android="http://schemas.android.com/apk/res/android"
		android:orientation="horizontal"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:paddingLeft="8dp"
		android:paddingRight="8dp"
		android:paddingTop="10dp"
		android:paddingBottom="10dp"
		android:gravity="center">

		<Button
			android:id="@+id/by_ref_score"
			android:layout_height="wrap_content"
			android:layout_width="wrap_content"
			android:paddingLeft="8dp"
			android:paddingRight="8dp"
			android:text="By Reference" />

		<Button
			android:id="@+id/by_verse_score"
			android:layout_height="wrap_content"
			android:layout_width="wrap_content"
			android:paddingLeft="8dp"
			android:paddingRight="8dp"
			android:text="By Verse" />

		<Button
			android:id="@+id/fitb_score"
			android:layout_height="wrap_content"
			android:layout_width="wrap_content"
			android:paddingLeft="8dp"
			android:paddingRight="8dp"
			android:text="Fill in the Blanks" />
	</LinearLayout>
</LinearLayout>

If that looks like a lot of code, it’s really not. Most of the generic Views are used for spacing or the 1-pixel lines around the colored TextViews. Here’s what the layout looks like when added to an activity:

Between the Score and the High Scores headers is where I want my submit name section to go. Since I want the submit name layout to be displayed first, I created that XML layout next:

score_submit.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="horizontal"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:paddingLeft="8dp"
	android:paddingRight="8dp"
	android:paddingTop="20dp"
	android:paddingBottom="20dp"
	android:gravity="center">

	<TextView
		android:id="@+id/lbl_submit_name"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:textSize="15sp"
		android:textColor="@color/gameTitleColor"
		android:text="Name:" />

	<EditText
		android:id="@+id/txt_submit_name"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_weight="1"
		android:background="@android:drawable/editbox_background" />

	<Button
		android:id="@+id/submit_score"
		android:layout_height="wrap_content"
		android:layout_width="wrap_content"
		android:paddingLeft="8dp"
		android:paddingRight="8dp"
		android:text="Submit" />

</LinearLayout>

Loading this in the score.xml is easier than one might think. I used the Android developer article Creating Reusable UI Components for the information. All I had to do is add the following line where I wanted to include the score_submit.xml:

<include
		android:id="@+id/score_name_entry"
		layout="@layout/score_submit" />

Here’s the resulting output:

For the last XML layout I put together score_submitted.xml as follows:

score_submitted.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="horizontal"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:paddingLeft="8dp"
	android:paddingRight="8dp"
	android:gravity="center">

	<Button
		android:id="@+id/new_game"
		android:layout_height="wrap_content"
		android:layout_width="wrap_content"
		android:paddingLeft="8dp"
		android:paddingRight="8dp"
		android:text="New Game" />

</LinearLayout>

Right now it only has one button (I may add more in the future) but this will do for the sake of the article.

Dynamically switching layout in code

So we have all three XML layouts and running the program at this point produces the screenshot above. Now to load the score_submitted.xml when the “Submit” button from score_submit.xml is pressed.

I begin by getting a reference to the “Submit” button and creating an onClickListener for it in the activity onCreate:

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.score);

		// Watch for button clicks.
		Button button = (Button)findViewById(R.id.submit_score);
		button.setOnClickListener(submitListener);
	}

	private OnClickListener submitListener = new OnClickListener() {
		public void onClick(View v) {

		}
	};

Within the OnClickListener three things need to happen: I need to get a reference to the score_name_entry of score.xml, remove the views in that layout, and load the score_submitted.xml layout into it.

Below is the resulting code, which should be commented enough to be self-explanatory:

// Get a reference to the score_name_entry object in score.xml
			LinearLayout submitScoreLayout = (LinearLayout)findViewById(R.id.score_name_entry);
			submitScoreLayout.removeAllViews();

			// Create new LayoutInflater - this has to be done this way, as you can't directly inflate an XML without creating an inflater object first
			LayoutInflater inflater = getLayoutInflater();
			submitScoreLayout.addView(inflater.inflate(R.layout.score_submitted, null));

			// The listener for the second button also has to be defined here as opposed to in the onCreate, as the score_submitted.xml isn't loaded yet at activity first run
			Button button = (Button)findViewById(R.id.new_game);
			button.setOnClickListener(newGameListener);

And that’s really it! I’ll attach the full (current) source code of the files to the end of this post. Here’s the screenshots of what the program looks like:

On HighScores activity load:

On Submit button click:

TL;DR (Conclusion)

myLayout.addView(getLayoutInflater().inflate(R.layout.my_xml_layout, null));

Dynamic XML layout loading can pretty much be summed up with that one line of code. Most of the work goes into designing the layouts and making sure the layout you’re replacing has the same size and layout constraints as the one that’s replacing it, otherwise you will end up with the layout shifting to compensate for the different-sized layout.

Now I just have to decide whether to use a custom ListView for the high scores table, a custom Table Layout, or an agglomeration of TextViews…

Questions/comments welcomed.

source_files.zip

10 Comments

  1. can’t find the reference of “score_name_entry” in any of your xml code. What am I missing?

    • Hi ted,

      score_name_entry is a unique id created in the score_submit.xml file. It’s not pointing to another XML file – it’s creating a new id for that specific layout object so that it can be referenced elsewhere.

      In this specific example, we create a reference to score_name_entry located in the score_submit.xml file with this line in the *.java file:
      LinearLayout submitScoreLayout = (LinearLayout)findViewById(R.id.score_name_entry);
      Once we do that, we can manipulate the objects in that LinearLayout, like removing the views from it:
      submitScoreLayout.removeAllViews();
      and inflating another view into it:
      LayoutInflater inflater = getLayoutInflater();
      submitScoreLayout.addView(inflater.inflate(R.layout.score_submitted, null));

      Please let me know if that doesn’t quite clear it up for you!

  2. Hi, i need one help.
    I make one dynamically View that ever time i click on one button he creates another. Each new button have a message diolog with “DELETE” and “CANCEL” option when clicked. but i can`t make the “DELETE” option to delete the correct button. have some whey to pass the current button clicked to the removeview(???);

    Thanks

  3. Great article and just what I needed, thanks!

  4. Thanx for the tutorial

  5. You might do better, from a conceptual standpoint, at least; to put a frame layout with a handy id like submit_content_frame or some such, and put your include inside it. That way when you go to replace the layout, the listener doesn’t need to know what’s in the box, it can just empty the box and fill it with something else. There might be some rule to the contrary but to me it seems clearer to have that content placeholder

  6. Very nice! This is exactly what i’m looking for. Thanks

  7. The source code is unavailable.

Leave a Reply

Required fields are marked *.