views:

80

answers:

3

Hi. Im posting from my phone so please excuse stupid typos and formatting issues.

I have an activity which lists saved games that the player can load.

I created a simple layout xml file which defines a ScrollView. On load, I grab all the saved games and programatically add a view for each saved game to a vertically oriented LinearLayout child of the ScrollView.

The view for each game consists of a Horizontally oriented LinearLayout which in turn contains a Button and a vertically oriented LinearLayout. That LinearLayout in turn contains some TextViews and ImageViews (and one more LinearLayout which I'm ommitting here for the sake of clarity).

The hierarchy looks something like this (some details omitted).

 ScrollView
      LinearLayout - vertical 
           Each saved game:
           LinearLayout - horizontal 
                Button - load game
                LinearLayout - vertical 
                     TextView - game name
                     TextView - date string

My problem:

I would like the top of the button and the "game name" texview to be vertically aligned but the TextView (or maybe it's LinearLayout parent) has some rogue padding on top that I can't get rid of. See screenshot for details.

alt

LoadSaved class: Note: mScrollView is badly named. It refers to the ScrollView's child LinearLayout.

   public class LoadSaved extends Activity {
     public LinearLayout mScrollView;
     private MinerDb mDb;

      public void onCreate(Bundle b) { 
             super.onCreate(b);
             setContentView(R.layout.loadsaved);
             mDb = new MinerDb(this);

            mScrollView = (LinearLayout) findViewById(R.id.load_scroll_view);
            Bundle[] savedGames = mDb.getSavedGames();

            for (int i = 0; i < savedGames.length; i++) {
                 Bundle game = savedGames[i];

                 final int gameId = game.getInt("gameId");
                 String name = game.getString("name");
                 String date = game.getString("date");

                 Bundle player = game.getBundle("player");
                 int playerMoney = player.getInt("money");
                 int playerHealth = player.getInt("health");

                 LinearLayout gameContainer = new LinearLayout(getApplicationContext());
                 gameContainer.setPadding(5, 5, 5, 5);
                 gameContainer.setGravity(Gravity.TOP);
                 gameContainer.setOrientation(LinearLayout.HORIZONTAL);
                 gameContainer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

                 Button loadButton = new Button(getApplicationContext());
                 loadButton.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                 loadButton.setText("Load");


                 LinearLayout gameInfo = new LinearLayout(getApplicationContext());
                 gameInfo.setOrientation(LinearLayout.VERTICAL);
                 gameInfo.setPadding(10,0,10,10);
                 gameInfo.setGravity(Gravity.TOP);
                 gameInfo.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

                 TextView nameView = new TextView(getApplicationContext());
                 nameView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
                 nameView.setGravity(Gravity.TOP);
                 nameView.setText(name);

                 TextView dateView = new TextView(getApplicationContext());
                 dateView.setPadding(5,0,0,0);
                 dateView.setGravity(Gravity.TOP);
                 dateView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
                 dateView.setText(date);

                 LinearLayout playerView = new LinearLayout(getApplicationContext());
                 playerView.setOrientation(LinearLayout.HORIZONTAL);
                 playerView.setPadding(5,0,0,0);
                 playerView.setGravity(Gravity.TOP);
                 playerView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

                 TextView playerMoneyView = new TextView(getApplicationContext());
                 playerMoneyView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                 playerMoneyView.setPadding(0,0,10,0);
                 playerMoneyView.setTextColor(Color.GREEN);
                 playerMoneyView.setText("$" + playerMoney);

                TextView playerHealthView = new TextView(getApplicationContext());
                playerHealthView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                playerHealthView.setPadding(0,0,10,0);
                playerHealthView.setTextColor(Color.RED);
                playerHealthView.setText(playerHealth + "%");

                playerView.addView(playerMoneyView);
                playerView.addView(playerHealthView);

                gameInfo.addView(nameView);
                gameInfo.addView(dateView);
                gameInfo.addView(playerView);

                gameContainer.addView(loadButton);
                gameContainer.addView(gameInfo);

                mScrollView.addView(gameContainer);

                loadButton.setOnClickListener(new OnClickListener() { 
                     public void onClick(View v) {
                           Log.e("LoadSaved", "LoadSaved::onCreate: Clicking: " + gameId);
                           Intent loadGameIntent = new Intent(LoadSaved.this, Miner.class);
                           loadGameIntent.putExtra("load_game", gameId);
                           startActivity(loadGameIntent);
                           finish();
                      }
                 });
            }
       }
  }

loadsaved.xml

           <LinearLayout
                android:orientation="vertical"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:id="@+id/load_scroll_view" />

       </ScrollView>
  </LinearLayout>
+3  A: 

If you want any kind of alignment, why don't you use a RelativeLayout? That's basically designed to align one view with another. android:layout_alignTop sounds like something you want.

(And, of course, verify that the padding values are the same in all controls, but I'm sure you did that.)

EboMike
Thanks. Ill look into that. This is my first android project so I'm a bit of a noob with the platform.
Electronic Zebra
Yeah, getting layouts to work the way you want them takes a bit of practice at first, but you'll like it once you get a hang of it. RelativeLayout is typically your best choice for anything that goes beyond a simple list of elements.
EboMike
This looks like my solution. I'm programming this in a bar right now so it might take a while to rewrite my view... the beers are taking their toll. Ill mark you correct once I get to it. I want to release tomorrow so I might just wait until I do my initial update after release to fix this.
Electronic Zebra
+2  A: 

Why don't you try using a ListView for that kind of gui.

You will still need to define a row xml.

Macarse
+1  A: 

+1 to the answers suggesting ListView and RelativeLayout. For this type of situation you probably want a ListView with an item layout using RelativeLayout. (ListView will scale much better if there are many items, and if this is for a list of saved games it seems like this could grow quite a bit.) For this type of UI it's recommended to have the whole row/list item clickable rather than use a small Load button, but that's a design issue and ultimately up to you.

Don't use getApplicationContext for creating your views. Activity is a Context, just pass this in your case.

By default LinearLayouts try to align child views by their text baseline if present. Note that the bottom of your button's Load text aligns perfectly with the CURRENT_GAME text in your screenshot. Try gameContainer.setBaselineAligned(false).

Normally your gameInfo layout would only report the baseline of one of its children if you set a baselineAlignedChildIndex, but it looks like this behavior changed between cupcake and eclair when creating LinearLayouts programmatically. (Link to the commit that changed it in AOSP here.)

adamp
Re: Passing "this" for context. Seems when I do that I end up leaking memory all over the place. Then after starting a few activities it crashes. With getApplicationContext memory consumption stays stable.
Electronic Zebra
All the answers were good (and I'm glad I learned a bit about RelativeLayout) but your answer worked easiest. Thanks.
Electronic Zebra
You do have to be careful about where you pass your Activity as a Context, but there should be no problem using it to construct views unless you're storing references to those views in such a way that they persist beyond your Activity's lifespan. If you're doing this you might have other problems.
adamp