自定义按钮背景被绘制错误(有时)

时间:2013-05-13 15:56:12

标签: android ondraw onmeasure

我有这个自定义按钮,它不会绘制自己并且没有子视图。应用程序启动后,它看起来像这样:

enter image description here

此时我不知道在此处发布可能与哪些代码和详细信息相关。事实是,在应用更改状态后,按钮会检查是保留VISIBLE还是INVISIBLE。它仍然是VISIBLE。它调用setVisibility(View.VISIBLE),之后,当屏幕再次显示时,它看起来像这样:

enter image description here

如果我点击按钮,它会返回原来的背景尺寸。

到目前为止我做了什么
我已经将代码调试到了android源码 首先 onDraw();我只在那里调用super.onDraw();,它似乎只处理文本而不是背景,如果是这样,它可以正常工作,因为文本仍然像以前一样定位和标注尺寸。
第二 onMeasure();我也只打电话给super.onMeasure();;它在第一次显示之前被称为几(11)次,在setVisibility()之后被称为5次;单击按钮时根本不会调用它 单击按钮时调用第三 onTouchEvent()。它为ACTION_DOWN设置了不同的颜色背景,并在ACTION_UP

上恢复原始颜色背景
@Override
protected void onDraw(Canvas canvas) {
    Log.d(TAG + " " + getText(), "+ onDraw(canvas:" + canvas + ")");
    super.onDraw(canvas);
    Log.d(TAG + " " + getText(), "- onDraw()");
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    Log.d(TAG + " " + getText(), String.format("+ onMeasure(widthMeasureSpec:%x, heightMeasureSpec:%x)", widthMeasureSpec,heightMeasureSpec));
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    Log.d(TAG + " " + getText(), String.format("- onMeasure(): width=%d, hieght=%d", getMeasuredWidth(), getMeasuredHeight()));
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d(TAG + " " + getText(), "+ onTouchEvent(event:" + event + ")");
    super.onTouchEvent(event);

    if( clickable ) {
        if( event.getAction() == MotionEvent.ACTION_UP ) {
            setBackgroundDrawable(normalBackground);
            clickUp.soundPlay();
        } else if( event.getAction() == MotionEvent.ACTION_DOWN ) {
            setBackgroundDrawable(pressedBackground);
            clickDown.soundPlay();
        }
    }

    Log.d(TAG + " " + getText(), "- onTouchEvent()");
    return true;
}

/**
 * Sets the MyButton visible if stateFlags matches.<br>
 * @param stateFlags The current app state.<br> 
 */
public void setState(int stateFlags) {
    Log.d(TAG + " " + getText(), "+ setState(stateFlags:" + stateFlags + ")");
    if( state == stateFlags || state == State.NORMAL) {
        setVisibility(View.VISIBLE);
        Log.d(TAG + " " + getText(), "state(" + state + ") VISIBLE before was " + getVisibility());
    } else {
        setVisibility(View.INVISIBLE);
        Log.d(TAG + " " + getText(), "state(" + state + ") INVISIBLE before was " + getVisibility());
    }
    requestLayout();
    Log.d(TAG + " " + getText(), "- setState()");
}

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    Log.d(TAG + " " + getText(), "+ onCreateDrawableState(extraSpace:" + extraSpace + ")");
    Log.d(TAG + " " + getText(), "- onCreateDrawableState()");
    return super.onCreateDrawableState(extraSpace);
}

这些是按钮的日志:

*** Beggining - first show ***
+ onMeasure(widthMeasureSpec:800002b8, heightMeasureSpec:800003aa)
- onMeasure(): width=200, hieght=125
+ onMeasure(widthMeasureSpec:40000099, heightMeasureSpec:800003aa)
- onMeasure(): width=153, hieght=125
+ onMeasure(widthMeasureSpec:800002b8, heightMeasureSpec:80000152)
- onMeasure(): width=200, hieght=125
+ onMeasure(widthMeasureSpec:40000099, heightMeasureSpec:80000152)
- onMeasure(): width=153, hieght=125
+ onMeasure(widthMeasureSpec:800002b8, heightMeasureSpec:80000035)
- onMeasure(): width=200, hieght=53
+ onMeasure(widthMeasureSpec:40000099, heightMeasureSpec:80000035)
- onMeasure(): width=153, hieght=53
+ onSizeChanged(w:153, h:53, oldw:0, oldh:0)
- onSizeChanged()
+ onLayout(changed:true, left:12, top:3, right:165, bottom:56)
- onLayout()
+ onMeasure(widthMeasureSpec:800002b8, heightMeasureSpec:80000035)
- onMeasure(): width=200, hieght=53
+ onMeasure(widthMeasureSpec:40000099, heightMeasureSpec:80000035)
- onMeasure(): width=153, hieght=53
+ onLayout(changed:false, left:12, top:3, right:165, bottom:56)
- onLayout()
+ onDraw(canvas:android.view.Surface$CompatibleCanvas@b3e434b0)
+ onCreateDrawableState(extraSpace:0)
- onCreateDrawableState()
- onDraw()

*** App changes state - button shows wrong ****
+ setState(stateFlags:2)
state(1) VISIBLE before was 0
- setState()
+ onMeasure(widthMeasureSpec:800002b8, heightMeasureSpec:80000152)
- onMeasure(): width=200, hieght=125
+ onMeasure(widthMeasureSpec:800002b8, heightMeasureSpec:80000035)
- onMeasure(): width=200, hieght=53
+ onMeasure(widthMeasureSpec:40000099, heightMeasureSpec:80000035)
- onMeasure(): width=153, hieght=53
+ onLayout(changed:false, left:12, top:3, right:165, bottom:56)
- onLayout()
+ onDraw(canvas:android.view.Surface$CompatibleCanvas@b3e434b0)
- onDraw()

*** I am about to click the button, return to show fine ***
+ onTouchEvent(event:MotionEvent { action=ACTION_DOWN, id[0]=0, x[0]=61.0, y[0]=36.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=2879242, downTime=2879242, deviceId=0, source=0x1002 })
- onTouchEvent()
+ onDraw(canvas:android.view.Surface$CompatibleCanvas@b3e434b0)
+ onCreateDrawableState(extraSpace:0)
- onCreateDrawableState()
- onDraw()
+ onTouchEvent(event:MotionEvent { action=ACTION_UP, id[0]=0, x[0]=61.0, y[0]=36.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=2879342, downTime=2879242, deviceId=0, source=0x1002 })
- onTouchEvent()
+ performClick()
- performClick()
+ onDraw(canvas:android.view.Surface$CompatibleCanvas@b3e434b0)
+ onCreateDrawableState(extraSpace:0)
- onCreateDrawableState()
- onDraw()

如果认为相关,将根据需要发布更多详细信息。

1 个答案:

答案 0 :(得分:0)

我找到了一个绕过!,但我非常想了解真正的问题。

旁路是:

@Override
protected void onDraw(Canvas canvas) {
    // This is a bypass for the problem of partial background redraw.
    // The problem causes are not understood yet.
    // But setting mBackgroundSizeChanged = true; in View causes the next Draw to be OK
    onScrollChanged(0, 0, 0, 0);
    super.onDraw(canvas);
}

最终旁路线是(View.class):

mBackgroundSizeChanged = true;

但是mBackgroundSizeChanged无法从派生类访问,并且它本身没有setter。所以我找到了最接近定位器的东西:onScrollChanged();它设置了mBackgroundSizeChanged = true,在我的情况下就是它所做的一切。检查TextView.class和View.class中的几个源代码行,看看在你的情况下是否还有其他东西。

Old Bypass(仍然有效但执行更多不必要的行):

在我更改按钮的可见性后,我添加了这些行:

Drawable d = cb.getBackground();
cb.setBackgroundDrawable(null);
cb.setBackgroundDrawable(d);

它解决了问题,现在按钮保留了它的背景。

解释我是如何到达那里的
这会强制按钮背景自行重置。我确实查看了setBackgroundDrawable()的来源,试图了解哪些代码有所作为。我首先注意到必须先将背景设置为null,然后重置它以强制重做 进一步调试将差异缩小为:

mBackgroundSizeChanged = true; 

setBackgroundDrawable()

的持续代码行中