我有一个Sliding Fragments DevByte的实现。除了将片段滑入视图之外,我还想在它被遮挡的内容上绘制阴影。我已经修改了FractionalLinearLayout
从视频到measure
自身的两倍屏幕宽度layout
及其右半部分的子项。在动画循环期间,我在左半边绘制了一个增加 alpha 的黑色矩形,并将我的 X 坐标设置为负值以显示右半边。
我的问题是,当我将内容滑入视图时它工作正常,但当我将内容滑出视图时失败。在进入的过程中,我得到了我想要的行为:翻译和变暗的阴影。在输出的过程中,我只得到了平移,并且阴影在整个动画过程中一直保持它的第一帧颜色。
我在日志中看到的是,在进入的过程中,setPercentOnScreen()
和onDraw()
方法交替调用,正如预期的那样。然而,在出去的路上,我接到一个电话到 ,然后setPercentageOnScreen()
接到一个电话到onDraw()
,然后只接到一个电话到setPercentOnScreen()
。Android 正在优化绘图,但我不知道为什么。
更新:有趣的是我只在运行 Android 4.4 的模拟器上看到这种行为。运行 4.0.3 和 4.3 的模拟器按预期在两个方向上运行动画。旧的 Nexus 7 会出现问题,而其他模拟器 4.4 则不会。它在设备上似乎是一致的,但在设备之间会有所不同。
再次更新:我提取了一个示例项目并将其放在 GitHub 上:barend/android-slidingfragment。GitHub 上的自述文件包含十几个设备上的测试结果。对于模拟器,问题与“启用主机 GPU”功能相关,但仅限于 Jelly Bean 和 KitKat;不在 ICS 上。
再次更新:进一步的测试表明,问题出现在运行 Jelly Bean 或更高版本的物理设备上,以及启用“使用主机 GPU”的运行 Jelly Bean 或更高版本的 ARM 模拟器上。无论 Android 版本如何,它都不会出现在 x86 模拟器和没有“使用主机 GPU”的 ARM 模拟器上。我的测试的确切表格可以在上面链接的 github 项目中找到。
// Imports left out
public class HorizontalSlidingLayout extends FrameLayout {
/**
* The fraction by which the content has slid into view. Legal range: from 0.0 (all content
* off-screen) to 1.0 (all content visible).
*/
private float percentOnScreen;
private int screenWidth, screenHeight, shift;
private Paint shadowPaint;
// Constructors left out, all three call super, then init().
private void init() {
if (isInEditMode()) {
// Ensure content is visible in edit mode.
percentOnScreen = 1.0f;
} else {
setWillNotDraw(false);
percentOnScreen = 0.0f;
shadowPaint = new Paint();
shadowPaint.setAlpha(0x00);
shadowPaint.setColor(0x000000);
shadowPaint.setStyle(Paint.Style.FILL);
}
}
/** Reports our own size as (2w, h) and measures all children at (w, h). */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
screenWidth = MeasureSpec.getSize(widthMeasureSpec);
screenHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(2 * screenWidth, screenHeight);
for (int i = 0, max = getChildCount(); i < max; i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, heightMeasureSpec);
}
}
/** Lays out the children in the right half of the view. */
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
for (int i = 0, max = getChildCount(); i < max; i++) {
View child = getChildAt(i);
child.layout(screenWidth, top, right, bottom);
}
}
/**
* Draws a translucent shadow in the left half of the view, darkening by
* {@code percentOnScreen}, then lets superclass draw children in the right half.
*/
@Override
protected void onDraw(Canvas canvas) {
// Maintain 30% translucency
if (percentOnScreen < 0.7f) {
shadowPaint.setAlpha((int) (percentOnScreen * 0xFF));
}
android.util.Log.i("Slider", "onDraw(" + percentOnScreen + ") -> alpha(" + shadowPaint.getAlpha() + ')');
canvas.drawRect(shift, 0, screenWidth, screenHeight, shadowPaint);
super.onDraw(canvas);
}
@SuppressWarnings("unused")
public float getPercentOnScreen() {
return percentOnScreen;
}
/** Repeatedly invoked by an Animator. */
@SuppressWarnings("unused")
public void setPercentOnScreen(float fraction) {
this.percentOnScreen = fraction;
shift = (int)(fraction < 1.0 ? fraction * screenWidth : screenWidth);
setX(-shift);
android.util.Log.i("Slider", "setPOS(" + fraction + ") -> invalidate(" + shift + ',' + screenWidth + ')');
invalidate(shift, 0, screenWidth, screenHeight);
//invalidate() // Makes no difference
}
}
奇怪的是,这违反了对称性。我在滑出时和滑入时反向做完全相同的事情,但行为不同。我可能忽略了一些愚蠢的事情。有任何想法吗?