Створити спеціальний перегляд, надувши макет?


107

Я намагаюся створити спеціальний вигляд, який би замінив певний макет, який я використовую в декількох місцях, але я намагаюся це зробити.

В основному, я хочу це замінити:

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

Цим:

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

Отже, я не хочу користувацького макета, я хочу користувацький перегляд (це перегляд не повинно мати дитину).

Єдине, що може змінитися з одного примірника MyQuantityBox на інший - це заголовок. Я дуже хотів би мати можливість вказати це в XML (як це я роблю в останньому рядку XML)

Як я можу це зробити? Чи слід помістити RelativeLayout у XML-файл у / res / layout та надути його у своєму класі MyBoxQuantity? Якщо так, як це зробити?

Дякую!


Дивіться розділ «Складові елементи управління» в Android, і ця посилання: stackoverflow.com/questions/1476371 / ...
greg7gkb

Відповіді:


27

Так, ви можете це зробити. RelativeLayout, LinearLayout тощо - це перегляди, тож власний макет - це власний вигляд. Просто щось врахувати, бо якби ви хотіли створити спеціальний макет, ви могли б.

Що ви хочете зробити, це створити складний контроль. Ви створите підклас RelativeLayout, додасте всі наші компоненти в код (TextView тощо), і в конструкторі ви зможете прочитати атрибути, передані з XML. Потім ви можете передати цей атрибут до заголовка TextView.

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


130

Трохи старий, але я думав поділитися тим, як би це зробити, грунтуючись на відповіді chubbsondubs: я використовую FrameLayout(див. Документація ), оскільки він використовується, щоб містити єдиний вигляд, і вбудувати в нього подання з xml.

Код наступний:

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
Оскільки View клас має статичний Inflate () метод не існує потреба в LayoutInflater.from ()
околичне

1
Це не просто рішення Йоганнеса звідси: stackoverflow.com/questions/17836695/… Але все-таки це надуває інший макет всередині. Тож це насправді не найкращий спосіб вирішення.
Тобіас Райх

3
так, але рішення Йоганнеса - від 7.24.13, а розум - від 7.1.13 ... Крім того, у моєму рішенні використовується FrameLayout, який повинен містити лише один вид (як написано в документі, на який йдеться у рішенні). Таким чином, насправді він призначений для використання в якості заповнювача для перегляду. Я не знаю жодного рішення, яке б не використовувало заповнення заповнення для завищеного перегляду.
Фокс

Я не розумію. Цей метод (надуває) повертає вигляд, який ігнорується. Здається, вам потрібно додати його до поточного подання.
Джефрі Блатман

1
@ Джефрі Блатман, будь ласка, перегляньте метод View.inflate , ми використовуємо цей (вказавши корінь як this, третій параметр)
V1raNi

36

Ось проста демонстраційна версія для створення користувальницького перегляду (складного перегляду) шляхом надування з 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

Ми повинні використовувати mergeтут замість того , щоб ConstraintLayoutз - за

Якщо ми будемо використовувати ConstraintLayoutтут, ієрархія макета буде ConstraintLayout-> ConstraintLayout-> ImageView+ TextView=> у нас є 1 зайвий ConstraintLayout=> не дуже хороший для продуктивності

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

Використання 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>

Результат

введіть тут опис зображення

Демонстрація Github


4
Це має бути або прийнятою, або найбільш голосовою відповіддю в цій темі, оскільки вона зазначає непотрібну ієрархію компонування.
Фарид

15

Використовуйте LayoutInflater, як я показав нижче.

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

Я спробував те саме. його добре працює. але, коли я натискаю btn1, він зателефонує до веб-служб і після отримання відповіді від сервера, я хочу оновити деякий текст, зокрема позицію text2 ... будь-яка допомога pls?
harikrishnan

7

На практиці я виявив, що потрібно бути обережним, особливо якщо ви багато разів використовуєте трохи xml. Припустимо, наприклад, що у вас є таблиця, для якої потрібно створити рядок таблиці для кожного запису в списку. Ви встановили xml:

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

Потім ви хочете створити його один раз у рядку з деяким кодом. Передбачається, що ви визначили батьківський TableLayout myTable для приєднання рядків.

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

Для наочного простого прикладу поводження з rowButton.setOnClickListener (це) див. Onclicklistener для програмно створеної кнопки .


Привіт Ніл, я спробував те саме. його добре працює. але, коли я натискаю rowButton, він зателефонує до веб-служб і після отримання відповіді від сервера, я хочу оновити текст у певній позиції rowText .. будь-яка довідка pls?
harikrishnan

дивіться також моє запитання. stackoverflow.com/questions/48719970 / ...
Харікрішнан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.