通过膨胀布局来创建自定义视图?

时间:2010-12-01 20:34:06

标签: android xml view

我正在尝试创建一个自定义视图来替换我在多个地方使用的某个布局,但我很难这样做。

基本上,我想替换它:

<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放在/ res / layout中的XML文件中并在MyBoxQuantity类中对其进行充气吗?如果是,我该怎么做?

谢谢!

6 个答案:

答案 0 :(得分:99)

有点旧,但我想根据chubbsondubs的回答分享我是怎么做的: 我使用FrameLayout(请参阅Documentation),因为它用于包含单个视图,并从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() {
        View view = inflate(getContext(), R.layout.my_view_layout, null);
        addView(view);
    }
}

答案 1 :(得分:24)

是的,你可以这样做。 RelativeLayout,LinearLayout等是Views,因此自定义布局是自定义视图。只需考虑一下,因为如果你想创建一个自定义布局,你可以。

您要做的是创建一个复合控件。您将创建RelativeLayout的子类,在代码中添加所有组件(TextView等),在构造函数中,您可以读取从XML传入的属性。然后,您可以将该属性传递给标题TextView。

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

答案 2 :(得分:11)

使用布局inflater,如下所示

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

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


    return v;
}

答案 3 :(得分: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(this)的简单示例,请参阅Onclicklistener for a programatically created button

答案 4 :(得分:4)

这是一个简单的演示,用于通过从xml扩展来创建customview(compoundview)

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个冗余{{ 1}} =>效果不佳

ConstraintLayout

使用 activity_main.xml

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

结果

enter image description here

Github demo

答案 5 :(得分:-1)

使用 Kotlin 的简单自定义视图

用你想扩展的任何视图替换 FrameLayout

 /**
 * Simple Custom view
 */
class CustomView@JvmOverloads
constructor(context: Context,
            attrs: AttributeSet? = null,
            defStyleAttr: Int = 0)
    : FrameLayout(context, attrs, defStyleAttr) {


    init {
        // Init View
       val rootView = (getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater)
                .inflate(R.layout.custom_layout, this, true)
       val titleView= rootView.findViewById(id.txtTitle)

        // Load Values from XML
        val attrsArray = getContext().obtainStyledAttributes(attrs, R.styleable.CutsomView, defStyleAttr, 0)
        val titleString = attrsArray.getString(R.styleable.cutomAttrsText)
        attrsArray.recycle()


    }

}