简体   繁体   English

Android:具有现有布局的自定义视图的多个视图子项

[英]Android: Multiple view children for custom view with existing layout

I have to build a more complex custom view in Android.我必须在 Android 中构建更复杂的自定义视图。 The final layout should look like this:最终布局应如下所示:

<RelativeLayout>
  <SomeView />
  <SomeOtherView />
  <!-- maybe more layout stuff here later -->
  <LinearLayout>
    <!-- the children -->
  </LinearLayout>
</RelativeLayout>

However, in the XML files I just want do define this (without defining SomeView, SomeOtherView etc.):但是,在 XML 文件中我只想定义它(不定义 SomeView、SomeOtherView 等):

<MyCustomView>
  <!-- the children -->
</MyCustomView>

Is this possible in Android, and if yes: What would be the cleanest way to do it?这在 Android 中是否可行,如果是:最干净的方法是什么? The possible solutions that came to my mind were 'override the addView() methods' and 'remove all views and add them again later', but I am unsure which way to go...我想到的可能解决方案是“覆盖 addView() 方法”和“删除所有视图并稍后再次添加它们”,但我不确定该走哪条路......

Thanks a lot in advance for your help: :)非常感谢您的帮助::)

It's absolutely possible, and encouraged, to create custom container views.创建自定义容器视图是完全可能的,并且受到鼓励。 This is what Android would call a compound control.这就是 Android 所说的复合控件。 So:所以:

public class MyCustomView extends RelativeLayout {
    private LinearLayout mContentView;

    public MyCustomView(Context context) {
        this(context, null);
    }

    public MyCustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        //Inflate and attach your child XML
        LayoutInflater.from(context).inflate(R.layout.custom_layout, this);
        //Get a reference to the layout where you want children to be placed
        mContentView = (LinearLayout) findViewById(R.id.content);

        //Do any more custom init you would like to access children and do setup
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if(mContentView == null){
            super.addView(child, index, params);
        } else {
            //Forward these calls to the content view
            mContentView.addView(child, index, params);
        }
    }
}

You can override as many versions of addView() as you feel are necessary, but in the end they all call back to the version I placed in the sample.您可以根据需要覆盖任意多个版本的addView() ,但最终它们都会回调到我放置在示例中的版本。 Overriding just this method will have the framework pass all children found inside its XML tag to a specific child container.仅覆盖此方法将使框架将在其 XML 标记内找到的所有子项传递给特定的子容器。

And then modify the XML as such:然后这样修改 XML:

res/layout/custom_layout.xml资源/布局/custom_layout.xml

<merge>
  <SomeView />
  <SomeOtherView />
  <!-- maybe more layout stuff here later -->
  <LinearLayout
      android:id="@+id/content" />
</merge>

The reason for using <merge> is to simplify the hierarchy.使用<merge>的原因是为了简化层次结构。 All the child views will get attached to your custom class, which is a RelativeLayout .所有子视图都将附加到您的自定义类,即RelativeLayout If you don't use <merge> , you end up with a RelativeLayout attached to another RelativeLayout attached to all the children, which can cause issues.如果您不使用<merge> ,您最终会将一个RelativeLayout附加到另一个附加到所有子项的RelativeLayout ,这可能会导致问题。


Kotlin version:科特林版本:


    private fun expand(view: View) {
        val parentWidth = (view.parent as View).width
        val matchParentMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY)
        val wrapContentMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)

        view.measure(matchParentMeasureSpec, wrapContentMeasureSpec)
        val targetHeight = view.measuredHeight
        view.isVisible = true
        val animation: Animation = getExpandAnimation(view, targetHeight)

        view.startAnimation(animation)
    }

    private fun getExpandAnimation(
        view: View,
        targetHeight: Int
    ): Animation = object : Animation() {
        override fun applyTransformation(
            interpolatedTime: Float,
            transformation: Transformation
        ) {
            view.layoutParams.height =
                if (interpolatedTime == 1f) {
                    LayoutParams.WRAP_CONTENT
                } else {
                    (targetHeight * interpolatedTime).toInt()
                }

            view.requestLayout()
        }

        override fun willChangeBounds(): Boolean {
            return true
        }
    }.apply {
        duration = getDuration(targetHeight, view)
    }

    private fun collapse(view: View) {
        val initialHeight = view.measuredHeight
        val animation: Animation = getCollapseAnimation(view, initialHeight)

        view.startAnimation(animation)
    }

    private fun getCollapseAnimation(
        view: View,
        initialHeight: Int
    ): Animation = object : Animation() {
        override fun applyTransformation(
            interpolatedTime: Float,
            transformation: Transformation
        ) {
            if (interpolatedTime == 1f) {
                view.isVisible = false
            } else {
                view.layoutParams.height =
                    initialHeight - (initialHeight * interpolatedTime).toInt()
                view.requestLayout()
            }
        }

        override fun willChangeBounds(): Boolean = true

    }.apply {
        duration = getDuration(initialHeight, view)
    }

    /**
     * Speed = 1dp/ms
     */
    private fun getDuration(initialHeight: Int, view: View) =
        (initialHeight / view.context.resources.displayMetrics.density).toLong()

you can use include tag 你可以使用include标签

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical">
    <include android:id="@+id/nav_bar_layout" layout="@layout/nav_bar" android:layout_above="@+id/web_view" />
    <WebView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/web_view" android:layout_centerInParent="true" />
    <include android:id="@+id/admob_layout" layout="@layout/admob_layout" android:layout_below="@+id/web_view" />
</RelativeLayout>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM