Динамически добавлять несколько представлений и классов к текущему представлению

Я заинтересован в заполнении экрана/действия определенным пользователем количеством одинаковых просмотров. Каждое представление будет иметь одинаковую компоновку: пара TextView и несколько кнопок. Дело в том, что каждая кнопка будет управлять тем, что будет отображать каждый TextView.

То, как я думал реализовать это, заключалось в том, чтобы иметь один класс XML и один класс Java. Затем, в зависимости от числа, которое вводит пользователь, заполните экран таким же количеством представлений (используя цикл for). Вопрос в том, можно ли это сделать? как? правильно ли я думаю об этом?

Пожалуйста, помогите с любым входом или мыслями, примеры кода тоже будут отличными.


person KingsInnerSoul    schedule 14.05.2013    source источник
comment
Я еще ничего не пробовал. Я читал о лучшем и наиболее эффективном способе сделать это. Я видел фрагменты, инфляторы и список. но я хочу, чтобы он реагировал на нажатия кнопок в каждом отдельном добавленном представлении. Я не мог найти документальное объяснение этому.   -  person KingsInnerSoul    schedule 14.05.2013
comment
Вы должны удалить свой вопрос. Вас пометят и заминусуют, как ад.   -  person Siddharth    schedule 14.05.2013
comment
это еще одна вещь, которую я искал. как удалить вопросы?   -  person KingsInnerSoul    schedule 14.05.2013
comment
Под тегами есть кнопка удалить   -  person Siddharth    schedule 14.05.2013


Ответы (2)


конечно можно сделать.

Я думаю, что самым простым для вашей ситуации, плюс вы можете легко расширить, является создание некоторых вспомогательных функций, которые заботятся о:

1) создать пустой экран 2) создать кнопку для экрана 3) создать текстовое представление для экрана и, наконец, 4) создать экран и заполнить его

Вы должны выбрать правильный корневой элемент для ваших представлений, в зависимости от нужного вам расположения дочерних элементов. Для простоты давайте выберем LinearLayout, но для RelativeLayout или TableLayout пример тот же, с той лишь разницей, что когда вы добавляете элементы, вам нужно использовать дополнительные параметры для их правильного размещения.

Обратите внимание, что функция для создания пустого пользовательского представления возвращает ViewGroup ("откуда происходят все макеты"). Таким образом, вы всегда работаете с ViewGroups и просто определяете тип макета экрана один раз внутри createCustomView. Таким образом, вы можете изменить тип экранов прямо там, а остальной код будет работать ...

Вот код для вашего вдохновения:

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;
}

Итак, теперь вы можете:

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

и добавьте экраны в родительский макет, в ViewFlipper, в ViewSwitcher и т. д., например:

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

В XML вам просто нужно создать корневой макет и назвать его root_of_screens...

хорошая кодировка!!! Я предполагаю, что в приведенном выше коде будут некоторые ошибки, просто напечатайте его здесь, но я надеюсь, что вы поняли идею и настроили ее в соответствии со своими потребностями!

РЕДАКТИРОВАТЬ: v2.0: Расширение представления Создайте новый .java с именем «MyCoolScreen.java» или любым другим именем в той же папке, где находится ваша деятельность (для простоты):

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;
    }
}

Итак, теперь, чтобы использовать это, в своей деятельности вы можете сделать:

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

    .
    .
    .

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

    myRootLayout.addView(screen1);

Что в этом хорошего, так это то, что теперь функциональность полностью инкапсулирована в вашем пользовательском представлении. И он находится в другом .java, поэтому ваш код активности очень чистый, и вы даже можете расширить функциональность View, не делая его уродливым.

Также обычной практикой является создание интерфейсов и прослушивателей для ваших представлений для связи с внешним миром, поэтому, например, мы можем сделать:

     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...

         }
     });

Вы можете делать всевозможные интересные вещи с вашим новым пользовательским представлением. Например, вы можете определить:

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

И вы можете рисовать круги, линии и т. д. над вашими текстовыми полями и кнопками :) Чтобы это работало, вы должны поставить

    setWillNotDraw(false) on the constructor.

Там могут быть ошибки, просто набрал код здесь, но я надеюсь, что это поможет вам!

person rupps    schedule 14.05.2013
comment
Я не тестировал этот код, но он кажется правильным. Так же, как я думал сделать. То, что мне не хватало, и ваш пост показал, что идея решения заключалась в создании уникальных идентификаторов для каждого созданного элемента. Я бы добавил в ваш код .setId(i); для каждого добавленного элемента. :-) Уже поздно, завтра проверю и отвечу. -Ваше здоровье - person KingsInnerSoul; 14.05.2013
comment
noprob :) Но теперь, когда вы знаете о setId, позвольте мне рассказать вам о setTag(). Это старший двоюродный брат, таким образом вы можете назначить кнопке что угодно, а не только идентификатор. Вы можете сделать: view.setTag(моя кнопка) или view.setTag(new String[]{button1,screen2}) или даже view.setTag(new MyStuffInfo(a,b,c,d,e))) . ... и позже выполните view.getTag(), чтобы получить его :) - person rupps; 14.05.2013
comment
Это работает как шарм (после того, как вы сильно изменили свой код, чтобы он соответствовал моему желаемому взгляду). Теперь у меня проблемы с идентификаторами, тегами и порядком элементов в LinearLayout. Не могу понять, как правильно вызвать TextViews в onClick для кнопок. - person KingsInnerSoul; 15.05.2013
comment
для этой проблемы вы можете, например, сохранить все текстовые поля в массиве на уровне экрана, а затем назначить идентификатор кнопки или пометить индекс массива текстового поля. В OnClick вы получаете щелкнутое представление в качестве параметра, поэтому идентификатор кнопки будет индексом текстового поля. Кстати, профессиональным способом было бы создание класса Screen, который расширяет LinearLayout, с логикой для его заполнения внутри и ссылками на собственные текстовые поля и кнопки. Таким образом, каждый экран будет отделен друг от друга, и проблема, с которой вы столкнетесь, будет намного проще решить. Скажите, нужен ли вам пример кода об этом... - person rupps; 15.05.2013
comment
Если я понял ваш профессиональный путь - то это так, как я хотел сделать изначально - но не знал как. Поскольку на MainScreen размещены все представления, которые одинаковы, я думал повторить одно и то же представление столько раз, сколько определил пользователь. Вот где я ударился о стену, и ваше решение пригодилось. Пример кода был бы великолепен, если вы можете, пожалуйста. - person KingsInnerSoul; 15.05.2013
comment
хорошо, чтобы понять профессиональный способ, лучше сначала понять первый пример, теперь вы можете правильно его отрефакторить! См. прикрепленный код - person rupps; 15.05.2013

Добавлять и удалять представления в Android динамически?

это поможет вам больше всего ...

person Ramesh J    schedule 14.05.2013
comment
Я уже читал его раньше. Это не то, что я ищу. Я хочу динамически добавлять несколько представлений, которые происходят из одного представления, и иметь возможность независимо управлять событиями для каждого представления. Такие события, как изменение TextViews и ImageViews для каждого отдельного представления. - person KingsInnerSoul; 14.05.2013