关键就是实现平滑地改变上层 chlid drawable 的 inset。
平滑,又是 Scroller 那一套,重画后要求重画自己。
今天从书上看到,重画间隔是 16 毫秒。
TransationDrawable 是改变透明度,Scroller 是改变 mScrollX,这里是要改变 inset。
本质是一样的,利用重画间隔,不断重画,达到平滑的效果。
public class BottomInsetTransitionDrawable extends LayerDrawable {
private double bottomHeight;
public BottomInsetTransitionDrawable(Drawable[] layers, int bottomHeight) {
super(layers);
this.bottomHeight = bottomHeight;
mBottomInset = bottomHeight;
}
//
private static final int TRANSITION_STARTING = 0;
private static final int TRANSITION_RUNNING = 1;
private static final int TRANSITION_NONE = 2;
private int mTransitionState = TRANSITION_NONE;
private long mStartTimeMillis;
private int mDuration;
// 实际底部内边距
private double mBottomInset;
public void startTransition(int durationMillis) {
mDuration = durationMillis;
mTransitionState = TRANSITION_STARTING;
invalidateSelf();
}
public void resetTransition() {
mBottomInset = bottomHeight;
mTransitionState = TRANSITION_NONE;
invalidateSelf();
}
//
@Override
public void draw(Canvas canvas) {
setLayerInset(1, 0, 0, 0, (int) mBottomInset);
// 改变 child 的 bounds
onBoundsChange(getBounds());
// 重绘所有 child drawable
super.draw(canvas);
Log.e("重画", mBottomInset + " " + mTransitionState);
switch (mTransitionState) {
case TRANSITION_STARTING:
mStartTimeMillis = SystemClock.uptimeMillis();
mTransitionState = TRANSITION_RUNNING;
break;
case TRANSITION_RUNNING:
if (mStartTimeMillis >= 0) {
float normalized = (float) (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration;
normalized = Math.min(normalized, 1.0f);
// 根据时间流逝,改变底部内边距
mBottomInset = (1 - normalized) * mBottomInset;
}
break;
}
if (mTransitionState == TRANSITION_RUNNING && mBottomInset != 0) {
// 重画后要求下一次重画,时间间隔 16ms
invalidateSelf();
}
}
//
@Override
public boolean isStateful() {
return true;
}
@Override
protected boolean onStateChange(int[] state) {
if (isStatePressed(state)) {
// 本来是设置 1000 的
// 但时间上 0.3 秒就到底部了,剩余的 0.7 在 0.01 到 0 这个层次变化。
// 所以才会发明差值器这种东西把。
startTransition(300);
} else {
resetTransition();
}
return true;
}
private boolean isStatePressed(int[] states) {
for (int s : states) {
if (s == android.R.attr.state_pressed) {
return true;
}
}
return false;
}
}
public class FlatButton extends TextView {
public FlatButton(Context context) {
super(context);
}
public FlatButton(Context context, AttributeSet attrs) {
super(context, attrs);
int cornerRadius = dp2px(context, 6);
ColorDrawable originalBackground = (ColorDrawable) getBackground();
if (originalBackground != null) {
GradientDrawable unpressed = new GradientDrawable();
unpressed.setCornerRadius(cornerRadius);
int bgColor = originalBackground.getColor();
unpressed.setColor(bgColor);
GradientDrawable pressed = new GradientDrawable();
pressed.setCornerRadius(cornerRadius);
int bgColorPressed = colorBurn(bgColor);
pressed.setColor(bgColorPressed);
Drawable[] d = new Drawable[]{pressed, unpressed};
BottomInsetTransitionDrawable background = new BottomInsetTransitionDrawable(d, 12);
setBackground(background);
setTextColor(Color.WHITE);
setClickable(true);
}
}
//
protected int dp2px(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
/**
* 颜色加深处理
*
* @param RGBValues RGB的值,由alpha(透明度)、red(红)、green(绿)、blue(蓝)构成,
* Android中我们一般使用它的16进制,
* 例如:"#FFAABBCC",最左边到最右每两个字母就是代表alpha(透明度)、
* red(红)、green(绿)、blue(蓝)。每种颜色值占一个字节(8位),值域0~255
* 所以下面使用移位的方法可以得到每种颜色的值,然后每种颜色值减小一下,在合成RGB颜色,颜色就会看起来深一些了
* @return
*/
private int colorBurn(int RGBValues) {
//int alpha = RGBValues >> 24;
int red = RGBValues >> 16 & 0xFF;
int green = RGBValues >> 8 & 0xFF;
int blue = RGBValues & 0xFF;
red = (int) Math.floor(red * (1 - 0.2));
green = (int) Math.floor(green * (1 - 0.2));
blue = (int) Math.floor(blue * (1 - 0.2));
return Color.rgb(red, green, blue);
}
}