PDA

Ver la Versión Completa : [ SOLUCIONADO ] Eliminar fila añadida mediante addView - Que no atino!!!


Godlike
10/08/15, 16:40:35
Buenas a todos,

Tengo un LinearLayout donde, con programación, añado "filas" al pulsar un botón. En realidad lo que hago es crear los elementos que quiero (una imagen, EditText...) y "unirlos" en una fila (un LinearLayout).

Luego añado ese LinearLayout al original y así consigo que se vayan añadiendo unas debajo de otras:

public void addConcepto (View view) {
lineNumber++;
//Instance LinearLayout that is about to store each new Concepto line
LinearLayout li = (LinearLayout) findViewById(R.id.listConceptos);

//Creating new LinearLayout where I create new elements.
//This is what I am going actually to add to the first LinearLayout
LinearLayout horizontal = new LinearLayout(this);

//Building up the line by adding elements to the new LinearLayout
ImageButton et1 = new ImageButton(this);
EditText et2 = new EditText(this);
EditText et3 = new EditText(this);
EditText et4 = new EditText(this);

et1.setBackgroundColor(0);
et1.setImageResource(R.drawable.minus_icon2);
et1.setOnClickListener(new View.OnClickListener() { //Adding onClickListener to remove lines
public void onClick(View v) {
removeConcepto(lineNumber);
}
});
//ImageView et4 = new ImageView(this);

et2.setHint("Concepto");
et3.setHint("IVA");
et4.setHint("Precio");

et2.setWidth(150);
et3.setWidth(150);
et4.setWidth(150);
//et4.setWidth(150);

horizontal.addView(et1);
horizontal.addView(et2);
horizontal.addView(et3);
horizontal.addView(et4);

li.addView(horizontal);

//Globals.listaConceptos.add();
//et1.setTag(lineNumber);
}Pues bien, como veréis por los trozos que tengo comentados, estoy tratando de añadir una imagen al comienzo de cada fila, que es un signo de menos, y así cuando pulse quiero que esa línea se elimine.

Tengo esta función:

public void removeConcepto (int line) {
LinearLayout li = (LinearLayout) findViewById(R.id.listConceptos);

//int pos = (Integer) view.getTag(1);
//li.removeViewAt(li.getChildCount());
li.removeViewAt(line);
}Que si pongo una línea fija, por ejemplo la "1", me la elimina. Pero no soy capaz de relacionar el número de fila con la fila real.

Mi intención era la que veis, un contador lineNumber que voy incrementando, y cuando creo la imagen de restar le asigno ese número al llamar a removeConcepto, pero me da fallos como si llamara a un objeto que no existe. Además de esta manera si creo tres filas, y borro la segunda. La que era la tercera seguirá teniendo el número "3" en su botón de borrar, cuando habrá pasado a ser el "2". No puede ser tan complicado!!.

Además de esto, como programador web que soy, me chirría que cada campo EditText no tenga un ID o nombre único, porque cuando el usuario acabe de añadir todas las filas (conceptos o productos en la aplicación) tendré que guardarlos todos en una base de datos... ¿cómo acceder a toda la información si no puedo diferenciar los campos?.

En PHP los campos tendrían en su nombre un ID dinámico, pero en Java aparte de no poderse hacer tal cual, es que no creo que sea tan "chapucero".

Por favor, cualquier ayuda será bienvenida.

Un saludo!

mocelet
10/08/15, 17:19:03
Lo suyo es que uses los patrones de diseño de Android y en concreto el patrón MVC (Modelo - Vista - Controlador) que es muy habitual en programación móvil y también en diversos frameworks web.

Para las listas tienes los ListView que precisamente ayudan a gestionar la presentación de listas de elementos (creas un layout a modo de plantilla de una fila y ya se encarga la implementación de poblar la lista a partir del modelo de datos, reutilizando vistas si se hace scroll para mejorar el rendimiento).

Los datos (el modelo) se almacenan en alguna estructura de datos (en memoria, base de datos, etc.) y se proporcionan a la interfaz de usuario (la vista) a través de los denominados adaptadores, y esos sí tienen constancia de la posición que ocupa un elemento en la lista para que cuando definas el listener del botón se borre lo que tiene que borrarse en el modelo y desaparezca la fila que tiene que desaparecer de la vista.

Esa es la forma de hacerlo en Android, aun así, si por lo que sea necesitas especificar algún identificador a un elemento, tienes los métodos setTag, getTag y findViewWithTag de las vistas que te permiten ponerle el nombre que quieras y buscarlo.

Godlike
10/08/15, 18:35:49
Muchísimas gracias compañero.

Tengo una actividad como mencionas, el problema es que lo hice sabiendo tan poco que no he tratado de repetirlo en estas listas que consideraba mucho más sencilla. La otra que tengo es mostrar en lista todos los elementos de una base de datos, y utilizo adaptadores como comentas.

Voy a darle una vuelta y ojalá no tenga que volver a preguntar :)

Gracias de nuevo!!

Godlike
14/08/15, 17:27:43
Lo suyo es que uses los patrones de diseño de Android y en concreto el patrón MVC (Modelo - Vista - Controlador) que es muy habitual en programación móvil y también en diversos frameworks web.

Para las listas tienes los ListView que precisamente ayudan a gestionar la presentación de listas de elementos (creas un layout a modo de plantilla de una fila y ya se encarga la implementación de poblar la lista a partir del modelo de datos, reutilizando vistas si se hace scroll para mejorar el rendimiento).

Los datos (el modelo) se almacenan en alguna estructura de datos (en memoria, base de datos, etc.) y se proporcionan a la interfaz de usuario (la vista) a través de los denominados adaptadores, y esos sí tienen constancia de la posición que ocupa un elemento en la lista para que cuando definas el listener del botón se borre lo que tiene que borrarse en el modelo y desaparezca la fila que tiene que desaparecer de la vista.

Esa es la forma de hacerlo en Android, aun así, si por lo que sea necesitas especificar algún identificador a un elemento, tienes los métodos setTag, getTag y findViewWithTag de las vistas que te permiten ponerle el nombre que quieras y buscarlo.


Buenas!

Tengo este fragmento de código que utilizo para listar el contenido de una tabla en base de datos:

final Cursor cursor = db.getAllRowsPartes();
final String[] fromFieldNames = new String[]{MySQLiteHelper.KEY_PARTES_ID, MySQLiteHelper.KEY_PARTES_ID_CLIENTE, MySQLiteHelper.KEY_PARTES_HORA_INICIO, MySQLiteHelper.KEY_PARTES_HORA_FIN, MySQLiteHelper.KEY_PARTES_TECNICO, MySQLiteHelper.KEY_PARTES_NOTA_PUBLICA, MySQLiteHelper.KEY_PARTES_NOTA_INTERNA, MySQLiteHelper.KEY_PARTES_PERSONA_CONTACTO, MySQLiteHelper.KEY_PARTES_DNI, MySQLiteHelper.KEY_PARTES_EMAIL, MySQLiteHelper.KEY_PARTES_FECHA};
final int[] toViewIDs = new int[]{R.id.itemId, R.id.itemIdCliente, R.id.itemHinicio, R.id.itemHfin, R.id.itemTecnico, R.id.itemNpublica, R.id.itemNinterna, R.id.itemPcontacto, R.id.itemDni, R.id.itemEmail, R.id.selectedItem};
final SimpleCursorAdapter myCursorAdapter;
myCursorAdapter = new SimpleCursorAdapter(getBaseContext(), R.layout.activity_list_db_each, cursor, fromFieldNames, toViewIDs, 0);
final ListView myList = (ListView) findViewById(R.id.listDBitems);

myList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//ItemClicked item = parent.getItemAtPosition(position);

Cursor itemCursor = (Cursor) myCursorAdapter.getItem(position);
String email = itemCursor.getString(cursor.getColumnIndex(MySQLit eHelper.KEY_PARTES_EMAIL));

Intent intent = new Intent(getApplicationContext(), EditParte.class);
intent.putExtra("email", email);
startActivity(intent);
}
});

myList.setAdapter(myCursorAdapter);


El problema es que no consigo adaptarlo, ya que lo que tengo ahora no son datos que vengan de algún sitio, sino simplemente quiero introducir líneas con 3 campos de texto donde el usuario escribirá, y luego pasar los datos de todos esos campos a otro activity que los almacenará en una base de datos.

¿Podrías echarme una mano? :dios:

mocelet
14/08/15, 19:45:50
En vez de una base de datos SQlite usa un ArrayList de objetos (un array de objetos de tipo Gasto p.ej. que tenga tres variables: concepto, iva y precio). Los datos están en esa estructura de datos (modelo), las filas del listview realmente no son más que una representación visual (vista) de esos datos.

En vez de un SimpleCursorAdapter usa un ArrayAdapter

En vez de modificar la listview directamente o de añadir o eliminar vistas por tu cuenta, modifica el array de datos donde almacenes los valores de cada fila. Cuando modifiques algo (cambiar un valor, borrar o añadir un Gasto) tienes que llamar al método notifyDataSetChanged() del adapter para que la vista, es decir, la lista, se actualice con los nuevos datos. En otras palabras, no añades o quitas filas a la lista, las añades o quitas del array y ya se encarga Android de crear las filas.

Para editar es igual, si quieres que un usuario modifique un campo de una fila concreta (por el ejemplo al añadir un nuevo gasto que por defecto estará todo a cero p.ej.), en el listener de esa fila gracias al parámetro position puedes llamar a adapter.getItem(position) y tendrás el objeto de tipo Gasto correspondiente para modificarlo.

La clave es pensar en que los datos realmente no están en la lista o en los campos, están en un array y la lista se genera a partir de los datos de ese array.

Ventajas: no te preocupas de las vistas, y los datos luego no hay que "leerlos de los campos" porque en cualquier momento el estado está guardado en un array.

Godlike
15/08/15, 14:20:07
En vez de una base de datos SQlite usa un ArrayList de objetos (un array de objetos de tipo Gasto p.ej. que tenga tres variables: concepto, iva y precio). Los datos están en esa estructura de datos (modelo), las filas del listview realmente no son más que una representación visual (vista) de esos datos.

En vez de un SimpleCursorAdapter usa un ArrayAdapter

En vez de modificar la listview directamente o de añadir o eliminar vistas por tu cuenta, modifica el array de datos donde almacenes los valores de cada fila. Cuando modifiques algo (cambiar un valor, borrar o añadir un Gasto) tienes que llamar al método notifyDataSetChanged() del adapter para que la vista, es decir, la lista, se actualice con los nuevos datos. En otras palabras, no añades o quitas filas a la lista, las añades o quitas del array y ya se encarga Android de crear las filas.

Para editar es igual, si quieres que un usuario modifique un campo de una fila concreta (por el ejemplo al añadir un nuevo gasto que por defecto estará todo a cero p.ej.), en el listener de esa fila gracias al parámetro position puedes llamar a adapter.getItem(position) y tendrás el objeto de tipo Gasto correspondiente para modificarlo.

La clave es pensar en que los datos realmente no están en la lista o en los campos, están en un array y la lista se genera a partir de los datos de ese array.

Ventajas: no te preocupas de las vistas, y los datos luego no hay que "leerlos de los campos" porque en cualquier momento el estado está guardado en un array.


Buenas mocelet, antes de nada muchas gracias, sé el rollo que es ayudar a alguien con poca idea en estos temas, y más a través de un foro.

La realidad es que estoy echando tiempo en esto y solo con tu ayuda avanzo, me gustaría mostrarte lo que estoy intentando, algo se me escapa.

Para tratar de aplicar todo lo que me comentas tengo por un lado una clase Producto:

public class Producto {
private int id;
private String ref;
private String nombre;
private int iva;
private int precio_publico;
private String n_serie;

public Producto (String ref, String nombre, int iva, int precio_publico, String n_serie){
super();
this.ref = ref;
this.nombre = nombre;
this.iva = iva;
this.precio_publico = precio_publico;
this.n_serie = n_serie;
}

public Producto (){
super();
this.ref = null;
this.nombre = null;
this.iva = 0;
this.precio_publico = 0;
this.n_serie = null;
}

//Getters
public int getId() { return this.id; }
public String getRef (){
return this.ref;
}
public String getNombre(){
return this.nombre;
}
public int getIva(){
return this.iva;
}
public int getPrecio_publico(){
return this.precio_publico;
}
public String getN_serie(){
return this.n_serie;
}

//Setters
public void setId (int id) { this.id = id; }
public void setRef (String ref){
this.ref = ref;
}
public void setNombre(String nombre){
this.nombre = nombre;
}
public void setIva(int iva){
this.iva = iva;
}
public void setPrecio_publico(int precio_publico){
this.precio_publico = precio_publico;
}
public void setN_serie(String n_serie){
this.n_serie = n_serie;
}
}


Por otro lado, he creado un ArrayList en la Activity donde quiero hacer todo lo que te consulto:

ArrayList <Producto> Conceptos = new ArrayList<Producto>();

En el XML principal tengo el ListView donde quiero que vayan apareciendo las líneas al pulsar el botón:


<ListView
android:id="@+id/listConceptos"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</ListView>


He creado un XML a modo de plantilla para cada línea:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:descendantFocusability="blocksDescendants"
android:textIsSelectable="false" >

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.conde.adan.partesedreams.ParteTecnico"
android:orientation="vertical" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_marginLeft="10dp"
android:orientation="horizontal"
android:paddingTop="10dp" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="horizontal" >

<ImageButton
android:id="@+id/delId"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/minus_icon2"
/>

<EditText
android:id="@+id/conceptoId"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:paddingTop="20dp"
android:paddingLeft="5dp"
android:hint="Concepto"
android:textSize="20dp"/>

<EditText
android:id="@+id/ivaId"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:paddingTop="20dp"
android:paddingLeft="5dp"
android:hint="IVA"
android:textSize="20dp"/>

<EditText
android:id="@+id/precioId"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:paddingTop="20dp"
android:paddingLeft="5dp"
android:hint="Precio"
android:textSize="20dp"/>

</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>


Y en la función de addConcepto trato de crear la vinculación:

final ArrayAdapter <Producto> myArrayAdapter = new ArrayAdapter <Producto> (this, android.R.layout.simple_list_item_1, Conceptos);
ListView listView = (ListView) findViewById(R.id.listConceptos);
listView.setAdapter(myArrayAdapter);


Pero tengo una sensación, y es que no quiero mostrar información de una base de datos o en un ArrayList como en este caso, sino añadir la plantilla XML cada vez que pulso el botón y, como comentas, poder controlar los datos y que Android se encargue de la visualización, pero todo esto que he hecho me lleva a pensar que estoy tratando de hacer lo contrario, mostrar información de algún sitio en la vista.

Siento abusar pero podrías darme otro empujón? básicamente quiero poder añadir ese XML cada vez que pulse un botón y capturar los datos escritos en cada campo al final del documento, imagino que esto podrá hacerse... lo que me dices me suena genial pero parece en tiempo real, no se me ocurre como hacer la verdad, no se si es almacenar los datos que escribe el usuario en el ArrayList y avisar al Adapter en cada momento.

Mil gracias de antemano!

mocelet
15/08/15, 15:34:29
Pero tengo una sensación, y es que no quiero mostrar información de una base de datos o en un ArrayList como en este caso, sino añadir la plantilla XML cada vez que pulso el botón y, como comentas, poder controlar los datos y que Android se encargue de la visualización, pero todo esto que he hecho me lleva a pensar que estoy tratando de hacer lo contrario, mostrar información de algún sitio en la vista.

Lo has entendido perfectamente, y esa es la base del patrón MVC (modelo-vista-controlador), tener separados los datos de la presentación y de la lógica. Y cuando cambias algo, lo cambias en el modelo de datos, no en la vista. La vista simplemente refleja los datos.

Ahora bien, puedes pensar que no tienes ningún dato porque quieres que el usuario los introduzca. Eso es correcto, pero necesitas un sitio donde guardar los datos introducidos, el ArrayList de Producto, que al principio estará vacío.

En vez de pensar en "añadir la plantilla XML cuando pulse el botón" para que el usuario escriba en una nueva fila, piensa en añadir un nuevo Producto al array. Inicialmente ese producto tendrá los atributos a cero o al valor por defecto que pongas, y gracias al adaptador y al listview se creará automáticamente una nueva fila a partir de la plantilla XML (que, por cierto, el ScrollView te sobra, una única fila no tiene scroll, y la listview ya lo lleva).

Esa nueva fila tendrá campos EditText con valores por defecto que el usuario tendrá que cambiar. En vez de esperar a que el usuario rellene todo, puedes ir guardando en el Producto asociado los valores según se van añadiendo gracias al listener de los EditText (addTextChangedListener) que te indican que se ha editado un campo, o a un botón de guardar que pongas en cada fila o como quieras.

Así siempre tienes el array actualizado con los datos del usuario, y si introduce algo incorrecto puedes indicarlo en la propia fila antes de guardarlo, cambiarle el color o cualquier indicador para el usuario.

Igual es matar moscas a cañonazos para introducir uno o dos productos... pero si un día añade muchos o si tienes casos como el de ahora elimino una fila, ahora añado otra, etc. no te dará ningún problema.

Godlike
15/08/15, 19:58:39
Lo has entendido perfectamente, y esa es la base del patrón MVC (modelo-vista-controlador), tener separados los datos de la presentación y de la lógica. Y cuando cambias algo, lo cambias en el modelo de datos, no en la vista. La vista simplemente refleja los datos.

Ahora bien, puedes pensar que no tienes ningún dato porque quieres que el usuario los introduzca. Eso es correcto, pero necesitas un sitio donde guardar los datos introducidos, el ArrayList de Producto, que al principio estará vacío.

En vez de pensar en "añadir la plantilla XML cuando pulse el botón" para que el usuario escriba en una nueva fila, piensa en añadir un nuevo Producto al array. Inicialmente ese producto tendrá los atributos a cero o al valor por defecto que pongas, y gracias al adaptador y al listview se creará automáticamente una nueva fila a partir de la plantilla XML (que, por cierto, el ScrollView te sobra, una única fila no tiene scroll, y la listview ya lo lleva).

Esa nueva fila tendrá campos EditText con valores por defecto que el usuario tendrá que cambiar. En vez de esperar a que el usuario rellene todo, puedes ir guardando en el Producto asociado los valores según se van añadiendo gracias al listener de los EditText (addTextChangedListener) que te indican que se ha editado un campo, o a un botón de guardar que pongas en cada fila o como quieras.

Así siempre tienes el array actualizado con los datos del usuario, y si introduce algo incorrecto puedes indicarlo en la propia fila antes de guardarlo, cambiarle el color o cualquier indicador para el usuario.

Igual es matar moscas a cañonazos para introducir uno o dos productos... pero si un día añade muchos o si tienes casos como el de ahora elimino una fila, ahora añado otra, etc. no te dará ningún problema.


Gracias de nuevo, me parece genial hacerlo así, la intención de hacer esta aplicación es ayudar a una persona en su empresa, pero sobretodo aprender Android, y hacerlo bien ahora aunque sea para muy pocos productos me vendrá mejor que bien para cuando tenga que hacer algo más complejo, que seguro llegará.

Probablemente con lo que me has dicho debería ya ser capaz de hacerlo por mi mismo, pero salvo eliminar el ScrollView y entender bien lo que me dices, no soy capaz de aplicarlo en la práctica (esto es lo que más me está costando en Android).

Esto lo declaro al inicio del Activity:

ArrayList <Producto> Conceptos = new ArrayList<Producto>();Y para mí debería también ir ahí:

ArrayAdapter <Producto> myArrayAdapter = new ArrayAdapter <Producto> (this, android.R.layout.simple_list_item_1, Conceptos);Pero así la aplicación crashea, de forma que lo he dejado dentro de la función addConcepto (al que llamo al pulsar una imagen).

La duda principal que tengo, una vez me encuentro en addConcepto con esto:

ArrayAdapter <Producto> myArrayAdapter = new ArrayAdapter <Producto> (this, android.R.layout.simple_list_item_1, Conceptos);
ListView listView = (ListView) findViewById(R.id.listConceptos);
listView.setAdapter(myArrayAdapter);Es que falta algo, no hay acción! y de hecho al pulsar el botón no pasa nada... es cierto que los productos no existen y de hecho están vacíos, voy a probar a crear uno cada vez que pulso añadir, pero aún así no lo tendré bindeado a esa línea, ¿no?.

¿Faltaría hacer una clase de ArrayAdapter para decirle todos los campos que tengo en Producto y cómo tratarlos? ¿O con lo que tengo ya basta con crear una instancia de Producto?.

La teoría creo que debería ser crear en efecto esa instancia de Producto, permitir al usuario añadir datos a la línea que aparecerá (no sé cómo Android sabrá que ese producto es del ArrayList) y con la función que me dices (no la conocía y me viene genial) ir almacenando los datos.

Luego, en caso de pulsar la imagen de borrar línea, elimino el Producto de esa posición del ArrayList... pero como te digo esto es lo que creo que hay que hacer, me falta algo en el código que no cuadra todo :(

EDIT:

He añadido estas líneas al principio, creando un Producto que añado al Array:

Producto myProducto = new Producto();
myProducto.setId(3);
myProducto.setIva(21);
myProducto.setN_serie("409KNG");
myProducto.setRef("315F");
myProducto.setNombre("PRODUCTO 1");
myProducto.setPrecio_publico(57);
Conceptos.add(myProducto);

ArrayAdapter <Producto> myArrayAdapter = new ArrayAdapter <Producto> (this, android.R.layout.simple_list_item_1, Conceptos);
ListView listView = (ListView) findViewById(R.id.listConceptos);
listView.setAdapter(myArrayAdapter);Ahora cuando le doy al botón de añadir me añade una fila (al menos va por ahí la cosa!) pero es el nombre de mi app jaja... "com.miapellido.minombre.laempresa.Producto@52a7f6e 4"

Incluso si soluciono esto, pulsando más veces sobre el botón de añadir producto no hace nada, es como que estoy mostrando todos los datos contenidos en el Array pero necesitaría que me introdujera más, para poder escribir en los campos :)

mocelet
15/08/15, 20:32:00
Vas bien encaminado, en efecto falta un poco de "pegamento" para que todo funcione bien.

El adaptador tiene que saber crear las vistas de la listview a partir de la plantilla (el layout)

Échale un vistazo a este tutorial:
http://www.sgoliver.net/blog/interfaz-de-usuario-en-android-controles-de-seleccion-ii/

Aunque en el fondo es parecido a lo que tenías para sqlite.

Godlike
16/08/15, 02:01:24
Vas bien encaminado, en efecto falta un poco de "pegamento" para que todo funcione bien.

El adaptador tiene que saber crear las vistas de la listview a partir de la plantilla (el layout)

Échale un vistazo a este tutorial:
http://www.sgoliver.net/blog/interfaz-de-usuario-en-android-controles-de-seleccion-ii/

Aunque en el fondo es parecido a lo que tenías para sqlite.


Hola de nuevo,

he estado siguiendo la guía del link que me has pasado y me ha aclarado muchísimo, ahora tiene sentido, lo que faltaba, el "pegamento". Sin embargo estoy atascado en algo algo técnico...

Por un lado, aunque en el ejemplo utilizan Titulo[] foo = new titulo ...

Yo tengo ArrayList <Producto> foo = new ...

Entiendo que no tiene más importancia, lo que hago para que funcione así es que en la clase del Adapter customizado hago:

concepto.setText(datos.get(position).getNombre()); En lugar de datos[position].getNombre();

Esto creo que está bien.


Pero obtenía un error al pulsar el botón ya testeando el programa, me decía algo de ID y TextView... buscando por Internet (http://stackoverflow.com/questions/9280965/arrayadapter-requires-the-resource-id-to-be-a-textview-xml-problems)
encuentro que ArrayAdapter necesita un TextView a "pelo", o bien modificar su constructor... pero eso es justamente lo que estaba haciendo!:

public ConceptoAdapter (Context context, ArrayList<Producto> datos){
super(context, R.layout.activity_parte_tecnico_each, R.id.TVid, datos);
}Me he inventado un TextView en el XML, aunque no lo necesito, y he indicado su ID en la linea que ves, y así al pulsar el botón por fin se inserta... pero las letras que te comentaba que salían del nombre de mi aplicación ahora salen en ese TextView, y a continuación todos los EditText que si que necesito.

El problema es que no necesito ningún TextView, y por otro lado aunque pulse varias veces el botón no se insertan más líneas, a pesar de crear un Producto y añadirlo al ArrayList en cada pulsación. Pensaba que sería cosa de usar notifyDataSetChanged() pero no parece que sea eso:

public void addConcepto (View view) {
Producto myProducto = new Producto();
conceptos.add(myProducto);

ConceptoAdapter myAdapter = new ConceptoAdapter (this, conceptos);
ListView listView = (ListView) findViewById(R.id.listConceptos);
listView.setAdapter(myAdapter);

myAdapter.notifyDataSetChanged();
A ver si pudieras echarme otro cable... si necesitas cualquier captura o código solo pídemelo :)

Esta es de lo último que me falta para terminar la app v0.1 jeje...

Saludos!

mocelet
16/08/15, 09:04:22
No me había fijado en el tema del TextView, siempre puedes hacer un apaño... que es crear un TextView invisible solo para que se quede contento. Está hecho para crear listas rápidamente, el texto raro que te pone es el toString() de Producto, que como no tiene ninguno es el identificador del objeto Java. Pero vamos, con este TextView de mentira lo resuelves rápido, ponlo en cualquier sitio de la fila que no ocupa espacio ni nada (o ponle el atributo de visibilidad gone al que ya tienes):


<TextView
android:id="@+id/fakeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>


En el addConcepto parece que además de añadir el nuevo producto y llamar al notifyDataSetChanged (es lo único que hay que hacer) también vuelves a crear el adaptador e inicializar la lista. El adaptador tienes que crearlo solo una vez y asociarlo a la lista solo una vez, por ejemplo en el onCreate. Si no difícilmente podrán mantener el estado de la lista.

Así que esas tres líneas de crear adapter, buscar listview y asociar adapter ponlas en onCreate, y en el addConcepto solo añade el producto al array y llama al notifyDataSetChanged. Con eso debería funcionar, o al menos estar más cerca de que funcione xD

Godlike
16/08/15, 20:38:16
Gracias!

Ya aparecen bien! me quedan dos cosas, me siento taaaan torpe con Android...

Mira, tengo esto declarado al inicio del Activity:

ArrayList <Producto> conceptos = new ArrayList<Producto>();
ConceptoAdapter myAdapter;
ListView listView;

En el OnCreate (entre otras muchas cosas):

myAdapter = new ConceptoAdapter (this, conceptos);
listView = (ListView) findViewById(R.id.listConceptos);
listView.setAdapter(myAdapter);

Y por último la función de addConcepto:

public void addConcepto (View view) {
Producto myProducto = new Producto();
conceptos.add(myProducto);

myAdapter.notifyDataSetChanged();
}

El problema hasta aquí: Siguen sin añadirse más de una fila, por más que pulse el botón. Tampoco tengo claro cómo coger luego todos los datos, pero imagino que es cuestión de usar la función que me comentaste y almacenar los valores conforme se modifican en la posición que toque, o bien hacerlo al final del todo con un botón recorriendo el ArrayList.

Pero el otro problema está relacionado con ese, tengo otra función:

public void removeConcepto (View view) {
}

Y no sé cómo acceder a la position correcta de myAdapter. Este es mi adaptador customizado completo:

public class ConceptoAdapter extends ArrayAdapter {
public ConceptoAdapter (Context context, ArrayList<Producto> datos){
super(context, R.layout.activity_parte_tecnico_each, R.id.fakeText, datos);
}

public View getView(int position, View convertView, ViewGroup parent, ArrayList <Producto> datos){
LayoutInflater inflater = LayoutInflater.from(getContext());
View item = inflater.inflate(R.layout.activity_parte_tecnico_e ach, null);

EditText concepto = (EditText) item.findViewById(R.id.conceptoId);
concepto.setText(datos.get(position).getNombre());

EditText iva = (EditText) item.findViewById(R.id.ivaId);
iva.setText(datos.get(position).getIva());

EditText precio = (EditText) item.findViewById(R.id.precioId);
precio.setText(datos.get(position).getPrecio_publi co());

return(item);
}

}

He tratado de meterle algo igual al EditText pero de la imagen de delete, que por cierto es:

<ImageButton
android:id="@+id/delId"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/minus_icon2"
/>

Pero no sé cómo sacar la posición, de hecho en el Adapter es una variable pasada por referencia, y yo no sé ni dónde se la paso :oh: intuyo que de la vista, de forma automática, pero no se si debería hacer un getPosition o qué, no sé cómo sacarla!

Ojalá termine esto antes de que te desesperes conmigo :gracias:

Godlike
16/08/15, 21:07:22
Añado rápidamente que si que se añaden más ahora! aunque con scroll y no muy juntas unas de otras, modificaré el XML :)

Me queda saber básicamente la position para diferenciarlas y guardar la info, que creo que será poner el listener y sabiendo la position almacenar el valor del campo en el ArrayList (es la misma position o puedo saber que estoy en ESTE campo?). Además así podré eliminar filas :)

mocelet
16/08/15, 21:18:52
¿Seguro que se muestra alguna fila? El método getView de ArrayAdapter que tienes que sobreescribir tiene tres parámetros, no cuatro, así que borra el último:

override
public View getView(int position, View convertView, ViewGroup parent) {

Poner el override es interesante porque así Android Studio te avisa de si de verdad estás sobreescribiendo el método o no. Puedes comprobarlo poniendo el override antes de cambiar los parámetros.

Sobre borrar, una opción es prescindir del removeConcepto y definir el listener dentro del adaptador al crear la vista de esa fila, es decir, dentro del getView. Algo así:


View removeButton = item.findViewById(R.id.button);
final int pos = position;
removeButton.setOnClickListener(new View.OnClickListener() {
override
public void onClick(View v) {
conceptos.remove(pos);
notifyDataSetChanged();
}
});

Otra opción es usar el setTag y el getTag que te comentaba en el primer post, de modo que en la vista guardas la posición para poder recuperarla en el removeConcepto. Entonces en vez del onClickListener lo que haces es un removeButton.setTag(position) y en el método removeConcepto haces lo mismo del onClick pero con pos = view.getTag();

Elige la que más rabia te dé :)

P.D: Ignoro cómo poner la arroba en el foro para que salga bien en el código y no lo confunda con una mención a un usuario
P.D.2: He borrado un "myAdapter." que sobraba, ya estás en el adaptador

Godlike
17/08/15, 19:42:53
¿Seguro que se muestra alguna fila? El método getView de ArrayAdapter que tienes que sobreescribir tiene tres parámetros, no cuatro, así que borra el último:

override
public View getView(int position, View convertView, ViewGroup parent) {Poner el @override (http://www.htcmania.com/member.php?u=42589) es interesante porque así Android Studio te avisa de si de verdad estás sobreescribiendo el método o no. Puedes comprobarlo poniendo el @override (http://www.htcmania.com/member.php?u=42589) antes de cambiar los parámetros.

Sobre borrar, una opción es prescindir del removeConcepto y definir el listener dentro del adaptador al crear la vista de esa fila, es decir, dentro del getView. Algo así:


View removeButton = item.findViewById(R.id.button);
final int pos = position;
removeButton.setOnClickListener(new View.OnClickListener() {
override
public void onClick(View v) {
conceptos.remove(pos);
notifyDataSetChanged();
}
});Otra opción es usar el setTag y el getTag que te comentaba en el primer post, de modo que en la vista guardas la posición para poder recuperarla en el removeConcepto. Entonces en vez del onClickListener lo que haces es un removeButton.setTag(position) y en el método removeConcepto haces lo mismo del onClick pero con pos = view.getTag();

Elige la que más rabia te dé :)

P.D: Ignoro cómo poner la arroba en el foro para que salga bien en el código y no lo confunda con una mención a un usuario
P.D.2: He borrado un "myAdapter." que sobraba, ya estás en el adaptador


Hola de nuevo!!

Antes de nada, mil gracias. No sé si lo creerás pero esto es como un cursillo intenso para mi, he comprendido perfectamente el patrón MVC e incluso he estado viendo videos de cómo funciona en otras plataformas, como la web, donde nunca lo he utilizado.

Mira, he colocado lo que me dices (tiene todo el sentido hacerlo desde dentro de la propia clase, donde la vista tiene la posición!! desconozco de dónde la saca Android pero supongo que ya lo aprenderé más adelante, de momento con que funcione...):

override[/MENTION]
public View getView(final int position, View convertView, ViewGroup parent, ArrayList <Producto> datos){
LayoutInflater inflater = LayoutInflater.from(getContext());
View item = inflater.inflate(R.layout.activity_parte_tecnico_e ach, null);

View removeButton = item.findViewById(R.id.delId);
final int pos = position;
removeButton.setOnClickListener(new View.OnClickListener() {
override
public void onClick(View v) {
datos.remove(pos);
notifyDataSetChanged();
}
});

EditText concepto = (EditText) item.findViewById(R.id.conceptoId);
concepto.setText(datos.get(position).getNombre());

EditText iva = (EditText) item.findViewById(R.id.ivaId);
iva.setText(datos.get(position).getIva());

EditText precio = (EditText) item.findViewById(R.id.precioId);
precio.setText(datos.get(position).getPrecio_publi co());

return(item);
}

(Te copio, que con código PHP parece que queda mejor visualmente)

Como curiosidad, esta línea

removeButton.setOnClickListener(new View.OnClickListener() {

No me lo reconocía así, de forma que probé simplemente con "new OnClickListener() ..." y él solo añadió lo de View.OnClickListener luego, son cosas que aún no entiendo :oh:

Ahora, sobre lo que comentas... efectivamente me tomé la libertad de añadir ese parámetro, y efectivamente también, si comento la línea de

datos.remove(pos);

El override de arriba del getView indica que no estoy sobreescribiendo el método, como bien me decías. El problema es que la guía que seguí del link que me pasaste tienen esta clase dentro del propio Activity, y yo lo tengo aparte, así que la variable "datos" no está reconocida. Desconozco cómo hacer para que esta clase sepa a qué me refiero, así como cuando me dices que ponga:

conceptos.remove(pos);

he puesto "datos.remove(pos);" ya que "conceptos" no lo tengo en esta clase tampoco. Si no la comento el Override no "se queja", pero en la línea "datos.remove(pos);" dice "Variable 'datos' is accessed from within inner class, needs to be declared final.

Aquí falta algo y creo que será la clave de todo, porque solucionaría el Override del getView, y tendría la variable "datos" reconocida, que actualmente mi programa, si elimino "ArraList <Producto> datos" de los parámetros de getView no reconocería esa variable :cry:

Gracias de nuevo ;-)

kriogeN
17/08/15, 21:12:38
El problema está en que getView debe ser:
override
public View getView(final int position, View convertView, ViewGroup parent)

Si añades el parámetro "datos"es una función distinta, y por eso se queja al añadirle el override, porque te está diciendo "¿Qué quieres que sobreescriba si esto no existe en mi padre?

La forma correcta es pasar los datos en el constructor, o si los datos van a ser cambiantes, tener una función llamada "setDatos" a la que asignas los datos y haces el notifyDataSetChanged().

Es decir, algo así:



ArrayList <Producto> datos = null;

public void setDatos(ArrayList <Producto> datos) {
this.datos = datos;
this.notifyDataSetChanged();
}


Añadiendo eso a la clase y dejando getView como te he indicado antes debería funcionarte bien.

Ahora ya lo único que tienes que hacer en la clase cliente es llamar a setDatos inmediatamente después del constructor del ArrayAdapter.

mocelet
17/08/15, 21:42:00
Poco que añadir a lo dicho por kriogeN.

Dado que usa un ArrayAdapter, la lista no va a cambiar porque el adaptador está ligado ya al array que se le pasa. Así que pasaría el array datos por el constructor y lo almacenaría como variable final.

Si no, puede que se le olvide llamar al setDatos o, peor, que lo llame más veces para cambiar el array y la lista esté ligada a un array pero él toque otro array distinto.

EDIT: Es decir:

public class ConceptoAdapter extends ArrayAdapter {
private final ArrayList<Producto> datos;

public ConceptoAdapter (Context context, ArrayList<Producto> datos){
super(context, R.layout.activity_parte_tecnico_each, R.id.fakeText, datos);
this.datos = datos;
}

kriogeN
17/08/15, 21:56:18
Poco que añadir a lo dicho por kriogeN.

Dado que usa un ArrayAdapter, la lista no va a cambiar porque el adaptador está ligado ya al array que se le pasa. Así que pasaría el array datos por el constructor y lo almacenaría como variable final.

Si no, puede que se le olvide llamar al setDatos o, peor, que lo llame más veces para cambiar el array y la lista esté ligada a un array pero él toque otro array distinto.

EDIT: Es decir:

public class ConceptoAdapter extends ArrayAdapter {
private final ArrayList<Producto> datos;

public ConceptoAdapter (Context context, ArrayList<Producto> datos){
super(context, R.layout.activity_parte_tecnico_each, R.id.fakeText, datos);
this.datos = datos;
}

Nunca he usado ArrayAdapter, siempre usaba BaseAdapter, y desde hace casi 1 año uso RecyclerView.Adapter. Así que no sabía que no se podían cambiar, pensaba que era como el BaseAdapter en ese sentido. Y si, lo mejor es hacerlo en el constructor. De hecho aunque tengas un setDatos y se trate de un BaseAdapter no está de más pasar siempre la "primera colección" en el constructor.

Godlike
17/08/15, 22:06:23
Vaya lujo teneros dándome consejos... gracias a ambos, voy a tratar de sacar un rato hoy o mañana para probarlo y os cuento :)

GRACIAS!

mocelet
17/08/15, 22:20:30
Nunca he usado ArrayAdapter, siempre usaba BaseAdapter, y desde hace casi 1 año uso RecyclerView.Adapter. Así que no sabía que no se podían cambiar, pensaba que era como el BaseAdapter en ese sentido. Y si, lo mejor es hacerlo en el constructor. De hecho aunque tengas un setDatos y se trate de un BaseAdapter no está de más pasar siempre la "primera colección" en el constructor.

Lo cierto es que yo tampoco xD Le recomendé el ArrayAdapter por su sencillez (solo tenía que implementar el getView, ¡o eso parecía en teoría!).

Después vino la "chapucilla" porque si no hay TextViews no le gusta. Luego la segunda chapucilla de tener que guardar la referencia de la lista porque el ArrayAdapter se la guarda para él pero luego no te expone métodos como poder borrar por posición. Sin embargo sí te permite añadir o consultar una posición.

Al final, salvo para aprender, compensa implementarse el adaptador desde cero a partir de la clase base, al menos así se está seguro de qué está haciendo exactamente.

Vaya lujo teneros dándome consejos... gracias a ambos, voy a tratar de sacar un rato hoy o mañana para probarlo y os cuento :)

GRACIAS!

Suerte ;)

Godlike
17/08/15, 23:12:00
Buenas!

voy a ir a la cama porque parecía que todo cuadraba y me salen errores de cierre de aplicación al probarlo!

Lo dejo por aquí por si puediérais y os apetece echar un vistazo, mañana seguiré probando de todas formas :)

08-17 16:19:56.785 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ Late-enabling CheckJNI
08-17 16:19:57.081 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed 70K, 5% free 2753K/2880K, paused 17ms, total 17ms
08-17 16:19:57.093 4015-4015/com.apellido.nombre.paquete I/dalvikvm-heap﹕ Grow heap (frag case) to 3.742MB for 1048588-byte allocation
08-17 16:19:57.109 4015-4021/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed <1K, 4% free 3777K/3908K, paused 16ms, total 16ms
08-17 16:19:57.361 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed 6K, 4% free 3788K/3908K, paused 23ms, total 23ms
08-17 16:19:57.385 4015-4015/com.apellido.nombre.paquete I/dalvikvm-heap﹕ Grow heap (frag case) to 4.751MB for 1048588-byte allocation
08-17 16:19:57.389 4015-4024/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed <1K, 3% free 4811K/4936K, paused 4ms, total 4ms
08-17 16:19:57.585 4015-4015/com.apellido.nombre.paquete D/libEGL﹕ loaded /system/lib/egl/libEGL_genymotion.so
08-17 16:19:57.585 4015-4015/com.apellido.nombre.paquete D/﹕ HostConnection::get() New Host Connection established 0xb92d81f8, tid 4015
08-17 16:19:57.601 4015-4015/com.apellido.nombre.paquete D/libEGL﹕ loaded /system/lib/egl/libGLESv1_CM_genymotion.so
08-17 16:19:57.605 4015-4015/com.apellido.nombre.paquete D/libEGL﹕ loaded /system/lib/egl/libGLESv2_genymotion.so
08-17 16:19:57.677 4015-4015/com.apellido.nombre.paquete W/EGL_genymotion﹕ eglSurfaceAttrib not implemented
08-17 16:19:57.677 4015-4015/com.apellido.nombre.paquete E/OpenGLRenderer﹕ Getting MAX_TEXTURE_SIZE from GradienCache
08-17 16:19:57.681 4015-4015/com.apellido.nombre.paquete E/OpenGLRenderer﹕ MAX_TEXTURE_SIZE: 16384
08-17 16:19:57.697 4015-4015/com.apellido.nombre.paquete E/OpenGLRenderer﹕ Getting MAX_TEXTURE_SIZE from Caches::initConstraints()
08-17 16:19:57.697 4015-4015/com.apellido.nombre.paquete E/OpenGLRenderer﹕ MAX_TEXTURE_SIZE: 16384
08-17 16:19:57.697 4015-4015/com.apellido.nombre.paquete D/OpenGLRenderer﹕ Enabling debug mode 0
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete I/dalvikvm﹕ Could not find method android.view.ViewGroup.onNestedScrollAccepted, referenced from method android.support.v7.internal.widget.ActionBarOverla yLayout.onNestedScrollAccepted
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete W/dalvikvm﹕ VFY: unable to resolve virtual method 11382: Landroid/view/ViewGroup;.onNestedScrollAccepted (Landroid/view/View;Landroid/view/View;I)V
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ VFY: replacing opcode 0x6f at 0x0000
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete I/dalvikvm﹕ Could not find method android.view.ViewGroup.onStopNestedScroll, referenced from method android.support.v7.internal.widget.ActionBarOverla yLayout.onStopNestedScroll
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete W/dalvikvm﹕ VFY: unable to resolve virtual method 11388: Landroid/view/ViewGroup;.onStopNestedScroll (Landroid/view/View;)V
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ VFY: replacing opcode 0x6f at 0x0000
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete I/dalvikvm﹕ Could not find method android.support.v7.internal.widget.ActionBarOverla yLayout.stopNestedScroll, referenced from method android.support.v7.internal.widget.ActionBarOverla yLayout.setHideOnContentScrollEnabled
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete W/dalvikvm﹕ VFY: unable to resolve virtual method 9071: Landroid/support/v7/internal/widget/ActionBarOverlayLayout;.stopNestedScroll ()V
08-17 16:19:59.849 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ VFY: replacing opcode 0x6e at 0x000e
08-17 16:19:59.853 4015-4015/com.apellido.nombre.paquete I/dalvikvm﹕ Could not find method android.content.res.TypedArray.getChangingConfigur ations, referenced from method android.support.v7.internal.widget.TintTypedArray. getChangingConfigurations
08-17 16:19:59.853 4015-4015/com.apellido.nombre.paquete W/dalvikvm﹕ VFY: unable to resolve virtual method 382: Landroid/content/res/TypedArray;.getChangingConfigurations ()I
08-17 16:19:59.853 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ VFY: replacing opcode 0x6e at 0x0002
08-17 16:19:59.853 4015-4015/com.apellido.nombre.paquete I/dalvikvm﹕ Could not find method android.content.res.TypedArray.getType, referenced from method android.support.v7.internal.widget.TintTypedArray. getType
08-17 16:19:59.853 4015-4015/com.apellido.nombre.paquete W/dalvikvm﹕ VFY: unable to resolve virtual method 404: Landroid/content/res/TypedArray;.getType (I)I
08-17 16:19:59.853 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ VFY: replacing opcode 0x6e at 0x0002
08-17 16:20:00.001 4015-4015/com.apellido.nombre.paquete W/EGL_genymotion﹕ eglSurfaceAttrib not implemented
08-17 16:20:02.792 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed 161K, 4% free 5330K/5548K, paused 5ms, total 5ms
08-17 16:20:02.796 4015-4015/com.apellido.nombre.paquete I/dalvikvm-heap﹕ Grow heap (frag case) to 6.258MB for 1048588-byte allocation
08-17 16:20:02.936 4015-4024/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed 1K, 4% free 6353K/6576K, paused 141ms, total 142ms
08-17 16:20:03.012 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed 52K, 3% free 6471K/6632K, paused 4ms, total 4ms
08-17 16:20:03.076 4015-4015/com.apellido.nombre.paquete I/Choreographer﹕ Skipped 30 frames! The application may be doing too much work on its main thread.
08-17 16:20:03.272 4015-4015/com.apellido.nombre.paquete W/EGL_genymotion﹕ eglSurfaceAttrib not implemented
08-17 16:20:05.020 4015-4015/com.apellido.nombre.paquete W/ResourceType﹕ No package identifier when getting value for resource number 0x00000000
08-17 16:20:05.020 4015-4015/com.apellido.nombre.paquete D/AndroidRuntime﹕ Shutting down VM
08-17 16:20:05.020 4015-4015/com.apellido.nombre.paquete W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0xa4d25b20)
08-17 16:20:05.024 4015-4015/com.apellido.nombre.paquete E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.apellido.nombre.paquete, PID: 4015
android.content.res.Resources$NotFoundException: String resource ID #0x0
at android.content.res.Resources.getText(Resources.ja va:244)
at android.widget.TextView.setText(TextView.java:3888 )
at com.apellido.nombre.paquete.ConceptoAdapter.getVie w(ConceptoAdapter.java:43)
at android.widget.AbsListView.obtainView(AbsListView. java:2255)
at android.widget.ListView.onMeasure(ListView.java:11 47)
at android.view.View.measure(View.java:16497)
at android.view.ViewGroup.measureChildWithMargins(Vie wGroup.java:5125)
at android.widget.LinearLayout.measureChildBeforeLayo ut(LinearLayout.java:1404)
at android.widget.LinearLayout.measureVertical(Linear Layout.java:695)
at android.widget.LinearLayout.onMeasure(LinearLayout .java:588)
at android.view.View.measure(View.java:16497)
at android.widget.ScrollView.measureChildWithMargins( ScrollView.java:1226)
at android.widget.FrameLayout.onMeasure(FrameLayout.j ava:310)
at android.widget.ScrollView.onMeasure(ScrollView.jav a:326)
at android.view.View.measure(View.java:16497)
at android.view.ViewGroup.measureChildWithMargins(Vie wGroup.java:5125)
at android.widget.FrameLayout.onMeasure(FrameLayout.j ava:310)
at android.view.View.measure(View.java:16497)
at android.view.ViewGroup.measureChildWithMargins(Vie wGroup.java:5125)
at android.support.v7.internal.widget.ActionBarOverla yLayout.onMeasure(ActionBarOverlayLayout.java:453)
at android.view.View.measure(View.java:16497)
at android.view.ViewGroup.measureChildWithMargins(Vie wGroup.java:5125)
at android.widget.FrameLayout.onMeasure(FrameLayout.j ava:310)
at android.view.View.measure(View.java:16497)
at android.view.ViewGroup.measureChildWithMargins(Vie wGroup.java:5125)
at android.widget.LinearLayout.measureChildBeforeLayo ut(LinearLayout.java:1404)
at android.widget.LinearLayout.measureVertical(Linear Layout.java:695)
at android.widget.LinearLayout.onMeasure(LinearLayout .java:588)
at android.view.View.measure(View.java:16497)
at android.view.ViewGroup.measureChildWithMargins(Vie wGroup.java:5125)
at android.widget.FrameLayout.onMeasure(FrameLayout.j ava:310)
at com.android.internal.policy.impl.PhoneWindow$Decor View.onMeasure(PhoneWindow.java:2291)
at android.view.View.measure(View.java:16497)
at android.view.ViewRootImpl.performMeasure(ViewRootI mpl.java:1912)
at android.view.ViewRootImpl.measureHierarchy(ViewRoo tImpl.java:1109)
at android.view.ViewRootImpl.performTraversals(ViewRo otImpl.java:1291)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl .java:996)
at android.view.ViewRootImpl$TraversalRunnable.run(Vi ewRootImpl.java:5600)
at android.view.Choreographer$CallbackRecord.run(Chor eographer.java:761)
at android.view.Choreographer.doCallbacks(Choreograph er.java:574)
at android.view.Choreographer.doFrame(Choreographer.j ava:544)
at android.view.Choreographer$FrameDisplayEventReceiv er.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733 )
at android.os.Handler.dispatchMessage(Handler.java:95 )
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.jav a:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCa ller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit .java:601)
at dalvik.system.NativeStart.main(Native Method)
08-17 16:20:05.032 4015-4015/com.apellido.nombre.paquete D/dalvikvm﹕ GC_FOR_ALLOC freed 131K, 3% free 7591K/7780K, paused 3ms, total 3ms

Muchas gracias!

kriogeN
18/08/15, 00:24:19
Hay algún elemento que IVA o PrecioPublico es 0, y alguno de los 2 elementos (supongo que PrecioPublico) será un Integer. En ese caso Android al hacer el setText trata de asignar el recurso con el ID del Integer, y no el texto del Integer, la solución es convertir el Integer a String con String.valueOf.

Así:

EditText iva = (EditText) item.findViewById(R.id.ivaId);
iva.setText(String.valueOf(datos.get(position).get Iva()));

EditText precio = (EditText) item.findViewById(R.id.precioId);
precio.setText(String.valueOf(datos.get(position). getPrecio_publico()));

Godlike
18/08/15, 08:49:10
Hay algún elemento que IVA o PrecioPublico es 0, y alguno de los 2 elementos (supongo que PrecioPublico) será un Integer. En ese caso Android al hacer el setText trata de asignar el recurso con el ID del Integer, y no el texto del Integer, la solución es convertir el Integer a String con String.valueOf.

Así:

EditText iva = (EditText) item.findViewById(R.id.ivaId);
iva.setText(String.valueOf(datos.get(position).get Iva()));

EditText precio = (EditText) item.findViewById(R.id.precioId);
precio.setText(String.valueOf(datos.get(position). getPrecio_publico()));


Joder!! con perdón, pero si eso estoy harto de hacerlo... vaya saturación llevo, entre PHP por la mañana y resolución de incidencias y luego por la tarde Android T_T.

En cuanto llegue a casa lo pruebo, y continúo con las pruebas para eliminar fila y almacenar todo...

Lo próximo será almacenar bien las variables cuando cambia la app de estado, tengo onResume y estas cosas pero no estoy seguro de hacerlo bien, almaceno cada variable y luego las recupero a cada campo de texto, creo que es así.

Bueno ya os contaré, mil gracias de nuevo :dios:

Godlike
07/09/15, 21:26:53
Buenas!

Refloto el hilo después de vacaciones varias...

Solamente una cosa, consigo que se creen bien y se añaden en el ListView del ScrollView, pero al hacer Scroll es lentísimo! no digo de lag, sino que hay que darle varias veces hacia arriba con el dedo para pasar de una línea a otra.

He utilizado 200dp de height en lugar de wrap_content para que aparezcan tres visibles, pero es horrible el scrolling! he leído cosas sobre hacerlo con AsyncTask y demás pero no parece que sea lo que necesito, son 3 líneas contadas con unos campos de texto...

Voy a seguir mañana mirándolo pero dejo esto por aquí por si a alguien le suena y me ahorra sudores :)

Gracias de antemano!

Godlike
09/09/15, 08:56:13
¿Podría tal vez solucionarse con una barra lateral?

kriogeN
09/09/15, 12:54:30
Tienes algún evento en los elementos que hace que se invalide el evento de Touch del ScrollView, y por eso no se mueve. Cuando consigues que se mueva es porque estarás pulsando en alguna zona que no invalida.

Godlike
09/09/15, 19:48:51
Tienes algún evento en los elementos que hace que se invalide el evento de Touch del ScrollView, y por eso no se mueve. Cuando consigues que se mueva es porque estarás pulsando en alguna zona que no invalida.

Buenas kriogeN!

Qué alegría leerte, pues resulta que tengo un ScrollView para todo el layout porque si no no puedo ver la parte de abajo de la pantalla, que no cabe en la tablet, y es este el que "jode" el scrolling del otro. He probado a quitar el ScrollView "principal" y ya funciona perfecto el de abajo, el de los conceptos, pero claro esto no es lo que quiero...

hay manera sencilla de hacer funcionar los dos Scrolls?


Edit: Voy a probar esto...

https://trivedihardik.wordpress.com/2011/09/19/scrollview-inside-scrollview-scrolling-problem/

kriogeN
09/09/15, 22:19:57
A mi cuando se me ha presentado el caso lo he resuelto así, con el requestDisallowInterceptTouchEvent, pero luego al final he terminado buscando algún rediseño para la aplicación.

No es bueno tener 2 Scrolls en la misma dirección uno dentro de otro, porque te pasa lo que te está ocurriendo. La mejor solución será que hagas el ListView más pequeño para que así no necesites el ScrollView de fuera.

Otra opción consiste en hacer que el ListView sea el último elemento de la Activity, y hacer que scrollee sólo cuando ha terminado el scroll principal mientras estés scrolleando hacia abajo, y en cuanto empiezas a hacerlo hacia arriba vuelves a scrollear al principal hacia arriba, en cuanto has llegado a la parte superior de la Activity sigues scrolleando al ListView hacia arriba. Y así sucesivamente.

Suena complejo pero no lo es, es jugar comprobando las posiciones de los distintos scrolls y hacer el disallow según sea necesario. Con todo y con eso sigue siendo mala idea tener 2 scrolls en la misma dirección uno dentro de otro.

Godlike
11/09/15, 14:05:56
A mi cuando se me ha presentado el caso lo he resuelto así, con el requestDisallowInterceptTouchEvent, pero luego al final he terminado buscando algún rediseño para la aplicación.

No es bueno tener 2 Scrolls en la misma dirección uno dentro de otro, porque te pasa lo que te está ocurriendo. La mejor solución será que hagas el ListView más pequeño para que así no necesites el ScrollView de fuera.

Otra opción consiste en hacer que el ListView sea el último elemento de la Activity, y hacer que scrollee sólo cuando ha terminado el scroll principal mientras estés scrolleando hacia abajo, y en cuanto empiezas a hacerlo hacia arriba vuelves a scrollear al principal hacia arriba, en cuanto has llegado a la parte superior de la Activity sigues scrolleando al ListView hacia arriba. Y así sucesivamente.

Suena complejo pero no lo es, es jugar comprobando las posiciones de los distintos scrolls y hacer el disallow según sea necesario. Con todo y con eso sigue siendo mala idea tener 2 scrolls en la misma dirección uno dentro de otro.

Ostras estaba cometiendo un fallo extremadamente estúpido... trataba de meter un ScrollView dentro de otro ScrollView cuando lo que tengo es un ListView, así que incluso la solución temporal de deshabilitar uno u otro al capturar el touchEvent no funcionaba...

De esta manera lo solucioné y no recuerdo exactamente qué cambié, pero fue muy sencillo al darme cuenta de que no podía tratarlo como algo que no era, luego miro a ver qué cambié al final y lo pongo, es increíble no acordarme pero le di tantas vueltas...

La solución de llegar al límite de un scroll para iniciar el otro deslizando desde el mismo punto me gusta mucho, pensaba que era muy complejo y ni he intentado pero me parece una gran solución para la experiencia de usuario, a ver si le pego un ojo.

Mil gracias!!

Godlike
11/09/15, 18:16:53
Vale pues hice lo de capturar el toque de pantalla pero al ListView, y no al ScrollView que de hecho he podido eliminar, quedando un ListView dentro de un ScrollView:

//ListView Scrolling fix!
ListView lv = (ListView)findViewById(R.id.listConceptos); //The ListView inside ScrollView

lv.setOnTouchListener(new ListView.OnTouchListener() {
override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(t rue);
break;

case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(f alse);
break;
}

// Handle ListView touch events.
v.onTouchEvent(event);
return true;
}
});


Gracias!!

Godlike
20/09/15, 21:24:51
Buenas de nuevo!!

Creo que el eliminar ya está funcionando, aunque digo "creo" porque cuando elimino una fila se borra la información de todas las demás también, pienso que al hacer el notify recarga todo y como la información está como temporalmente en el view pero no está guardado en el array no existe y al recargar no puede mostrarse.

¿Podríais decirme cómo implementar el guardado "on the fly" para tener la información almacenada en el array al cambiar de campo o mientras se escribe? He pensado almacenar todo en el array, pasarlo entero como parámetro al siguiente Activity, que es donde se muestra un resumen de la info escrita y al guardar ir recorriendo el array, meter la info de cada linea en un objeto Concepto y almacenarlo en la base de datos.

¿Es buen planteamiento?

Gracias de antemano!!

Godlike
22/09/15, 10:20:52
En vez de una base de datos SQlite usa un ArrayList de objetos (un array de objetos de tipo Gasto p.ej. que tenga tres variables: concepto, iva y precio). Los datos están en esa estructura de datos (modelo), las filas del listview realmente no son más que una representación visual (vista) de esos datos.

En vez de un SimpleCursorAdapter usa un ArrayAdapter

En vez de modificar la listview directamente o de añadir o eliminar vistas por tu cuenta, modifica el array de datos donde almacenes los valores de cada fila. Cuando modifiques algo (cambiar un valor, borrar o añadir un Gasto) tienes que llamar al método notifyDataSetChanged() del adapter para que la vista, es decir, la lista, se actualice con los nuevos datos. En otras palabras, no añades o quitas filas a la lista, las añades o quitas del array y ya se encarga Android de crear las filas.

Para editar es igual, si quieres que un usuario modifique un campo de una fila concreta (por el ejemplo al añadir un nuevo gasto que por defecto estará todo a cero p.ej.), en el listener de esa fila gracias al parámetro position puedes llamar a adapter.getItem(position) y tendrás el objeto de tipo Gasto correspondiente para modificarlo.

La clave es pensar en que los datos realmente no están en la lista o en los campos, están en un array y la lista se genera a partir de los datos de ese array.

Ventajas: no te preocupas de las vistas, y los datos luego no hay que "leerlos de los campos" porque en cualquier momento el estado está guardado en un array.

Estoy un poco tratando de aplicar esto y no sé cómo hacerlo :cry: lo que trato de hacer es utilizar algún evento para guardar los datos de esa fila en un objeto en el array (o sin objeto?) y que se mantenga hasta que bien se actualice por añadir fila, eliminar una, etc. o bien se pase al siguiente activity y lo mande a guardar en la base de datos.

Cualquier ayuda es agradecida,
gracias!

mocelet
22/09/15, 17:04:12
Se cayó htcmania al enviar la respuesta antes, sniff.

Decía que para guardar tienes que escuchar los eventos de cambio de los elementos de la fila y actualizar el array, si no es normal que al hacer notifydatachanged se borre todo.

Con los edittext por ejemplo es con el método addTextChangedListener. El listener lo creas al crear/inflar/reciclar la vista, y ahí ya sabes la posición del array donde tendrás que guardar el nuevo valor.

Godlike
24/09/15, 17:47:21
Se cayó htcmania al enviar la respuesta antes, sniff.

Decía que para guardar tienes que escuchar los eventos de cambio de los elementos de la fila y actualizar el array, si no es normal que al hacer notifydatachanged se borre todo.

Con los edittext por ejemplo es con el método addTextChangedListener. El listener lo creas al crear/inflar/reciclar la vista, y ahí ya sabes la posición del array donde tendrás que guardar el nuevo valor.

Joe vaya casualidad, qué pu****, en cualquier caso te agradezco que lo reescribieras :)

Lo que no me queda claro es lo que marco en negrita, por qué habría de saberlo? tengo un método para añadir filas pero realmente cómo conozco la línea que ocupa un EditText para saber con qué posición del array coincide?

Hoy he estado revisando y añadiendo algunas cosas que faltaban y pienso que teniendo esto bien tendré una versión inicial utilizable del app :)

Mil gracias de antemano!

mocelet
24/09/15, 18:22:52
Para crear una fila en el adapter tendrás por algún sitio un public View getView (int position, View convertView, ViewGroup parent)

Ese position es la posición del array, los edit text que haya en esa fila sabes que tienes que guardarlos en esa posición.

Godlike
25/09/15, 08:30:05
Para crear una fila en el adapter tendrás por algún sitio un public View getView (int position, View convertView, ViewGroup parent)

Ese position es la posición del array, los edit text que haya en esa fila sabes que tienes que guardarlos en esa posición.

Ostras pensaba que con vista te referías al Activity, que es donde cargo la función para mostrarla, estaba confundido, muchas gracias voy a probarlo! :dios:

Godlike
25/09/15, 09:05:29
Buenas de nuevo!

Me ha quedado bastante sencillo, solo necesito guardar información cuando acaba de escribirse, así que:

final EditText precio = (EditText) item.findViewById(R.id.precioId);
precio.setText(String.valueOf(datos.get(position). getPrecio_publico()));
precio.addTextChangedListener(new TextWatcher() {
override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

override
public void afterTextChanged(Editable s) {
datos.get(position).setPrecio_publico(Integer.pars eInt(precio.getText().toString()));
}
});

Creo que está correcto, muchas gracias!.


Ahora me gustaría comentar lo que quiero hacer a ver si tiene sentido... en ese mismo Adapter tengo private final ArrayList <Producto> datos; que es el Array con datos, bien pues desde el Activity que añado los conceptos, me gustaría obtener ese Array para pasarlo como variable al Activity que saca un resumen de los datos y ya finalmente almacena todo en SQLite...

Pero aquí me viene una duda de concepto de Java que no comprendo. Para mí una clase es una estructura con datos y métodos, que una vez instanciados en un objeto puedes rellenar y utilizar, así todo muy resumido.

Pues no comprendo cómo es posible que tengo una clase llamada Globals, donde tengo esto:

public class Globals {
public static Parte parte = new Parte();

public static String signaturePath;

public static String usuarioLogueado;

public static ArrayList<Integer> listaConceptos = new ArrayList<>();

public static User currentUser = new User();
}

Y desde cualquier Activity puedo hacer:

//Adding data to Parte instance of Globals class with static fields, to send them to the next Activity
parte.setId_cliente(cliente);
parte.setTecnico(tecnico);
parte.setHora_inicio(hora_ini);
parte.setHora_fin(hora_fin);
parte.setNota_interna(notaInterna);
parte.setNota_publica(notaPublica);
parte.setPersona_contacto(nombre);
parte.setDni(dni);
parte.setEmail(email);

Globals.parte = parte;

Por poner un ejemplo, es decir que utilizo esa clase, sin instanciar como objeto (de hecho si lo usara como objeto no sería el tipo de global que quiero), y así por ejemplo me ahorro mandar datos en el Intent entre Activities, y tengo la información siempre disponible, de hecho es donde guardo la información del usuario logueado, etc.

Esto lo vi por alguna web y por eso lo utilizo, pero francamente no entiendo cómo puede ser.

Ahora me pregunto si puedo hacer lo mismo en el Adapter y hacer el Array "public", o bien crearle un método getArray, no sé cuál sería la forma correcta de obtener todos estos datos para enviarlos al siguiente Activity.

¿Es una aberración lo que hago con el Globals? ¿esa info puede irse sin previo aviso y perder por ejemplo la información del logueo del usuario?. Quizá tengo la mente demasiado en las sesiones de PHP...

Gracias de nuevo por tantísima ayuda

kriogeN
25/09/15, 10:40:46
Depende para que quieras hacerlo, a mi por ejemplo no me gusta usar propiedades estáticas en Android salvo para casos muy contados. Entre otras cosas porque sobreviven entre ejecuciones de la aplicación.

Es como preguntaban en otro hilo si algo era correcto, al final es correcto si tiene sentido para el problema que quieres solucionar. Lo único que hay que tener claro es el CONCEPTO de lo que significa y si lo estás aplicando bien.

Godlike
25/09/15, 11:32:08
Depende para que quieras hacerlo, a mi por ejemplo no me gusta usar propiedades estáticas en Android salvo para casos muy contados. Entre otras cosas porque sobreviven entre ejecuciones de la aplicación.

Es como preguntaban en otro hilo si algo era correcto, al final es correcto si tiene sentido para el problema que quieres solucionar. Lo único que hay que tener claro es el CONCEPTO de lo que significa y si lo estás aplicando bien.

Gracias KriogeN, entiendo lo que dices y evidentemente necesito conocer mejor muchas cosas para aplicarlas en el entorno adecuado.

En este caso por ejemplo me interesa que sean valores que no se borren aunque el usuario suspenda la aplicación porque se apague la pantalla y vuelva en un ratito, deberia seguir con su usuario almacenado, pero tampoco me interesa que pueda reiniciar la tablet y esas variables estén todavía ahí, por ejemplo.

¿Qué manera utilizarías si quieres, por ejemplo, mantener entre Activities unos datos de login de usuario, o un objeto que quieres poder modificar desde cualquier sitio?

Gracias! ;)

mocelet
25/09/15, 12:40:58
Los ciclos de vida de las variables, objetos y actividades dan para escribir mucho...

A los reinicios solo sobrevive lo que esté almacenado en almacenamiento persistente (memoria interna), es decir, las SharedPreferences, las bases de datos SQLite o en general cualquier fichero que guardes por tu cuenta en la memoria interna y/o en la SD.

Los atributos de un objeto viven mientras viva el objeto. Los atributos estáticos no pertenecen a un objeto sino a la propia clase. Pueden haberse destruido todos los objetos (instancias) de una clase, que si había un valor estático, cuando crees otra instancia, ahí estará el valor salvo que la máquina virtual haya destruido todo para liberar recursos.

En Android además están los Bundles para guardar el estado de una actividad, o los Intent para pasar información entre actividades, aunque de forma limitada (y tampoco sobreviven a reinicios)

Usar valores estáticos es, como decía kriogeN, una opción que está ahí, si se usa bien no hay por qué no usarla. De hecho a Diana Hackborn (una de las ingenieras de Android) le encantan para todo lo que sea compartir datos comunes entre actividades. Yo lo uso para compartir un socket entre actividades, si bien es precisamente uno de los casos delicados (tengo bastante cuidado de llevar la cuenta de qué actividades lo están usando para cerrarlo cuando no hay ninguna).

En cualquier caso, nadie te garantiza que cuando el usuario vuelva a la aplicación sigan estando los datos. Para situaciones de se apaga la pantalla y vuelve en un rato, lo normal es que sí. Si el usuario sale de la app, se pone a hacer otras cosas, le llaman, juega a algo, y vuelve... ya depende.

kriogeN
25/09/15, 14:17:57
Para asegurarte que si el usuario apaga la pantalla cuando vuelva los datos sigan ahí la única manera es usar el Bundle de savedInstanceState.

En cuanto a las propiedades de clase, yo sólo las uso si necesito que 2 Activities compartan datos que no entran en un Intent.

Por ejemplo, tengo una aplicación que una de las Activities es una cámara en tiempo real donde puedes aplicar filtros, la imagen que capturas (ya con el filtro) tiene que pasar a otra Activity donde puedes añadirle más cosas y luego enviarla a un servidor.

Una imagen no entra en un Intent, si lo haces habrá móviles que funcionará bien, pero otros que la Activity dará excepción al abrirse. Por norma general no puedes pasar más de 1 MB en total en un Intent.

Así que lo que hago es almacenar los bytes de la imagen en una propiedad de clase, y la recupero en la otra Activity.

Godlike
27/09/15, 13:52:17
Los ciclos de vida de las variables, objetos y actividades dan para escribir mucho...

A los reinicios solo sobrevive lo que esté almacenado en almacenamiento persistente (memoria interna), es decir, las SharedPreferences, las bases de datos SQLite o en general cualquier fichero que guardes por tu cuenta en la memoria interna y/o en la SD.

Los atributos de un objeto viven mientras viva el objeto. Los atributos estáticos no pertenecen a un objeto sino a la propia clase. Pueden haberse destruido todos los objetos (instancias) de una clase, que si había un valor estático, cuando crees otra instancia, ahí estará el valor salvo que la máquina virtual haya destruido todo para liberar recursos.

En Android además están los Bundles para guardar el estado de una actividad, o los Intent para pasar información entre actividades, aunque de forma limitada (y tampoco sobreviven a reinicios)

Usar valores estáticos es, como decía kriogeN, una opción que está ahí, si se usa bien no hay por qué no usarla. De hecho a Diana Hackborn (una de las ingenieras de Android) le encantan para todo lo que sea compartir datos comunes entre actividades. Yo lo uso para compartir un socket entre actividades, si bien es precisamente uno de los casos delicados (tengo bastante cuidado de llevar la cuenta de qué actividades lo están usando para cerrarlo cuando no hay ninguna).

En cualquier caso, nadie te garantiza que cuando el usuario vuelva a la aplicación sigan estando los datos. Para situaciones de se apaga la pantalla y vuelve en un rato, lo normal es que sí. Si el usuario sale de la app, se pone a hacer otras cosas, le llaman, juega a algo, y vuelve... ya depende.

Para asegurarte que si el usuario apaga la pantalla cuando vuelva los datos sigan ahí la única manera es usar el Bundle de savedInstanceState.

En cuanto a las propiedades de clase, yo sólo las uso si necesito que 2 Activities compartan datos que no entran en un Intent.

Por ejemplo, tengo una aplicación que una de las Activities es una cámara en tiempo real donde puedes aplicar filtros, la imagen que capturas (ya con el filtro) tiene que pasar a otra Activity donde puedes añadirle más cosas y luego enviarla a un servidor.

Una imagen no entra en un Intent, si lo haces habrá móviles que funcionará bien, pero otros que la Activity dará excepción al abrirse. Por norma general no puedes pasar más de 1 MB en total en un Intent.

Así que lo que hago es almacenar los bytes de la imagen en una propiedad de clase, y la recupero en la otra Activity.


Muchísimas gracias a ambos, es impresionante lo que sabéis y la de tiempo que ahorráis, porque de otra manera tocaría probarlo y al dar error ir cambiando... tengo un curso pagado en Udemy que quiero hacer pero no he tenido tiempo ya que esta aplicación era urgente y ya que dije que la haría estoy comprometido...

Además es genial poder saber para qué sirven por ejemplo los Bundles, en el sentido de poder diferenciarlos de otros tipos de salvado. Creo que por el momento me sirve con lo que estoy usando, ya que cada vez que se haga un nuevo Parte reescribiré todas estas variables y sería difícil mezclar datos, y por otro lado, quitando los casos de minimizar y maximizar (suspender y resumen) no necesito almacenar datos ya que al ser un parte de trabajo se irá rellenando sin muchos parones.

Me ha gustado lo de que Diana Hackborn lo utilice, siempre da más tranquilidad jajaja.

Muchísimas gracias, voy a tratar de recuperar y almacenar correctamente la lista de Conceptos, el siguiente "reto" es saber linkarlo con el id de parte correcto y con el propio id del producto.

Un saludo!!:gracias:

Godlike
27/09/15, 20:09:52
Buenas!!

Parece que va saliendo, encontré un problema y es que puse un autoComplete en un campo de la lista y al seleccionar el dato se debían rellenar los campos de esa fila, pero se rellenaban siempre los de la primera fila. Mirando mirando he encontrado un fallo muy tonto y es que tenía duplicada la variable "position".

En fin, ahora todo tiene mejor aspecto, pero hay algo que me está volviendo loco.

Yo tengo un ConceptoAdapter donde hay entre otras cosas, el getView donde defino el remove de una fila:

View removeButton = item.findViewById(R.id.delId);

removeButton.setOnClickListener(new View.OnClickListener() {
override
public void onClick(View v) {
datos.remove(pos);
notifyDataSetChanged();
}
});Como me indicásteis además ahora lo aprovecho para el guardar automático tras escribir en los campos. Sin embargo no es desde aquí que añado filas... con tantos frentes acabo de darme cuenta, ahora que todo funciona, de que no sé cuál es el ArrayList que contiene realmente los datos!!.

La duda es porque en el Adapter se define:

private final ArrayList <Producto> datos;Pero desde la Activity donde hago todo tengo definidos:

ArrayList <Producto> conceptos = new ArrayList<Producto>();
ConceptoAdapter myAdapter;Y la función
public void addConcepto (View view) {
Producto myProducto = new Producto();
conceptos.add(myProducto);

myAdapter.notifyDataSetChanged();
}He probado a crear esta función dentro del Adapter, parece más lógico ya que es allí donde elimino la información del Array:


View addButton = item.findViewById(R.id.addConcepto);
addButton.setOnClickListener(new View.OnClickListener() {
override
public void onClick(View v) {
Producto producto = new Producto();
datos.add(producto);
notifyDataSetChanged();
}
});Y sin embargo así no ocurre nada!!

Aquí es donde, en el Activity, se aplica el Adapter:

myAdapter = new ConceptoAdapter (this, conceptos);
listView = (ListView) findViewById(R.id.listConceptos);
listView.setAdapter(myAdapter);

¿Se carga el constructor al hacer el notifyDataSetChanged()? Porque de otra forma no entiendo cómo el Array del Adapter, donde se hace en el constructor la asignación, vuelve a tener esta información actualizada :S

Me surjen dos preguntas... primero, por qué no se añaden los datos y muestran? (he probado incluso dejando la función addConcepto del Activity y que simplemente haga el myAdapter.notifyDataSetChanged(), sin éxito). Y segundo, ¿dónde tengo realmente los datos? me he puesto hasta de mala leche por no haber caído en que estoy creando un ArrayList <Producto> concepto. Es decir un array de productos que se llama concepto, y aunque los Concepto son similares al Producto, en realidad quiero que Producto sea como una referencia de productos disponibles, y conceptos son los productos que asigno en un Parte de trabajo, la diferencia es que un concepto puede ser un Producto con precio modificado, por ejemplo, además de estar repetido.

Básicamente lo que quiero es guardar ahora en la base de datos este ArrayList de productos pero como Concepto (son menos campos y cuenta con referencias de ID a Parte y Producto), pero para empezar creo que habré de crear un campo aunque sea oculto con el ID del producto...

Espero que se entienda algo, de todas formas voy a seguir mirando a ver si me aclaro, gracias de antemano!!

mocelet
27/09/15, 21:00:17
Al final me voy a liar yo también xD

¿Ese "datos" es sólo una referencia, no? Supongo que lo creaste para tener la referencia al array en el adapter. Si en algún sitio hay un "datos = new ArrayList...", te sobra, solo tiene que haber un objeto ArrayList que modele los datos asociados a la vista.

Godlike
28/09/15, 19:43:25
Al final me voy a liar yo también xD

¿Ese "datos" es sólo una referencia, no? Supongo que lo creaste para tener la referencia al array en el adapter. Si en algún sitio hay un "datos = new ArrayList...", te sobra, solo tiene que haber un objeto ArrayList que modele los datos asociados a la vista.

Jajaja perdona! es que la verdad lo he armado un lío.

Mira, los datos en el Adapter:

private final ArrayList <Producto> datos;

public ConceptoAdapter (Context context, ArrayList<Producto> datos){
super(context, R.layout.activity_parte_tecnico_each, R.id.fakeText, datos);
this.datos = datos;
}Por lo que creo que es correcto recibir este Array en el constructor y emplearlo en el adapter... lo que no comprendo es que si cada vez que añado una fila lo hago desde la Activity principal al Array del activity, por qué el adapter tiene esos datos? si el constructor solo se llama una vez al comienzo!

Aparte de eso, que funciona pero me gustaría saber por qué :rolleyes: he hecho lo siguiente, a ver si lo explico sin liarlo tanto como anteriormente...

- Producto contiene la información de los productos disponibles, pongamos Actualizar software, con su id, una referencia, un precio, etc.

- Concepto debe ser similar, aunque tiene menos campos, porque una vez listo los productos disponibles, el técnico selecciona la Act. de software por ejemplo, pero puede variar el precio para ese caso en concreto, así que al guardarse deberá tener en la base de datos (y así está pensada la estructura del SQLite) un id propio, el id del parte al que se le asigna, el id del producto original (de forma que se sabe qué producto es) y un campo precio y nombre, que pueden ser o no los mismo que el producto original (me lo pdien así).

Ahora, como todo el rato trato con ArrayList de <Producto>, se hacen las modificaciones pertinentes y se guardan y sigue siendo un Producto, así que una vez finalizado el parte, al pulsar para mostrar la siguiente Activity (que es como un resumen que muestra datos) hago lo siguiente:


ArrayList <Concepto> conceptosFinal = new ArrayList<>();
</span>

final MySQLiteHelper db = new MySQLiteHelper(getApplicationContext());
int lastParte = db.getLastParte();
int currentParte = lastParte + 1;
//Parsing Productos to Conceptos and save ArrayList Conceptos in Globals
ListIterator <Producto> iterProducto = conceptos.listIterator();
if(iterProducto.hasNext()){
Producto currentProducto = iterProducto.next();
Concepto currentConcepto = new Concepto();
//Saving original Producto as Concepto with possible changes
currentConcepto.setId_producto(currentProducto.get Id());
currentConcepto.setNombre(currentProducto.getNombr e());
currentConcepto.setPrecio(currentProducto.getPreci o_publico());
currentConcepto.setId_parte(currentParte);


conceptosFinal.add(currentConcepto);}Y con esto pretendo crearme un ArrayList de Concepto, que básicamente serán los productos modificados, y con un id de los productos originales y otro id del parte (que por cierto como aún no está generado lo calculo como último parte introducido+1).


Tengo la sensación de hacer todo un poco a lo loco, pero para mi tiene sentido :silbando:

Lo otro que comentadaba y que lié tanto era que el borrar líneas lo hago desde el adapter, eliminando los datos del array, sin embargo para crear hago un "add" al ArrayList del Activity, no entiendo por qué luego el Adapter tiene esos datos!

Gracias por todo, de verdad!

mocelet
28/09/15, 20:07:39
Cuando haces this.datos=datos en el constructor se copia la referencia al objeto, no su valor (si fuera un tipo primitivo como un int, no). Sólo hay un ArrayList, accesible desde distintas variables.

Godlike
28/09/15, 20:58:49
Cuando haces this.datos=datos en el constructor se copia la referencia al objeto, no su valor (si fuera un tipo primitivo como un int, no). Sólo hay un ArrayList, accesible desde distintas variables.

Joder, en serio!? y sin puntero ni nada? XD

Ves muy lioso lo demás o parece correcto?

Gracias!! :dios:

mocelet
28/09/15, 21:57:46
Joder, en serio!? y sin puntero ni nada? XD

Realmente en Java todo se pasa por valor, no existe el paso por referencia ni los punteros. Lo que ocurre es que el valor de una variable cuando se trata de objetos es una referencia, nunca "el objeto" :) El objeto no se almacena en ninguna variable, se almacena donde le parezca bien a la máquina virtual de Java. Lo que tienes es una referencia para que uses el objeto.

Me explico, cuando haces "new algo" te devuelve una referencia al nuevo objeto, no el objeto. Cuando lo asignas a otra variable estás copiando el valor de esa variable, es decir, copias una referencia a cierto objeto, pero el objeto sigue estando en el mismo sitio. Al objeto sin su referencia no puedes acceder, y en Java no puedes acceder directamente a la memoria como en C, por lo que los punteros no tienen sentido.

Por eso tampoco hace falta un destructor explícito, cuando un objeto no tiene referencias pasa el recolector de basura y lo borra de memoria porque ya nadie va a poder acceder a él.

En los tipos primitivos, por ejemplo int x = 5, igualmente se copia el valor, y el valor es ya el número. De hecho no existe el paso por referencia, nuevamente porque no hay punteros. Si luego haces int y = x, y valdrá 5, pero si luego haces y = 7, x seguirá valiendo 5. No existe ningún mecanismo para que dos variables compartan el mismo valor en memoria, son copias separadas siempre.

En tu caso, datos y la otra variable almacenan las dos el mismo valor, la referencia a cierto objeto, pero si en algún momento le asignas otro valor a cualquiera de las dos variables ya no estarán refiriéndose al mismo objeto (fuente habitual de fallos por cierto).

Bueno, perdón por la extensión, a veces me sale la vena docente jajaja

Sobre los conceptos y productos no me he enterado del todo, pero eso del +1 suena muy extraño... (Edito: en tapatalk sólo veía un chorizo de código, formateado en la web queda mejor xD me uno al comentario de kriogeN, los iteradores son de la década pasada)

kriogeN
28/09/15, 22:11:46
¿Aún hay gente que usa iteradores? Yo pensaba que ya hasta daban error de compilación :D

for (Producto iterProducto : conceptos) {
}

Más limpio imposible.

Godlike
29/09/15, 09:03:56
Realmente en Java todo se pasa por valor, no existe el paso por referencia ni los punteros. Lo que ocurre es que el valor de una variable cuando se trata de objetos es una referencia, nunca "el objeto" :) El objeto no se almacena en ninguna variable, se almacena donde le parezca bien a la máquina virtual de Java. Lo que tienes es una referencia para que uses el objeto.

Me explico, cuando haces "new algo" te devuelve una referencia al nuevo objeto, no el objeto. Cuando lo asignas a otra variable estás copiando el valor de esa variable, es decir, copias una referencia a cierto objeto, pero el objeto sigue estando en el mismo sitio. Al objeto sin su referencia no puedes acceder, y en Java no puedes acceder directamente a la memoria como en C, por lo que los punteros no tienen sentido.

Por eso tampoco hace falta un destructor explícito, cuando un objeto no tiene referencias pasa el recolector de basura y lo borra de memoria porque ya nadie va a poder acceder a él.

En los tipos primitivos, por ejemplo int x = 5, igualmente se copia el valor, y el valor es ya el número. De hecho no existe el paso por referencia, nuevamente porque no hay punteros. Si luego haces int y = x, y valdrá 5, pero si luego haces y = 7, x seguirá valiendo 5. No existe ningún mecanismo para que dos variables compartan el mismo valor en memoria, son copias separadas siempre.

En tu caso, datos y la otra variable almacenan las dos el mismo valor, la referencia a cierto objeto, pero si en algún momento le asignas otro valor a cualquiera de las dos variables ya no estarán refiriéndose al mismo objeto (fuente habitual de fallos por cierto).

Bueno, perdón por la extensión, a veces me sale la vena docente jajaja

Sobre los conceptos y productos no me he enterado del todo, pero eso del +1 suena muy extraño... (Edito: en tapatalk sólo veía un chorizo de código, formateado en la web queda mejor xD me uno al comentario de kriogeN, los iteradores son de la década pasada)

De perdón nada, me encanta aprender y dado el poquísimo tiempo que tengo, mucho mejor si me lo contáis así, vaya tela que me hago mayor por culpa de la programación jaja, me quedé en C y sus punteros!.

Está explicado perfectamente, es decir que las "cajas" de las variables, cuando no se trata de tipos primitivos, contienen referencias siempre. Lo único que no me queda claro es cuando dices que si le asignas otro valor ya no se refieren al mismo objeto... te refieres si le asginas otro valor del tipo otro objeto, no? es decir que apuntaría a otro objeto, pero no si usas métodos del objeto al que apunta (add, remove del ArraList, ó setNombre de un objeto, etc.), verdad?.

Con respecto a lo otro siento liarte tanto!

La cosa es que los productos son fijos, es decir tengo unos productos con una serie de valores... de esa tabla obtengo los datos, el usuario selecciona el que quiere, y hay dos campos (nombre y precio) que son variables, así que al guardarlos almaceno la id del producto para tener todos los campos "originales" y además tengo los dos campos que se han modificado, con lo cual pasa de un ArrayList <Producto> a un ArrayList <Concepto>, de ahí que haga el lío ese de recorrer un Array e ir creando un Array de Concepto, no sé cómo lo ves.

Pero además, si añado 10 conceptos por ejemplo, todos ellos pertenecen a un parte de trabajo (tú vienes a mi empresa y me facturas 10 tareas realizadas), pero el parte no tiene ID en la base de datos hasta que lo guardo finalmente, así que lo que hago es buscar el último ID de parte, lo incremento en 1, y ese es el ID que asigno a cada concepto (en su campo de referencia id_parte) y al guardar el parte pretendo forzar el uso de ese ID (puedo añadir alguna comprobación como ver si ese ID está en uso).

Espero haberlo explicado mejor :rolleyes:

¿Aún hay gente que usa iteradores? Yo pensaba que ya hasta daban error de compilación :D

for (Producto iterProducto : conceptos) {
}

Más limpio imposible.


Ui ui qué bonito se ve! con esto estaría indicando que para cada elemento en Producto (iterProducto) tengo una variable conceptos, que es qué? mi Array original o el de "destino"? no había visto este tipo de código! mira que leo en webs y solo he encontrado lo de los iteradores, es mejor así además de más limpio?

Gracias a ambos! :ok:

kriogeN
29/09/15, 09:17:19
Significa que tienes un contenedor (List, ArrayList, etc) que se llama "conceptos", que los elementos que contiene son Producto. Haciendo eso iteras todos los elementos del contenedor.

List<Producto> conceptos;
.......
for (Producto p : conceptos) {
//Recorre todos los elementos Producto que hay en conceptos, almacenando cada iteración en la variable p
}

Godlike
29/09/15, 09:19:34
Significa que tienes un contenedor (List, ArrayList, etc) que se llama "conceptos", que los elementos que contiene son Producto. Haciendo eso iteras todos los elementos del contenedor.

List<Producto> conceptos;
.......
for (Producto p : conceptos) {
//Recorre todos los elementos Producto que hay en conceptos, almacenando cada iteración en la variable p
}

Genial! entonces me faltaría definir el Array de Concepto que hará de destino, verdad? (siento el lío porque "conceptos" en realidad es un array de Producto, tengo que cambiar todo esto XD).

Gracias! a ver si la termino esta semana, la versión alpha al menos, y puedo ponerme con el curso de Udemy que compré.

mocelet
29/09/15, 09:44:49
Lo único que no me queda claro es cuando dices que si le asignas otro valor ya no se refieren al mismo objeto... te refieres si le asginas otro valor del tipo otro objeto, no? es decir que apuntaría a otro objeto, pero no si usas métodos del objeto al que apunta (add, remove del ArraList, ó setNombre de un objeto, etc.), verdad?.

Exacto, asignar un valor es poner "=". Llamar a métodos del objeto referenciado no cambia el valor de la referencia.

EDITO:
Genial! entonces me faltaría definir el Array de Concepto que hará de destino, verdad? (siento el lío porque "conceptos" en realidad es un array de Producto, tengo que cambiar todo esto XD).

Gracias! a ver si la termino esta semana, la versión alpha al menos, y puedo ponerme con el curso de Udemy que compré.

En realidad sería
for (Producto currentProducto : conceptos) {
// y aquí lo mismo que hacías antes desde currentConcepto = new ...
}

Que es lo mismo que usar un iterador pero te evitas definir el iterador (borra iterProducto), las llamadas al hasNext y al next. El caso es que tu código no recorre todos porque no hay ningún bucle, pero si la idea es recorrerlos todos el for es lo mejor.

Y sí, cambia los nombres para no liarte jeje

Godlike
29/09/15, 10:12:43
Exacto, asignar un valor es poner "=". Llamar a métodos del objeto referenciado no cambia el valor de la referencia.


Gracias!! he estado mirando tus apps, la de enciende la navidad me mola especialmente, la pruebo y te doy 5 stars!. Por cierto, eres profe?


Tenéis una idea muy aproximada de cuánto se cobra por un app como la que estoy haciendo? Mi idea es que si se cobra de 3 a 5 mil euros, por ejemplo, pedirle unos 600, porque he tardado mucho (me la pidió en Navidad), soy novato y me está sirviendo para aprender. Seguiré modificándola y eso ya podemos ver si gratis o no, pero como alguien como vosotros haría toda la app mejor, quiero pedirle bastante menos, pero es que no sé si algo así se cobra 5 mil o mil, o... ni idea.

Godlike
29/09/15, 10:14:41
Exacto, asignar un valor es poner "=". Llamar a métodos del objeto referenciado no cambia el valor de la referencia.

EDITO:


En realidad sería
for (Producto currentProducto : conceptos) {
// y aquí lo mismo que hacías antes desde currentConcepto = new ...
}Que es lo mismo que usar un iterador pero te evitas definir el iterador (borra iterProducto), las llamadas al hasNext y al next. El caso es que tu código no recorre todos porque no hay ningún bucle, pero si la idea es recorrerlos todos el for es lo mejor.

Y sí, cambia los nombres para no liarte jeje


Ostras no tengo bucle! jaja, lo escribí y no lo testeé, a ver si saco un rato y lo modifico, me ha quedado todo claro!

Gracias :)

Godlike
31/10/15, 17:56:01
Sólamente escribo para agradecer vuestra inestimable ayuda, ya está funcionando correctamente, y la aplicación a falta de una cosa que ahora consultaré en un post está finalizada.

De hecho tras tener todo esto me di cuenta de que estaba utilizando productos y conceptos cuando me bastaba con utilizar conceptos y los productos sólo para los desplegables.

En fin, que no sólo me ahorrásteis tiempo sino que me habéis ayudado a aprender más.

Muchas gracias!! :dios: