我正在尝试创建一个自定义Android控件,包含LinearLayout 。您可以将其视为带有花式边框,背景,左侧图像的扩展LinearLayout ......
我可以用XML完成所有工作(效果很好),但由于我的应用程序中有很多次出现,因此难以维护。我认为有这样的东西会更好:
/* Main.xml */
<MyFancyLayout>
<TextView /> /* what goes inside my control's linear layout */
</MyfancyLayout>
你会怎么做?我想避免在onMeasure / onLayout方法上重写整个线性布局。这就是我现在所拥有的:
/* MyFancyLayout.xml */
<TableLayout>
<ImageView />
<LinearLayout id="container" /> /* where I want the real content to go */
</TableLayout>
和
/* MyFancyLayout.java */
public class MyFancyLayout extends LinearLayout
{
public MyFancyLayout(Context context) {
super(context);
View.inflate(context, R.layout.my_fancy_layout, this);
}
}
您如何在正确的位置(id =容器)插入用户指定的内容(main.xml中的TextView)?
干杯!
罗曼
-----编辑-------
仍然没有运气,所以我改变了我的设计以使用更简单的布局,并决定使用一些重复的XML。仍然非常感兴趣的人知道如何做到这一点!
答案 0 :(得分:10)
这个确切的问题已经困扰了我一段时间了,但现在我才解决了。
乍一看,问题在于声明性内容(在你的情况下是TextView)在 ctor之后(我们通常会给我们的布局充气)的某个时候实例化,所以它也是早期同时拥有声明和模板内容,以便将前者推入后者。
我找到了一个可以操纵两者的地方:它是一个onFinishInflate()方法。以下是我的情况:
@Override
protected void onFinishInflate() {
int index = getChildCount();
// Collect children declared in XML.
View[] children = new View[index];
while(--index >= 0) {
children[index] = getChildAt(index);
}
// Pressumably, wipe out existing content (still holding reference to it).
this.detachAllViewsFromParent();
// Inflate new "template".
final View template = LayoutInflater.from(getContext())
.inflate(R.layout.labeled_layout, this, true);
// Obtain reference to a new container within "template".
final ViewGroup vg = (ViewGroup)template.findViewById(R.id.layout);
index = children.length;
// Push declared children into new container.
while(--index >= 0) {
vg.addView(children[index]);
}
// They suggest to call it no matter what.
super.onFinishInflate();
}
上面引用的labeled_layout.xml与此类似:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation ="vertical"
android:layout_width ="fill_parent"
android:layout_height ="wrap_content"
android:layout_marginLeft ="8dip"
android:layout_marginTop ="3dip"
android:layout_marginBottom ="3dip"
android:layout_weight ="1"
android:duplicateParentState ="true">
<TextView android:id ="@+id/label"
android:layout_width ="fill_parent"
android:layout_height ="wrap_content"
android:singleLine ="true"
android:textAppearance ="?android:attr/textAppearanceMedium"
android:fadingEdge ="horizontal"
android:duplicateParentState="true" />
<LinearLayout
android:id ="@+id/layout"
android:layout_width ="fill_parent"
android:layout_height ="wrap_content"
android:layout_marginLeft ="8dip"
android:layout_marginTop ="3dip"
android:duplicateParentState="true" />
</LinearLayout>
现在(仍然在其他地方省略一些细节)我们可能会像这样使用它:
<com.example.widget.LabeledLayout
android:layout_width ="fill_parent"
android:layout_height ="wrap_content">
<!-- example content -->
</com.example.widget.LabeledLayout>
答案 1 :(得分:7)
这种方法为我节省了大量代码! :)
正如esteewhy所解释的那样,只需在onFinishInflate()
中将xml定义的内容交换到您自己的布局内部。例如:
我获取了我在xml中指定的内容:
<se.jog.custom.ui.Badge ... >
<ImageView ... />
<TextView ... />
</se.jog.custom.ui.Badge>
...并将它们移至我想要的LinearLayout
内部contents
:
public class Badge extends LinearLayout {
//...
private LinearLayout badge;
private LinearLayout contents;
// This way children can be added from xml.
@Override
protected void onFinishInflate() {
View[] children = detachChildren(); // gets and removes children from parent
//...
badge = (LinearLayout) layoutInflater.inflate(R.layout.badge, this);
contents = (LinearLayout) badge.findViewById(R.id.badge_contents);
for (int i = 0; i < children.length; i++)
addView(children[i]); //overridden, se below.
//...
super.onFinishInflate();
}
// This way children can be added from other code as well.
@Override
public void addView(View child) {
contents.addView(child);
}
结合custom XML attributes事情变得非常易于维护。
答案 2 :(得分:1)
您可以通过扩展LinearLayout来创建MyFancyLayout类。添加调用方法的三个构造函数(在本例中为“initialize”)以设置其余视图:
public MyFancyLayout(Context context) {
super(context);
initialize();
}
public MyFancyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public MyFancyLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
在初始化期间,您可以执行添加额外视图所需的任何操作。你可以获得LayoutInflater并膨胀另一个布局:
final LayoutInflater inflator = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflator.inflate(R.layout.somecommonlayout, this);
或者您可以在代码中创建视图并添加它们:
ImageView someImageView = new ImageView(getContext());
someImageView.setImageDrawable(myDrawable);
someImageView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
addView(someImageView);
如果您要使用Context很多,可以在构造函数中存储对它的引用,而不是使用getContext()
来节省一点开销。
答案 3 :(得分:0)
只需使用以下内容:
<org.myprogram.MyFancyLayout>
...
</org.myprogram.MyFancyLayout>
有用的链接 - http://www.anddev.org/creating_custom_views_-_the_togglebutton-t310.html