Preciso exatamente do mesmo recurso descrito nesta pergunta. Aqui está minha solução e código fonte: https://github.com/laoyang/android-dynamic-views . E você pode ver a demonstração em vídeo em ação aqui: http://www.youtube.com/watch?v=4HeqyG6FDhQ
Layout
Basicamente, você encontrará dois arquivos de layout xml:
- Uma exibição de linha LinearLayout horizontal com a
TextEdit
, a Spinner
e an ImageButton
para exclusão.
- Uma exibição de contêiner LinearLayout vertical com apenas um botão Adicionar novo .
Ao controle
No código Java, você adiciona e remove visualizações de linha no contêiner dinamicamente, usando inflate, addView, removeView, etc. Há algum controle de visibilidade para melhor UX no aplicativo Android padrão. Você precisa adicionar um TextWatcher para a visualização EditText em cada linha: quando o texto estiver vazio, oculte o botão Adicionar novo e o botão Excluir. No meu código, escrevi uma void inflateEditRow(String)
função auxiliar para toda a lógica.
Outros truques
- Definido
android:animateLayoutChanges="true"
em xml para ativar a animação
- Use o plano de fundo transparente personalizado com o seletor pressionado para tornar os botões visualmente iguais aos do aplicativo Android padrão.
Código fonte
O código Java da atividade principal (Isso explica toda a lógica, mas algumas propriedades são definidas nos arquivos de layout xml, consulte a fonte do Github para obter a solução completa):
public class MainActivity extends Activity {
// Parent view for all rows and the add button.
private LinearLayout mContainerView;
// The "Add new" button
private Button mAddButton;
// There always should be only one empty row, other empty rows will
// be removed.
private View mExclusiveEmptyView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.row_container);
mContainerView = (LinearLayout) findViewById(R.id.parentView);
mAddButton = (Button) findViewById(R.id.btnAddNewItem);
// Add some examples
inflateEditRow("Xiaochao");
inflateEditRow("Yang");
}
// onClick handler for the "Add new" button;
public void onAddNewClicked(View v) {
// Inflate a new row and hide the button self.
inflateEditRow(null);
v.setVisibility(View.GONE);
}
// onClick handler for the "X" button of each row
public void onDeleteClicked(View v) {
// remove the row by calling the getParent on button
mContainerView.removeView((View) v.getParent());
}
// Helper for inflating a row
private void inflateEditRow(String name) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View rowView = inflater.inflate(R.layout.row, null);
final ImageButton deleteButton = (ImageButton) rowView
.findViewById(R.id.buttonDelete);
final EditText editText = (EditText) rowView
.findViewById(R.id.editText);
if (name != null && !name.isEmpty()) {
editText.setText(name);
} else {
mExclusiveEmptyView = rowView;
deleteButton.setVisibility(View.INVISIBLE);
}
// A TextWatcher to control the visibility of the "Add new" button and
// handle the exclusive empty view.
editText.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
// Some visibility logic control here:
if (s.toString().isEmpty()) {
mAddButton.setVisibility(View.GONE);
deleteButton.setVisibility(View.INVISIBLE);
if (mExclusiveEmptyView != null
&& mExclusiveEmptyView != rowView) {
mContainerView.removeView(mExclusiveEmptyView);
}
mExclusiveEmptyView = rowView;
} else {
if (mExclusiveEmptyView == rowView) {
mExclusiveEmptyView = null;
}
mAddButton.setVisibility(View.VISIBLE);
deleteButton.setVisibility(View.VISIBLE);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
// Inflate at the end of all rows but before the "Add new" button
mContainerView.addView(rowView, mContainerView.getChildCount() - 1);
}