2

我需要您对我正在处理的自定义组件的建议。因此,很快,这个组件的目的就是调整自身大小并在需要时更改视图。所以现在我有一个组件正在工作:

public class SwitchBox extends FrameLayout {

    //Current view being displayed
    private View mCurrentView;
    //Tell if the custom is currently animating or not
    private boolean mAnimating = false;

    public SwitchBox(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //When all child are inflated, we set them all to alpha 0...
        for(int i = 0; i < getChildCount()-1; i ++) {
            getChildAt(i).setAlpha(0f);
            getChildAt(i).setVisibility(INVISIBLE);
            getChildAt(i).setClickable(false);
        }
        //Except for the displayed one which called mCurrentView
        mCurrentView = getChildAt(getChildCount()-1);
    }

    @Override
    public void bringChildToFront(View child) {

        mCurrentView.setClickable(false);
        //Animation set for the height and width animation
        AnimatorSet anim = new AnimatorSet();

        //Setting up width/height animation using nested classes WidthEvaluator and HeightEvaluator
        ValueAnimator scaleWidth = ValueAnimator.ofObject(new WidthEvaluator(this), getWidth(), child.getWidth());
        scaleWidth.setInterpolator(new AccelerateDecelerateInterpolator());
        scaleWidth.setDuration(600);
        ValueAnimator scaleHeight = ValueAnimator.ofObject(new HeightEvaluator(this), getHeight(), child.getHeight());
        scaleHeight.setInterpolator(new AccelerateDecelerateInterpolator());
        scaleHeight.setDuration(600);

        //Starting the alpha animation on the current view
        //and which end lanching the one on the "new" view
        final View view = child;
        final View current = mCurrentView;
        mCurrentView.animate().alpha(0f).setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {}
            @Override
            public void onAnimationEnd(Animator animator) {
                view.setVisibility(VISIBLE);
                view.animate().alpha(1f).start();
                current.setVisibility(INVISIBLE);
                current.animate().setListener(null);
            }
            @Override
            public void onAnimationCancel(Animator animator) {}
            @Override
            public void onAnimationRepeat(Animator animator) {}
        }).start();

        //And then starting the width/height AnimatorSet
        anim.play(scaleWidth).with(scaleHeight);
        mAnimating = true;
        anim.start();

        anim.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {}
            @Override
            public void onAnimationEnd(Animator animator) {
                view.setClickable(true);
                mAnimating = false;
            }
            @Override
            public void onAnimationCancel(Animator animator) {}
            @Override
            public void onAnimationRepeat(Animator animator) {}
        });


        //Finally we change the current view for the new one and bring it to front
        mCurrentView = child;
        super.bringChildToFront(child);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //To avoid the childs' height and width changing permanently 
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY);
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        //While animating we let android do its job
        if(!mAnimating) {
            //...then when is done we measure the view with the current displayed one
            //Mostly (entirely ?) for the initialisation of the component
            //but I coud not find another way to tell when all measurement was done  
            //Yet I tried onLayout/GlobalLayoutListener/... I think overriding onDraw was working well though 
            setMeasuredDimension(mCurrentView.getMeasuredWidth(), mCurrentView.getMeasuredHeight());
        }
    }

    //Class which allows width animation through ValueAnimator
    private class WidthEvaluator extends IntEvaluator {

        private View v;
        public WidthEvaluator(View v) {
            this.v = v;
        }

        @Override
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            Integer num = super.evaluate(fraction, startValue, endValue);
            ViewGroup.LayoutParams params = v.getLayoutParams();
            params.width = num;
            v.requestLayout();
            return num;
        }
    }
    //Class which allows height animation through ValueAnimator
    private class HeightEvaluator extends IntEvaluator {

        private View v;
        public HeightEvaluator(View v) {
            this.v = v;
        }

        @Override
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            Integer num = super.evaluate(fraction, startValue, endValue);
            ViewGroup.LayoutParams params = v.getLayoutParams();
            params.height = num;
            v.requestLayout();
            return num;
        }
    }
}

只要将子项设置为特定大小或 WRAP_CONTENT,它就可以正常工作。现在,我想做一个孩子里面的 FILL_PARENT,减去一个边距。我该怎么做?我在想也许可以找到一种方法来知道孩子的测量值何时设置为 FILL_PARENT 并给他 MAX_WIDTH/MAX_HEIGHT 尺寸,但看起来确实是正确的……</p>

注意 1: 我可能已经找到了一种更简单的方法来制作动画:

...animate().scaleX(
     (float)nextView.getWidth()/(float)currentView.getWidth()
   ).scaleY(
     (float)nextView.getHeight()/(float)currentView.getHeight()
   );

但我不确定有什么区别?我的测量问题是什么,即当我调整组件大小时,它会覆盖子组件的宽度/高度?我做了一些更多的测试,它不会覆盖我只用 3 个简单的View as children运行的测量

编辑:缩放动画显然不能工作,它也缩放所有孩子,我很傻。

注意2: 如果您认为任何其他方式可以实现我的目标,或者您认为我没有朝着正确的方向前进,请不要犹豫,给我您的意见!

注意 3: 如果您想要/需要更多代码,我很乐意向您展示到目前为止。

4

0 回答 0