代码来源: [Android源码]DeskClock/com.android.deskclock.AlarmClockFragment
源码版本: 4.4.2
/**
* Expands the alarm for editing.
*
* @param itemHolder The item holder instance.
*/
private void expandAlarm(final ItemHolder itemHolder, boolean animate) {
mExpanded.add(itemHolder.alarm.id);
bindExpandArea(itemHolder, itemHolder.alarm);
// Scroll the view to make sure it is fully viewed
mScrollAlarmId = itemHolder.alarm.id;
// Save the starting height so we can animate from this value.
final int startingHeight = itemHolder.alarmItem.getHeight();
// Set the expand area to visible so we can measure the height to animate to.
itemHolder.alarmItem.setBackgroundColor(mBackgroundColorExpanded);
itemHolder.expandArea.setVisibility(View.VISIBLE);
if (!animate) {
// Set the "end" layout and don't do the animation.
itemHolder.arrow.setRotation(180);
// We need to translate the hairline up, so the height of the collapseArea
// needs to be measured to know how high to translate it.
final ViewTreeObserver observer = mAlarmsList.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// We don't want to continue getting called for every listview drawing.
if (observer.isAlive()) {
observer.removeOnPreDrawListener(this);
}
int hairlineHeight = itemHolder.hairLine.getHeight();
int collapseHeight =
itemHolder.collapseExpandArea.getHeight() - hairlineHeight;
itemHolder.hairLine.setTranslationY(-collapseHeight);
return true;
}
});
return;
}
// Add an onPreDrawListener, which gets called after measurement but before the draw.
// This way we can check the height we need to animate to before any drawing.
// Note the series of events:
// * expandArea is set to VISIBLE, which causes a layout pass
// * the view is measured, and our onPreDrawListener is called
// * we set up the animation using the start and end values.
// * the height is set back to the starting point so it can be animated down.
// * request another layout pass.
// * return false so that onDraw() is not called for the single frame before
// the animations have started.
final ViewTreeObserver observer = mAlarmsList.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// We don't want to continue getting called for every listview drawing.
if (observer.isAlive()) {
observer.removeOnPreDrawListener(this);
}
// Calculate some values to help with the animation.
final int endingHeight = itemHolder.alarmItem.getHeight();
final int distance = endingHeight - startingHeight;
final int collapseHeight = itemHolder.collapseExpandArea.getHeight();
int hairlineHeight = itemHolder.hairLine.getHeight();
final int hairlineDistance = collapseHeight - hairlineHeight;
// Set the height back to the start state of the animation.
itemHolder.alarmItem.getLayoutParams().height = startingHeight;
// To allow the expandArea to glide in with the expansion animation, set a
// negative top margin, which will animate down to a margin of 0 as the height
// is increased.
// Note that we need to maintain the bottom margin as a fixed value (instead of
// just using a listview, to allow for a flatter hierarchy) to fit the bottom
// bar underneath.
FrameLayout.LayoutParams expandParams = (FrameLayout.LayoutParams)
itemHolder.expandArea.getLayoutParams();
expandParams.setMargins(0, -distance, 0, collapseHeight);
itemHolder.alarmItem.requestLayout();
// Set up the animator to animate the expansion.
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f)
.setDuration(EXPAND_DURATION);
animator.setInterpolator(mExpandInterpolator);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
Float value = (Float) animator.getAnimatedValue();
// For each value from 0 to 1, animate the various parts of the layout.
itemHolder.alarmItem.getLayoutParams().height =
(int) (value * distance + startingHeight);
FrameLayout.LayoutParams expandParams = (FrameLayout.LayoutParams)
itemHolder.expandArea.getLayoutParams();
expandParams.setMargins(
0, (int) -((1 - value) * distance), 0, collapseHeight);
itemHolder.arrow.setRotation(180 * value);
itemHolder.hairLine.setTranslationY(-hairlineDistance * value);
itemHolder.summary.setAlpha(1 - value);
itemHolder.alarmItem.requestLayout();
}
});
// Set everything to their final values when the animation's done.
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
// Set it back to wrap content since we'd explicitly set the height.
itemHolder.alarmItem.getLayoutParams().height =
LayoutParams.WRAP_CONTENT;
itemHolder.arrow.setRotation(180);
itemHolder.hairLine.setTranslationY(-hairlineDistance);
itemHolder.summary.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO we may have to deal with cancelations of the animation.
}
@Override
public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationStart(Animator animation) { }
});
animator.start();
// Return false so this draw does not occur to prevent the final frame from
// being drawn for the single frame before the animations start.
return false;
}
});
}
源码版本: 4.4.2
/**
* Expands the alarm for editing.
*
* @param itemHolder The item holder instance.
*/
private void expandAlarm(final ItemHolder itemHolder, boolean animate) {
mExpanded.add(itemHolder.alarm.id);
bindExpandArea(itemHolder, itemHolder.alarm);
// Scroll the view to make sure it is fully viewed
mScrollAlarmId = itemHolder.alarm.id;
// Save the starting height so we can animate from this value.
final int startingHeight = itemHolder.alarmItem.getHeight();
// Set the expand area to visible so we can measure the height to animate to.
itemHolder.alarmItem.setBackgroundColor(mBackgroundColorExpanded);
itemHolder.expandArea.setVisibility(View.VISIBLE);
if (!animate) {
// Set the "end" layout and don't do the animation.
itemHolder.arrow.setRotation(180);
// We need to translate the hairline up, so the height of the collapseArea
// needs to be measured to know how high to translate it.
final ViewTreeObserver observer = mAlarmsList.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// We don't want to continue getting called for every listview drawing.
if (observer.isAlive()) {
observer.removeOnPreDrawListener(this);
}
int hairlineHeight = itemHolder.hairLine.getHeight();
int collapseHeight =
itemHolder.collapseExpandArea.getHeight() - hairlineHeight;
itemHolder.hairLine.setTranslationY(-collapseHeight);
return true;
}
});
return;
}
// Add an onPreDrawListener, which gets called after measurement but before the draw.
// This way we can check the height we need to animate to before any drawing.
// Note the series of events:
// * expandArea is set to VISIBLE, which causes a layout pass
// * the view is measured, and our onPreDrawListener is called
// * we set up the animation using the start and end values.
// * the height is set back to the starting point so it can be animated down.
// * request another layout pass.
// * return false so that onDraw() is not called for the single frame before
// the animations have started.
final ViewTreeObserver observer = mAlarmsList.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// We don't want to continue getting called for every listview drawing.
if (observer.isAlive()) {
observer.removeOnPreDrawListener(this);
}
// Calculate some values to help with the animation.
final int endingHeight = itemHolder.alarmItem.getHeight();
final int distance = endingHeight - startingHeight;
final int collapseHeight = itemHolder.collapseExpandArea.getHeight();
int hairlineHeight = itemHolder.hairLine.getHeight();
final int hairlineDistance = collapseHeight - hairlineHeight;
// Set the height back to the start state of the animation.
itemHolder.alarmItem.getLayoutParams().height = startingHeight;
// To allow the expandArea to glide in with the expansion animation, set a
// negative top margin, which will animate down to a margin of 0 as the height
// is increased.
// Note that we need to maintain the bottom margin as a fixed value (instead of
// just using a listview, to allow for a flatter hierarchy) to fit the bottom
// bar underneath.
FrameLayout.LayoutParams expandParams = (FrameLayout.LayoutParams)
itemHolder.expandArea.getLayoutParams();
expandParams.setMargins(0, -distance, 0, collapseHeight);
itemHolder.alarmItem.requestLayout();
// Set up the animator to animate the expansion.
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f)
.setDuration(EXPAND_DURATION);
animator.setInterpolator(mExpandInterpolator);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
Float value = (Float) animator.getAnimatedValue();
// For each value from 0 to 1, animate the various parts of the layout.
itemHolder.alarmItem.getLayoutParams().height =
(int) (value * distance + startingHeight);
FrameLayout.LayoutParams expandParams = (FrameLayout.LayoutParams)
itemHolder.expandArea.getLayoutParams();
expandParams.setMargins(
0, (int) -((1 - value) * distance), 0, collapseHeight);
itemHolder.arrow.setRotation(180 * value);
itemHolder.hairLine.setTranslationY(-hairlineDistance * value);
itemHolder.summary.setAlpha(1 - value);
itemHolder.alarmItem.requestLayout();
}
});
// Set everything to their final values when the animation's done.
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
// Set it back to wrap content since we'd explicitly set the height.
itemHolder.alarmItem.getLayoutParams().height =
LayoutParams.WRAP_CONTENT;
itemHolder.arrow.setRotation(180);
itemHolder.hairLine.setTranslationY(-hairlineDistance);
itemHolder.summary.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO we may have to deal with cancelations of the animation.
}
@Override
public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationStart(Animator animation) { }
});
animator.start();
// Return false so this draw does not occur to prevent the final frame from
// being drawn for the single frame before the animations start.
return false;
}
});
}