Invalidate和postInvalidate的區別

       在Android實際開發中,我們常常需要進行View的刷新(例如:自定義控件等),Android中爲我們提供了兩組刷新View的方法—-Invalidate和postInvalidate,前者是在UI線程中更新View,而後者是在非UI線程中更新VIew,兩組方法的具體區別如下介紹:
       我們都知道,在Android中,更新UI的操作要在UI線程(也就是主線程)中進行,因爲Android UI操作並不是線程安全的,但是這些操作必須在UI線程中調用(單線程模式,這裏就不在詳細介紹了,可以自己上網查閱)。

Invalidate()方法:

       對於初學者來說,我們常用的更新UI的方式不外乎就是利用Handler機制,在工作線程中執行耗時操作等,然後發消息給UI線程,進行UI操作。以下我們以自定義控件來介紹該用法(自定義控件,實現一個圓環不停的刷新大小,由小變大):
自定義控件,繪製一個圓環,並更新其大小“`

public class MyView extends View {
    /**
     * 畫筆
     */
    private Paint mPaint ;
    /**
     * 自定義控件半徑
     */
    private int radiu ;

    public MyView(Context context) {
        this(context, null);
    }   
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化畫筆
        initPaint() ;
    }

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 初始化畫筆方法
     */
    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
        mPaint.setStyle(Style.STROKE) ;
        mPaint.setColor(Color.RED) ;
        mPaint.setStrokeWidth(20) ;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 執行繪製
        canvas.drawCircle(150, 150, radiu, mPaint) ;
    }

    public synchronized void setRadiu(int radiu) {
        this.radiu = radiu ;    
        // 刷新,即重新繪製
        invalidate() ;
    }

}

然後在MainActivity中:

public class MainActivity extends Activity {
    /**
     * 自定義控件對象
     */
    private MyView myView ;
    /**
     * 自定義控件的半徑
     */
    private int radiu ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myView = (MyView) findViewById(R.id.myview) ;

        // 開啓線程
        new Thread(new Runnable() {

            @Override
            public void run() {

                try {
                    // 死循環,不停的刷新View
                    while(true) {
                        // 當半徑小於200時,開始自增
                        if(radiu <= 200) {
                            radiu += 10 ;
                            // 發送消息給Handler處理
                              mHandler.obtainMessage().sendToTarget() ;
                        } else {
                            radiu = 0 ;
                        }
                        // 每隔40毫秒睡一次
                        Thread.sleep(40) ;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start() ;

    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {

        public void handleMessage(android.os.Message msg) {
            // 設置自定義控件的半徑
            myView.setRadiu(radiu) ;
        };

    } ;

    protected void onDestroy() {
        super.onDestroy() ;
        // 界面銷燬後移除Handler的引用
        mHandler.removeCallbacksAndMessages(null) ;
    };

}

       以上這種方式即爲在非UI線程中執行邏輯代碼,然後利用Handler機制發送消息通知UI線程進行更新UI的操作。

postInvalidate()方法:

       而postInvalidate()可以直接在非UI線程中刷新,不需要使用Handler機制,代碼邏輯也相對比較簡單:

public class MyView extends View implements Runnable {
    /**
     * 畫筆
     */
    private Paint mPaint ;
    /**
     * 自定義控件半徑
     */
    private int radiu ;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化畫筆
        initPaint() ;
    }

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    /**
     * 初始化畫筆方法
     */
    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
        mPaint.setStyle(Style.STROKE) ;
        mPaint.setColor(Color.RED) ;
        mPaint.setStrokeWidth(20) ;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 執行繪製
        canvas.drawCircle(150, 150, radiu, mPaint) ;
    }

    @Override
    public void run() {

        while(true) {

            try {
                if(radiu <= 150) {
                    radiu += 10 ;

                    postInvalidate() ;
                } else {
                    radiu = 0 ;
                }
                Thread.sleep(40) ;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

}   

在MainActivity中,開啓線程更新View:

public class MainActivity extends Activity {
    /**
     * 自定義控件對象
     */
    private MyView myView ;
    /**
     * 自定義控件的半徑
     */
    private int radiu ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myView = (MyView) findViewById(R.id.myview) ;
        // 開啓線程刷新界面
        new Thread(myView).start() ;

    }

       以上兩種方式,是我在利用自定義控件時使用的,可能對於不熟悉自定義控件的初學者來說看着會有點亂,其實仔細閱讀以下,就會發現,並不是那麼難,第一種方式中,我們選擇利用Handler機制來處理,利用消息機制來通知UI線程更新View操作,所以,調用Invalidate()的方法(即setRadiu())是運行在UI線程的,對於第二種方法,我們將更新View的方法放在了一個子線程中(即Runnable接口的run方法中),然後在調用該自定義控件時開啓一個線程來操作,所以postInvalidate()方法是運行在子線程的。
       我們在學習Android時,要記住一點,代碼千變萬化,萬變不離其宗,代碼稍微一改就會不一樣,但其調用的實質是一樣的。希望這篇博客能給大家一點幫助,若有解釋不合理的地方,希望大家提出寶貴的意見。

發佈了32 篇原創文章 · 獲贊 14 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章