Android Custom FrameLayout и дочернее измерение
Мне нужен ваш совет по пользовательскому компоненту, над которым я работаю. Таким образом, цель этого компонента - изменить его размер и изменить представление при необходимости. Так что сейчас у меня работает компонент:
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, но это кажется правильным…
Примечание 1: возможно, я нашел более простой способ сделать анимацию с помощью:
...animate().scaleX(
(float)nextView.getWidth()/(float)currentView.getWidth()
).scaleY(
(float)nextView.getHeight()/(float)currentView.getHeight()
);
Но я не уверен в разнице? Как насчет моей проблемы измерения, когда я изменяю размер компонента, он переопределяет ширину / высоту дочерних элементов? Я сделал еще один тест, и он не отменяет измерения, которые я выполняю, используя только 3 простых представления как дочерние.
РЕДАКТИРОВАТЬ: Масштаб аним не может работать, очевидно, он также масштабирует всех детей, глупый я.
Примечание 2: Если вы думаете о каком-либо другом способе достижения моей цели, или если вы думаете, что я не иду в правильном направлении, не стесняйтесь высказать мне свое мнение!
Примечание 3: Если вам нужно / нужно еще немного кода, я буду рад показать вам, что все так далеко.