首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Android:多视图子视图,用于现有布局的自定义视图

Android:多视图子视图,用于现有布局的自定义视图
EN

Stack Overflow用户
提问于 2012-06-26 06:02:42
回答 1查看 14.9K关注 0票数 34

我必须在Android中构建一个更复杂的自定义视图。最终的布局应该如下所示:

代码语言:javascript
复制
<RelativeLayout>
  <SomeView />
  <SomeOtherView />
  <!-- maybe more layout stuff here later -->
  <LinearLayout>
    <!-- the children -->
  </LinearLayout>
</RelativeLayout>

但是,我只想在XML文件中定义这个(不定义SomeView、SomeOtherView等):

代码语言:javascript
复制
<MyCustomView>
  <!-- the children -->
</MyCustomView>

这在Android中是可能的吗?如果可能:最干净的方式是什么?我想到的可能的解决方案是“覆盖addView()方法”和“删除所有视图,稍后再添加它们”,但我不确定该走哪条路……

非常感谢您的帮助!:)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-06-26 06:22:30

创建自定义容器视图是完全可能的,也是鼓励的。这就是Android所说的复合控件。所以:

代码语言:javascript
复制
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);
        }
    }
}

您可以根据需要覆盖任意多个版本的addView(),但最终它们都会回调到我放在示例中的版本。仅覆盖此方法将使框架将其XML标记中找到的所有子对象传递给特定的子容器。

然后按如下方式修改XML:

res/layout/custom_layout.xml

代码语言:javascript
复制
<merge>
  <SomeView />
  <SomeOtherView />
  <!-- maybe more layout stuff here later -->
  <LinearLayout
      android:id="@+id/content" />
</merge>

使用<merge>的原因是为了简化层次结构。所有的子视图都将附加到您的自定义类,这是一个RelativeLayout。如果您不使用<merge>,您最终会得到一个连接到另一个RelativeLayoutRelativeLayout,该RelativeLayout连接到所有子进程,这可能会导致问题。

Kotlin版本:

代码语言:javascript
复制
    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()
票数 64
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11198026

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档