通过放大布局来创建自定义视图?

我试图创建一个自定义视图,将取代我在多个地方使用的某个布局,但我正在努力做到这一点。

基本上,我想取代这个:

<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 中指定这一点(正如我在最后一行中所做的那样)

我怎么能这么做?我是否应该将 RelativeLayout 放在/res/layout 的 XML 文件中,并在 MyBoxQuantity 类中对其进行充气?如果是,我该怎么做?

谢谢!

155893 次浏览

是的,你能做到。RelativeLayout、 LinearLayout 等是视图,因此自定义布局是自定义视图。只是一些需要考虑的事情,因为如果你想创建一个自定义布局,你可以。

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

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

按照我下面所示的方式使用 Layoutflater。

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

在实践中,我发现您需要稍微小心一点,特别是当您重复使用一点 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>

然后您需要使用一些代码在每行中创建一次。它假设您已经定义了一个父 TableLayoutmyTable 来将 Rows 附加到。

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)的简单示例,请参见 程序创建按钮的 Onclicklisten

有点老了,但我想分享一下我会怎么做,基于胖子的回答: 我使用 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);
}
}

下面是一个简单的演示,通过从 xml 充气创建 customview (compoundview)

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 image_thumb = findViewById<ImageView>(R.id.image_thumb)
val text_title = findViewById<TextView>(R.id.text_title)


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

自定义 _ 布局. 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>

结果

enter image description here

请参阅完整代码:
Github

使用 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()
}
}

有多个答案指向不同方法的相同方向,我相信下面是最简单的不使用任何第三方库的方法,即使你可以使用 Java。

在 Kotlin;

创建 values/attr.xml

<resources>
<declare-styleable name="DetailsView">
<attr format="string" name="text"/>
<attr format="string" name="value"/>
</declare-styleable>
</resources>

为视图创建 layout/details_view.xml文件

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


<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">


<TextView
android:id="@+id/text_label"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.5"
tools:text="Label" />


<TextView
android:id="@+id/text_value"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.5"
tools:text="Value" />


</LinearLayout>


</merge>

创建自定义视图小部件 DetailsView.kt

import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.payable.taponphone.R


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


private val attributes: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.DetailsView)
private val view: View = View.inflate(context, R.layout.details_view, this)


init {
view.findViewById<TextView>(R.id.text_label).text = attributes.getString(R.styleable.DetailsView_text)
view.findViewById<TextView>(R.id.text_value).text = attributes.getString(R.styleable.DetailsView_value)
}
}

现在你可以在应用程序的任何地方调用这个小部件了,如下所示

<com.yourapp.widget.DetailsView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:text="Welcome"
app:value="Feb" />