Приложение вылетает при удалении
Я написал следующее приложение, в котором отображается сетка кнопок 3 на 4, и пользователь может изменять размеры сетки, щелкая пункты меню. Проблема в том, что с функцией "deleteSomething" происходит нечто странное. Ниже приведен полный код:
package com.example.myapplication;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity {
LinearLayout.LayoutParams params;
LinearLayout linearLayout;
int _row;
int column;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL); //Can also be done in xml by android:orientation="vertical"
params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT);
params.weight = 1.0f;
params.gravity = Gravity.TOP;
//layout.setBackgroundColor(0xFFFFFFFF);
_row=3;
column=4;
update();
}
public void update(){
for (int i = 0; i < _row; i++) {
LinearLayout row = new LinearLayout(this);
row.setLayoutParams(params);
for (int j = 0; j < column; j++) {
Button btnTag = new Button(this);
btnTag.setLayoutParams(params);
btnTag.setText("Button " + (j + 1 + (i * column)));
btnTag.setId(j + 1 + (i * column));
if ((i+j) % 2 == 0) {
btnTag.setBackgroundColor(0xFFFF0000);
} else {
btnTag.setBackgroundColor(0x00000000);
}
row.addView(btnTag);
}
linearLayout.addView(row);
}
setContentView(linearLayout);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, 1, Menu.NONE, "Item name");
menu.add(Menu.NONE, 2, Menu.NONE, "Item name");
return true;
}
public void deleteSomething(){
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(0);
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(1);
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(2);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_ITEM1:
deleteSomething();
//linearLayout.removeAllViews();
//_row=4;
//column=5;
//update();
return true;
case 2:
deleteSomething();
//linearLayout.removeAllViews();
_row=6;
column=3;
update();
default:
return false;
}
}
}
В этой части есть что-то странное:
public void deleteSomething(){
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(0);
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(1);
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(2);
}
Закомментируйте первые две строки, третья кнопка во вторых строках удаляется без проблем; закомментируйте вторую, первые две кнопки во второй тоже будут удалены без проблем. Однако, когда вы включаете все три строки, приложение вылетает сразу после того, как вы щелкнете по первому элементу в выпадающем меню.
Кто-нибудь знает почему?
2 ответа
Прежде всего, я хотел бы подтвердить объяснение Дхарма. Без его ответа я бы не смог придумать это решение. Хотя его / ее объяснение полезно, его / ее предлагаемое решение неверно. Я опубликую правильное решение в следующем: Изменить это
public void deleteSomething(){
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(0);
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(1);
((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(2);
}
чтобы:
public void deleteSomething(){
for (int i = _row-1; i >= 0; i--) {
for (int j = column - 1; j >= 0; j--) {
((LinearLayout) linearLayout.getChildAt(i)).removeViewAt(j);
}
}
}
Основная идея заключается в том, что мы не хотим изменять идентификаторы при удалении, и поэтому мы должны обращаться к их идентификаторам в обратном направлении.
Эта проблема
Ваша проблема в том, что вы ссылаетесь на представления по индексу. Каждый раз, когда вы удаляете представление, количество детей уменьшается. Давайте посмотрим на ваш пример:
LinearLayout начинается с 3 детей:
[A, B, C]
Первый звонок((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(0);
Теперь у вашего LinearLayout 2 детей:
[ДО НАШЕЙ ЭРЫ]
Выполнить следующий вызов((LinearLayout) linearLayout.getChildAt(1)).removeViewAt(1);
Теперь у вашего LinearLayout 1 ребенок:
[B]
Выполнить следующий вызов(LinearLayout) linearLayout.getChildAt(1)).removeViewAt(2);
Это вызывает исключение, потому что индекс 2 находится за пределами диапазона ваших детей (теперь размер 1).
Решение
Получите ссылки на любые представления, которые вы хотите удалить в первую очередь. Затем удалите их все из LayoutGroup, используя removeView(View view)
, Ниже приведен простой пример того, как вы можете сделать это в демонстрационных целях:
LinearLayout layout = (LinearLayout) linearLayout.getChildAt(1);
View a = layout.getChildAt(0);
View b = layout.getChildAt(1);
View c = layout.getChildAt(2);
layout.removeView(a);
layout.removeView(b);
layout.removeView(c);