Android: Multiple view children for custom view with existing layout

前端 未结 1 675
小蘑菇
小蘑菇 2021-01-31 17:16

I have to build a more complex custom view in Android. The final layout should look like this:


  
  

        
相关标签:
1条回答
  • 2021-01-31 17:30

    It's absolutely possible, and encouraged, to create custom container views. This is what Android would call a compound control. 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. Overriding just this method will have the framework pass all children found inside its XML tag to a specific child container.

    And then modify the XML as such:

    res/layout/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. All the child views will get attached to your custom class, which is a 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.


    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()
    
    0 讨论(0)
提交回复
热议问题