Buat Tampilan kustom dengan meluaskan tata letak?

107

Saya mencoba membuat Tampilan kustom yang akan menggantikan tata letak tertentu yang saya gunakan di banyak tempat, tetapi saya kesulitan melakukannya.

Pada dasarnya, saya ingin mengganti ini:

<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>

Dengan ini:

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

Jadi, saya tidak menginginkan layout kustom, saya ingin Tampilan kustom (tampilan ini tidak boleh memiliki turunan).

Satu-satunya hal yang dapat berubah dari satu instance MyQuantityBox ke yang lain adalah judulnya. Saya sangat ingin dapat menentukan ini dalam XML (seperti yang saya lakukan pada baris XML terakhir)

Bagaimana saya bisa melakukan ini? Haruskah saya meletakkan RelativeLayout dalam file XML di / res / layout dan memekarnya di kelas MyBoxQuantity saya? Jika ya, bagaimana saya melakukannya?

Terima kasih!

nbarraille
sumber
Lihat "Kontrol Gabungan" di Android, dan tautan ini: stackoverflow.com/questions/1476371/…
greg7gkb

Jawaban:

27

Ya, Anda bisa melakukan ini. RelativeLayout, LinearLayout, dll adalah Tampilan sehingga tata letak khusus adalah tampilan khusus. Hanya sesuatu yang perlu dipertimbangkan karena jika Anda ingin membuat tata letak kustom Anda bisa.

Yang ingin Anda lakukan adalah membuat Kontrol Gabungan. Anda akan membuat subclass RelativeLayout, menambahkan semua komponen Anda dalam kode (TextView, dll), dan di konstruktor Anda, Anda dapat membaca atribut yang diteruskan dari XML. Anda kemudian dapat meneruskan atribut itu ke TextView judul Anda.

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

chubbsondubs
sumber
130

Agak tua, tapi saya pikir berbagi bagaimana saya akan melakukannya, berdasarkan jawaban chubbsondubs: Saya menggunakan FrameLayout(lihat Dokumentasi ), karena digunakan untuk memuat satu tampilan, dan memekarkan ke dalamnya tampilan dari xml.

Kode berikut:

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);
    }
}
Rubah
sumber
13
Karena kelas View memiliki metode inflate () statis, tidak perlu LayoutInflater.from ()
keluar dari
1
Bukankah ini hanya solusi Johannes dari sini: stackoverflow.com/questions/17836695/… Namun, ini meningkatkan tata letak lain di dalamnya. Jadi itu bukan solusi terbaik yang saya kira.
Tobias Reich
3
Memang, tetapi solusi Johannes berasal dari 7.24.13, dan pikiran berasal dari 7.1.13 ... Selain itu, solusi saya menggunakan FrameLayout yang seharusnya hanya berisi satu Tampilan (seperti yang tertulis dalam dokumen yang direferensikan dalam solusi). Jadi sebenarnya ini dimaksudkan untuk digunakan sebagai placeholder untuk View. Saya tidak tahu solusi apa pun yang tidak melibatkan penggunaan placeholder untuk View yang diperbesar.
Fox
Saya tidak mengerti. Metode itu (memekarkan) mengembalikan tampilan, yang diabaikan. Sepertinya Anda perlu menambahkannya ke tampilan saat ini.
Jeffrey Blattman
1
@Jeffrey Blattman silakan periksa metode View.inflate , kami menggunakan yang ini (menentukan root sebagai this, parameter ke-3)
V1raNi
36

Berikut ini demo sederhana untuk membuat customview (tampilan majemuk) dengan menggembungkan dari 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

Kita harus menggunakan di mergesini daripada ConstraintLayoutkarena

Jika kita gunakan di ConstraintLayoutsini, hierarki layout akan ConstraintLayout-> ConstraintLayout-> ImageView+ TextView=> kita punya 1 redundan ConstraintLayout=> tidak terlalu bagus untuk performa

<?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>

Menggunakan 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>

Hasil

masukkan deskripsi gambar di sini

Demo Github

Phan Van Linh
sumber
4
Ini harus menjadi jawaban yang diterima atau paling banyak dipilih di utas ini karena menyebutkan hierarki tata letak yang tidak perlu.
Farid
15

Gunakan LayoutInflater seperti yang saya tunjukkan di bawah.

    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;
    }
Tsunaze
sumber
Saya telah mencoba yang sama. itu bekerja dengan baik. tetapi, ketika saya mengklik btn1, itu akan memanggil layanan web dan setelah menerima tanggapan dari server, saya ingin memperbarui beberapa teks di posisi tertentu teks2 .. ada bantuan tolong?
harikrishnan
7

Dalam praktiknya, saya menemukan bahwa Anda perlu sedikit berhati-hati, terutama jika Anda menggunakan sedikit xml berulang kali. Misalkan, misalnya, Anda memiliki tabel yang ingin Anda buat baris tabelnya untuk setiap entri dalam daftar. Anda telah menyiapkan beberapa xml:

Masuk 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>

Kemudian Anda ingin membuatnya sekali per baris dengan beberapa kode. Ini mengasumsikan bahwa Anda telah mendefinisikan TableLayout myTable induk untuk dilampirkan Baris ke.

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);
}

Untuk contoh sederhana yang jelas tentang menangani rowButton.setOnClickListener (ini), lihat Onclicklistener untuk tombol yang dibuat secara terprogram .

Neil Townsend
sumber
Hai Neil, saya telah mencoba yang sama. itu bekerja dengan baik. tetapi, ketika saya mengklik rowButton, itu akan memanggil layanan web dan setelah menerima tanggapan dari server, saya ingin memperbarui beberapa teks di posisi tertentu rowText .. ada bantuan tolong?
harikrishnan
lihat pertanyaan saya juga. stackoverflow.com/questions/48719970/…
harikrishnan