programacion - ¿Cómo pasar la referencia de vista a la vista personalizada de Android?
manual de android en pdf (3)
Hice lo siguiente
1) Creando un styleable
<declare-styleable name="Viewee">
<attr name="linkedView" format="reference"/>
</declare-styleable>
2) definiendo diseño de vista personalizado
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffc0">
<TextView
android:id="@+id/custom_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="[text]"
/>
</LinearLayout>
3) Creando la clase requerida
public class Viewee extends LinearLayout
{
public Viewee(Context context, AttributeSet attributeSet)
{
super(context, attributeSet);
View.inflate(context, R.layout.viewee, this);
TextView textView = (TextView) findViewById(R.id.custom_text);
TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.Viewee);
int id = typedArray.getResourceId(R.styleable.Viewee_linkedView, 0);
if (id != 0)
{
View view = findViewById(id);
textView.setText(((TextView) view).getText().toString());
}
typedArray.recycle();
}
}
y finalmente en una actividad como la de abajo.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.ns"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvTest"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="android"/>
<com.ns.Viewee
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:linkedView="@+id/tvTest"
/>
</LinearLayout>
ahora, aunque recibo un id
distinto de cero en ViewView constractor, findViewById(id)
retuns null y ocurre NullPointerException
.
¿Qué me estoy perdiendo?
¡Encontré la respuesta!
El problema fue con findViewById(id)
y donde lo llamé. findViewById
solo busca una vista secundaria, no existe una vista en el nivel de jerarquía superior como dice la documentation . Así que tengo que llamar a algo como getRootView().findViewById(id)
pero eso también devuelve null
porque el lugar al que llamé no era correcto.
En Viewee
constractor Viewee
no se ha adjuntado a su raíz aún, por lo que la llamada causa NullPointerException
.
Entonces, si llamo a getRootView().findViewById(id)
algún otro lugar después de la construcción, funciona bien y tanto "@+id/tvTest"
como "@id/tvTest"
son correctos. ¡Lo he probado!
La respuesta es la siguiente
public class Viewee extends LinearLayout
{
public Viewee(Context context, AttributeSet a)
{
super(context, attributeSet);
View.inflate(context, R.layout.main6, this);
TextView textView = (TextView) findViewById(R.id.custom_text);
TypedArray t = context.obtainStyledAttributes(a, R.styleable.Viewee);
int id = t.getResourceId(R.styleable.Viewee_linkedView, 0);
if (id != 0)
{
_id = id;
}
t.recycle();
}
private int _id;
public void Foo()
{
TextView textView = (TextView) findViewById(R.id.custom_text);
View view = getRootView().findViewById(_id);
textView.setText(((TextView) view).getText().toString());
}
}
y se llama a Foo
cuando se requiere que procese la vista adjunta a través de su ID de referencia en otro lugar de su actividad y similares.
El crédito es completamente para aquellos que contribuyeron en esta publicación . No había visto ese post antes de enviar la pregunta.
Sé que esta es una pregunta antigua, pero pensé que agregaría otra forma de hacerlo ya que quería resumir todo en mi vista personalizada.
En lugar de llamar desde el exterior, otra forma de obtener una vista más alta en la jerarquía, me he conectado a onAttachedToWindow()
lugar:
public class MyCustomView extends LinearLayout {
private int siblingResourceId;
private View siblingView;
public MyCustomView(Context context, AttributeSet a) {
super(context, attributeSet);
inflate(context, R.layout.main6, this);
TextView textView = (TextView) findViewById(R.id.custom_text);
TypedArray t = context.obtainStyledAttributes(a, R.styleable.Viewee);
siblingResourceId = t.getResourceId(R.styleable.MyCustomView_siblingResourceId, NO_ID);
t.recycle();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
if (siblingResourceId != NO_ID) {
siblingView = ((View) getParent()).findViewById(siblingResourceId);
}
}
}
onAttachedToWindow
se llama bastante pronto, pero aparentemente lo suficientemente tarde para que toda la jerarquía de vistas se haya establecido. Funciona impecable al menos para mis necesidades y está un poco más controlado y no necesita interacción desde el exterior para trabajar ;-)
EDITAR: código Kotlin añadido
class MyCustomView(context: Context, attributeSet: AttributeSet) : LinearLayout(context, attributeSet) {
private val siblingResourceId: Int
private lateinit var siblingView: View
// All other constructors left out for brevity.
init {
inflate(context, R.layout.main6, this)
val textView = findViewById<TextView>(R.id.custom_text)
val t = context.obtainStyledAttributes(a, R.styleable.Viewee)
siblingResourceId = t.getResourceId(R.styleable.MyCustomView_siblingResourceId, NO_ID)
t.recycle()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (siblingResourceId != NO_ID) {
siblingView = (parent as View).findViewById(siblingResourceId)
}
}
}
Nota: Suponemos que el elemento parent
de esta View
personalizada es una View
sí misma.
Su android:id
descrito se establece en app:linkedView="@+id/tvTest
. Sin embargo, @+id/tvTest
se usa para crear una nueva identificación con el nombre" tvTest ". Lo que desea hacer es usar app:linkedView="@id/tvTest
.