記錄一次Android自定義View的難忘教訓

一個倒計時控件:CountView extend JustifyTextView extend ImageView,CountView倒計時觸發後,調用JustifyTextView中的方法setText(String text);使用invalidate(),進行重繪,而不是使用TextView setText(),調用requestLayout進行重繪,原因是:requestLayout()方法會一層一層調用父佈局的:requestLayout()(會導致父佈局重新進行測量、佈局和繪製流程,這樣會影響某些View不能正常顯示:例如滑動顯示菜單,佈局變動時會自動關閉菜單顯示,這不是我想要的結果),而invalidate()只針對當前變動的View進行重繪

主要問題:第一個方法會不斷調用,第一次調用後會調用onDraw方法,但是UI上沒顯示畫的結果,並且隨後再也不調用onDraw方法

//JustifyTextView.java 一秒調用一次
public void setText(String text) {
        this.text = text;
        invalidate();
    }

//正常情況下每次調用invalidate();會調用下面的方法
@Override
protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (paint == null){
            initPaint();
        }
        Paint.FontMetrics fm = paint.getFontMetrics();
        float baseline = fm.descent - fm.ascent;

        canvas.drawText(text, desiredWidth/2, baseline, paint);
}

分析問題可能產生的原因:

  • onDraw沒調用是因爲 invalidate(),調用不對?換做下面幾個方法都不行,看來不是這個問題
requestLayout();
((View)getParent()).invalidate();
 forceLayout();
  • 嘗試在子View(CountView)中進行繪製,仍然只繪製一次
  • 對ImageView設置背景,仍然無果
  • 對View繪製的起始座標進行多次設置嘗試,仍然不行
canvas.drawText(text, x, y, paint);
  • 改變xml佈局中控件的wrap_content爲指定寬高,終於顯示出來
android:layout_width="wrap_content"
android:layout_height="wrap_content"
# 改爲
android:layout_width="100dp"
android:layout_height="100dp"

最後

  • 通過重新實現onMeasure方法(xml中仍然採用wrap_content),動態設置View的寬高,達到正常顯示的效果
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width;
        int height;

        //Measure Width
        if (widthMode == MeasureSpec.EXACTLY) {
            //Must be this size
            width = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            //Can't be bigger than...
            width = Math.min(desiredWidth, widthSize);
        } else {
            //Be whatever you want
            width = desiredWidth;
        }

        //Measure Height
        if (heightMode == MeasureSpec.EXACTLY) {
            //Must be this size
            height = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            //Can't be bigger than...
            height = Math.min(desiredHeight, heightSize);
        } else {
            //Be whatever you want
            height = desiredHeight;
        }

        //MUST CALL THIS
        setMeasuredDimension(width, height);
    }
感悟:View繪製異常時,後面不會再調用:onDraw;沒能靜下心來,抓問題的主要矛盾,分析產生問題的主要原因,導致做了許多無用的嘗試,浪費了將近一天的時間!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章