Creare una visualizzazione personalizzata gonfiando un layout?


107

Sto cercando di creare una visualizzazione personalizzata che sostituisca un determinato layout che utilizzo in più posizioni, ma faccio fatica a farlo.

Fondamentalmente, voglio sostituire questo:

<RelativeLayout
 android:id="@+id/dolphinLine"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
    android:layout_centerInParent="true"
 android:background="@drawable/background_box_light_blue"
 android:padding="10dip"
 android:layout_margin="10dip">
  <TextView
   android:id="@+id/dolphinTitle"
   android:layout_width="200dip"
   android:layout_height="100dip"
   android:layout_alignParentLeft="true"
   android:layout_marginLeft="10dip"
   android:text="@string/my_title"
   android:textSize="30dip"
   android:textStyle="bold"
   android:textColor="#2E4C71"
   android:gravity="center"/>
  <Button
   android:id="@+id/dolphinMinusButton"
   android:layout_width="100dip"
   android:layout_height="100dip"
   android:layout_toRightOf="@+id/dolphinTitle"
   android:layout_marginLeft="30dip"
   android:text="@string/minus_button"
   android:textSize="70dip"
   android:textStyle="bold"
   android:gravity="center"
   android:layout_marginTop="1dip"
   android:background="@drawable/button_blue_square_selector"
   android:textColor="#FFFFFF"
   android:onClick="onClick"/>
  <TextView
   android:id="@+id/dolphinValue"
   android:layout_width="100dip"
   android:layout_height="100dip"
   android:layout_marginLeft="15dip"
   android:background="@android:drawable/editbox_background"
   android:layout_toRightOf="@+id/dolphinMinusButton"
   android:text="0"
   android:textColor="#2E4C71"
   android:textSize="50dip"
   android:gravity="center"
   android:textStyle="bold"
   android:inputType="none"/>
  <Button
   android:id="@+id/dolphinPlusButton"
   android:layout_width="100dip"
   android:layout_height="100dip"
   android:layout_toRightOf="@+id/dolphinValue"
   android:layout_marginLeft="15dip"
   android:text="@string/plus_button"
   android:textSize="70dip"
   android:textStyle="bold"
   android:gravity="center"
   android:layout_marginTop="1dip"
   android:background="@drawable/button_blue_square_selector"
   android:textColor="#FFFFFF"
   android:onClick="onClick"/>
</RelativeLayout>

Da questo:

<view class="com.example.MyQuantityBox"
    android:id="@+id/dolphinBox"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:myCustomAttribute="@string/my_title"/>

Quindi, non voglio un layout personalizzato, voglio una vista personalizzata (non dovrebbe essere possibile per questa vista avere un figlio).

L'unica cosa che potrebbe cambiare da un'istanza di MyQuantityBox a un'altra è il titolo. Mi piacerebbe molto poterlo specificare nell'XML (come faccio nell'ultima riga XML)

Come posso fare questo? Devo mettere RelativeLayout in un file XML in / res / layout e gonfiarlo nella mia classe MyBoxQuantity? Se sì, come posso farlo?

Grazie!


Vedere "Controlli Compound" di Android, e questo link: stackoverflow.com/questions/1476371/...
greg7gkb

Risposte:


27

Si, puoi fare questo. RelativeLayout, LinearLayout e così via sono visualizzazioni, quindi un layout personalizzato è una visualizzazione personalizzata. Solo qualcosa da considerare perché se volessi creare un layout personalizzato potresti.

Quello che vuoi fare è creare un controllo composto. Creerai una sottoclasse di RelativeLayout, aggiungerai tutti i nostri componenti nel codice (TextView, ecc.) E nel tuo costruttore potrai leggere gli attributi passati dall'XML. È quindi possibile passare quell'attributo al titolo TextView.

http://developer.android.com/guide/topics/ui/custom-components.html


130

Un po 'vecchio, ma ho pensato di condividere come lo farei, in base alla risposta di chubbsondubs: io uso FrameLayout(vedi Documentazione ), poiché è usato per contenere una singola vista, e gonfio la vista dall'xml.

Codice seguente:

public class MyView extends FrameLayout {
    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public MyView(Context context) {
        super(context);
        initView();
    }

    private void initView() {
        inflate(getContext(), R.layout.my_view_layout, this);
    }
}

13
Dal View classe ha gonfiare statico () metodo non v'è alcuna necessità di LayoutInflater.from ()
periferico

1
Non è questa solo la soluzione di Johannes da qui: stackoverflow.com/questions/17836695/… Tuttavia, questo gonfia un altro layout all'interno. Quindi non è davvero la migliore soluzione che immagino.
Tobias Reich

3
lo è, ma la soluzione di Johannes è della 7.24.13 e la mente era della 7.1.13 ... Inoltre, la mia soluzione usa FrameLayout che dovrebbe contenere solo una vista (come scritto nel documento a cui si fa riferimento nella soluzione). Quindi in realtà è pensato per essere utilizzato come segnaposto per una vista. Non conosco alcuna soluzione che non invii utilizzando un segnaposto per la visualizzazione gonfiata.
Fox

Non lo capisco. Quel metodo (inflate) restituisce una vista, che viene ignorata. Sembra che sia necessario aggiungerlo alla visualizzazione corrente.
Jeffrey Blattman,

1
@ Jeffrey Blattman per favore controlla il metodo View.inflate , usiamo questo (specificando la radice come this, 3 ° parametro)
V1raNi

36

Ecco una semplice demo per creare customview (compoundview) gonfiando da xml

attrs.xml

<resources>

    <declare-styleable name="CustomView">
        <attr format="string" name="text"/>
        <attr format="reference" name="image"/>
    </declare-styleable>
</resources>

CustomView.kt

class CustomView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
        ConstraintLayout(context, attrs, defStyleAttr) {

    init {
        init(attrs)
    }

    private fun init(attrs: AttributeSet?) {
        View.inflate(context, R.layout.custom_layout, this)

        val ta = context.obtainStyledAttributes(attrs, R.styleable.CustomView)
        try {
            val text = ta.getString(R.styleable.CustomView_text)
            val drawableId = ta.getResourceId(R.styleable.CustomView_image, 0)
            if (drawableId != 0) {
                val drawable = AppCompatResources.getDrawable(context, drawableId)
                image_thumb.setImageDrawable(drawable)
            }
            text_title.text = text
        } finally {
            ta.recycle()
        }
    }
}

custom_layout.xml

Noi dobbiamo usare mergequi invece di ConstraintLayoutperché

Se usiamo ConstraintLayoutqui, la gerarchia del layout sarà ConstraintLayout-> ConstraintLayout-> ImageView+ TextView=> abbiamo 1 ridondante ConstraintLayout=> non molto buono per le prestazioni

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:parentTag="android.support.constraint.ConstraintLayout">

    <ImageView
        android:id="@+id/image_thumb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="ContentDescription"
        tools:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/text_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="@id/image_thumb"
        app:layout_constraintStart_toStartOf="@id/image_thumb"
        app:layout_constraintTop_toBottomOf="@id/image_thumb"
        tools:text="Text" />

</merge>

Utilizzando activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <your_package.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f00"
        app:image="@drawable/ic_android"
        app:text="Android" />

    <your_package.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0f0"
        app:image="@drawable/ic_adb"
        app:text="ADB" />

</LinearLayout>

Risultato

inserisci qui la descrizione dell'immagine

Demo di Github


4
Questa dovrebbe essere la risposta accettata o più votata in questo thread poiché menziona la gerarchia del layout non necessaria.
Farid

15

Usa LayoutInflater come mostrato di seguito.

    public View myView() {
        View v; // Creating an instance for View Object
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = inflater.inflate(R.layout.myview, null);

        TextView text1 = v.findViewById(R.id.dolphinTitle);
        Button btn1 = v.findViewById(R.id.dolphinMinusButton);
        TextView text2 = v.findViewById(R.id.dolphinValue);
        Button btn2 = v.findViewById(R.id.dolphinPlusButton);

        return v;
    }

Ho provato lo stesso. funziona bene. ma, quando clicco su btn1, chiamerà i servizi web e dopo aver ricevuto la risposta dal server, voglio aggiornare del testo in una posizione particolare text2..any help pls?
harikrishnan

7

In pratica, ho scoperto che devi stare un po 'attento, soprattutto se stai usando ripetutamente un po' di xml. Supponi, ad esempio, di avere una tabella per cui desideri creare una riga di tabella per ogni voce in un elenco. Hai impostato alcuni xml:

In my_table_row.xml:

<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android" 
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent" android:id="@+id/myTableRow">
    <ImageButton android:src="@android:drawable/ic_menu_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rowButton"/>
    <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="TextView" android:id="@+id/rowText"></TextView>
</TableRow>

Quindi vuoi crearlo una volta per riga con del codice. Si presume che tu abbia definito un TableLayout myTable padre a cui allegare le righe.

for (int i=0; i<numRows; i++) {
    /*
     * 1. Make the row and attach it to myTable. For some reason this doesn't seem
     * to return the TableRow as you might expect from the xml, so you need to
     * receive the View it returns and then find the TableRow and other items, as
     * per step 2.
     */
    LayoutInflater inflater = (LayoutInflater)getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View v =  inflater.inflate(R.layout.my_table_row, myTable, true);

    // 2. Get all the things that we need to refer to to alter in any way.
    TableRow    tr        = (TableRow)    v.findViewById(R.id.profileTableRow);
    ImageButton rowButton = (ImageButton) v.findViewById(R.id.rowButton);
    TextView    rowText   = (TextView)    v.findViewById(R.id.rowText);

    // 3. Configure them out as you need to
    rowText.setText("Text for this row");
    rowButton.setId(i); // So that when it is clicked we know which one has been clicked!
    rowButton.setOnClickListener(this); // See note below ...           

    /*
     * To ensure that when finding views by id on the next time round this
     * loop (or later) gie lots of spurious, unique, ids.
     */
    rowText.setId(1000+i);
    tr.setId(3000+i);
}

Per un chiaro esempio semplice sulla gestione di rowButton.setOnClickListener (this), vedere Onclicklistener per un pulsante creato in modo programmatico .


Ciao Neil, ho provato lo stesso. funziona bene. ma, quando faccio clic su rowButton, chiamerà i servizi web e dopo aver ricevuto la risposta dal server, voglio aggiornare del testo in una posizione particolare rowText..any help pls?
harikrishnan

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.