0

I am interested in populating a screen/activity with a user defined number of same views. Each view will have the exact same layout: couple TextViews and few Buttons. The thing is that each button will control what each TextView will display.

The way I was thinking to implement it was to have one XML and one Java class. Then dependimg on the number the user inputs, populate the screen with that many same views (using a for loop). The question is, can it be done? how? am I thinking about it in the right way?

Please help with any input or thoughts, code examples will be great too.

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
KingsInnerSoul
  • 1,373
  • 4
  • 20
  • 49
  • I have not tried nothing yet. I have been reading on the best and most efficient way to do it. I have seen Fragments, inflators, and listview. but i am looking to make it react to button presses on each seperate added view. i could not find a documented explanation for that. – KingsInnerSoul May 14 '13 at 04:14
  • You should delete your question. You will get flagged and downvoted like hell. – Siddharth May 14 '13 at 04:15
  • thats another thing I was looking for. how to delete questions? – KingsInnerSoul May 14 '13 at 04:31
  • There is a delete button below the tags – Siddharth May 14 '13 at 05:10

2 Answers2

1

of course it can be done.

I think the easiest for your situation, plus you can then easily extend, is to create some helper functions that take care of:

1) creating a empty screen 2) create a button for a screen 3) create a textview for a screen and finally 4) create a screen and populate it

You have to decide the proper Root element for your Views, depending on the child arragement you need. For simplicity let's choose a LinearLayout, but for a RelativeLayout or TableLayout the example is the same, it only changes that when you add the elements, you have to use additional parameters to properly place them.

Note that the function to create an empty custom view returns a ViewGroup ("where all layouts derive from"). This way, you always work with ViewGroups and just define the screen layout type once, inside createCustomView. So you can change the type of screens just there, and the rest of code will work ...

Here is some code for your inspiration:

private ViewGroup createCustomView(Context context) {

    LinearLayout myCoolNewView=new LinearLayout(context); // or RelativeLayout, etc..
    return myCoolNewView;
} 

private Button createButton(Context context, String buttonText) {
    Button newButton=new Button(context);
    newButton.setText(buttonText);
    return newButton;
}

private TextView createText(Context context, String initialText) {
    TextView newText=new TextView(context);
    newText.setText(buttonText);
    return newText;
}

private ViewGroup createScreen(Context context, int numberOfButtons, int numberOfTextfields) {

    ViewGroup newScreen=createCustomView(context);
    TextView[] textViews=new TextView[numberOfTextFields];

    for (int i=0; i<numberOfTextfields; i++) {
          textViews[i]=createText(context, "hi i am text "+i);
           newScreen.addView(textViews[i]); // you ideally provide here layoutparams to properly place your buttons

    }
    for (int j=0; i<numberOfButtons; j++) {
          Button button=createButton(context, "hi i am button "+j);
          button.setOnClickListener(new OnClickListener() {
              public void onClick (View clickedView) {
                    // here you have a button keypress and you know all the textviews
                    textView[i%j].setText("hey you pressed me");
              }
          });
          newScreen.addView(button);
    }
    return newScreen;
}

So now you can:

ViewGroup screen1=createScreen(context, 10, 10);
ViewGroup screen2=createScreen(context, 5, 3);
ViewGroup screen3=createScreen(context, 2, 5);

and add the screens to a parent layout, to a ViewFlipper, to a ViewSwitcher, etc... like this:

 ViewGroup parentLayoutOfAllScreens=findViewById(R.id.root_of_screens);
 parentLayoutOfAllScreens.addView(screen1);
 parentLayoutOfAllScreens.addView(screen2);
 parentLayoutOfAllScreens.addView(screen3);

In the XML you just have to create the root layout, and name it root_of_screens...

good coding !!! I suppose there'll be some errors in the code above, just typed it here, but I hope you get the idea and tweak it to suit your needs!

EDIT : v2.0 : Extending a View Create a new .java named "MyCoolScreen.java" or whatever name, in the same folder where your activity is (for simplicity):

package ........
public class MyCoolScreen extends LinearLayout {

    /** Now every view holds its own buttons, and they are private, it's good for encapsulating */
    private TextView[] mTextViews; // <-- as a convention, members should start with "m"
    private Button[] mButtons;
    private UserPressedButtons mUserPressedButtonsListener; // See below

    /** The following constructors must always be present for a custom view, and must always call super */
    public MyCoolScreen(Context context) {
        // This is the constructor you will use when creating your view programmatically
        super(context);
    }

    public MyCoolScreen(Context context, AttributeSet attrs) {

        // This is the constructor Android calls when you include your custom view in an XML
        // You can do this too!! 
        // The ATTRS will then include your numberofbuttons and numberoftextfields from the XML
        // this is beyond the example, but read about it, it's interesting

        super(context, attrs); // this MUST ALWAYS be here for custom views, or they will not work.
                               // it tells the parent view to continue the construction.
    }

    public MyCoolScreen(Context context, AttributeSet attrs, int defStyle) {
        // Another constructor Android calls from the XML
        super(context, attrs, defStyle); 
    }


    /** We create an "init" method to initialize this view from outside */
    public void init(int numberOfTextViews, int numberOfButtons) {
        createScreen(numberOfTextViews, numberOfButtons);
    }


    /** This is the same */
    private Button createButton(Context context, String buttonText) {
        Button newButton=new Button(context);
        newButton.setText(buttonText);
        return newButton;
    }

    /** This is the same */
    private TextView createText(Context context, String initialText) {
        TextView newText=new TextView(context);
        newText.setText(buttonText);
        return newText;
    }

    /** We tweak this function so it doesnt return a view, but rather fills up this one :) */

    private void createScreen(int numberOfButtons, int numberOfTextfields) {

        ViewGroup newScreen=this; // It's this view the one we gonna fill up!
        mTextViews=new TextView[numberOfTextfields];
        mButtons=new Button[numberOfButtons];
        Context context=getContext(); // Views always know their context after constructed

        for (int i=0; i<numberOfTextfields; i++) {
              mTextViews[i]=createText(context, "hi i am text "+i);
              newScreen.addView(textViews[i]); // you ideally provide here layoutparams to properly place your buttons
        }

        for (int j=0; i<numberOfButtons; j++) {
              Button button=createButton(context, "hi i am button "+j);
              button.setId(j);
              button.setOnClickListener(new OnClickListener() {
                  public void onClick (View clickedView) {
                        // here you have a button keypress and you know all the textviews
                        if (mUserPressedButtonsListener!=null) mUserPressedButtonsListener.OnButtonPressed(j);
                        textView[i%j].setText("hey you pressed me");
                  }
              });
              mButtons[j]=button;
              newScreen.addView(button);
        }
    }

    public interface UserPressedButtons {
        public void OnButtonPressed(int buttonNumber);
    }

    public void setUserPressedButtonsListener (UserPressedButtons listener) {
         mUserPressedButtonsListener=listener;
    }
}

Ok, so now to use this, in your Activity you can do:

    import ....... .MyCoolScreen;
    import ....... .MyCoolScreen.UserPressedButtons;

    .
    .
    .

    MyCoolScreen screen1=new MyCoolScreen(context);
    screen1.init(5,5); // initializes the screen.

    myRootLayout.addView(screen1);

What's cool about this, is now functionality is totally encapsulated in your custom view. And it resides in another .java, so your activity code is very clean, and you can even expand the View functionality without making it ugly.

It's also a common practice to create interfaces and listeners for your views to communicate with the outside world, so for example, we can do:

     screen1.setUserPressedButtonsListener(new MyCoolScreen.UserPressedButtons() {
         @Override
         public void OnButtonPressed (int number) {
              // you know the user pressed button "number", and you can do stuff about it without
              // having to include it inside the MyCoolScreen class. Of course in your example you
              // don't need this at the moment, because the View will modify its textfield, but suppose
              // one of the buttons is "rocket launch" , that is something you will handle at the activity level, ie.

              if (number==ROCKET_LAUNCH) RocketLauncher.setTarget(10,10).launch(); // Your MyCoolScreen doesnt know how to launch rockets, but your activity maybe yes...

         }
     });

You can do all kinds of cool things with your new custom view. For example, you could define:

     @Override
     public void OnDraw(Canvas c) {
          c.drawEllipse ...
          c.drawRectangle ....
     }

And you can paint circles, lines, etc... over your textfields & buttons :) For this to work, you have to put

    setWillNotDraw(false) on the constructor.

There might be errors, just typed the code here, but I hope it helps you!

rupps
  • 9,712
  • 4
  • 55
  • 95
  • I have not tested this code, but it seems right. Just as what I thought about doing. The thing I was missing, and your post raised that the thought for the solution, was about generating unique IDs for each element created. I would add to your code `.setId(i);` for each added element. :-) Its late, and I will test it tomorrow and will reply back. -Cheers – KingsInnerSoul May 14 '13 at 04:50
  • noprob :) But now that u know about setId, let me tell you about setTag(). It's the older cousin, that way you can assign whatever to a button, not only an id. You can do: view.setTag("my button"), or view.setTag(new String[]{"button1","screen2"}) or yet even view.setTag(new MyStuffInfo(a,b,c,d,e))) .... and later do view.getTag() to get it :) – rupps May 14 '13 at 04:54
  • It works like a charm (after heaviliy modifying your code to fit my desired outlook). Now I am having issues with id's and tag's and order of elements within the LinearLayout. Cant figure the right way of calling back the TextViews in the onClick for teh buttons. – KingsInnerSoul May 15 '13 at 05:28
  • for that problem you can keep all the textfields in an array at the screen level, for example, then assign as button ID or Tag the array index of the textfield. In OnClick you get the clicked view as parameter, so the button id will be the textfield index. By the way, the "pro" way would be creating a Screen class that extends LinearLayout, with the logic to fill it inside, and references to its own textfields and buttons. That way each screen would be separated from each other, and the probkem you are facing will be much easier to solve. Tell me if you need a code sample about this... – rupps May 15 '13 at 13:20
  • If I udnerstand your "pro" way - then its the way I wanted to do it initially - but did not know how. Becaues the MainScreen hosts all the views - which are the same - I was thinking to repeat the same view, as many times as the user defined. Thats where I hit the wall and your solution came in handy. A code example would great, if you can please. – KingsInnerSoul May 15 '13 at 17:38
  • well to understand the pro way it's better to understand the first example before, now you can refactor it properly! See the attached code – rupps May 15 '13 at 18:39
0

Add and Remove Views in Android Dynamically?

this will helps to you most...

Community
  • 1
  • 1
Ramesh J
  • 794
  • 2
  • 11
  • 24
  • I have read through it already before. Its not what I am looking for. I am looking to dynamically add several views - which originate from one view - and be able to control events for each view independently. Events such as changing TextViews and ImageViews for each seperate view. – KingsInnerSoul May 14 '13 at 04:39