1. 程式人生 > >invalidate和requestLayout

invalidate和requestLayout

Invalidate:
To farce a view to draw,call invalidate().——摘自View類原始碼
從上面這句話看出,invalidate方法會執行draw過程,重繪View樹。
當View的appearance發生改變,比如狀態改變(enable,focus),背景改變,隱顯改變等,這些都屬於appearance範疇,都會引起invalidate操作。

所以當我們改變了View的appearance,需要更新介面顯示,就可以直接呼叫invalidate方法。

View(非容器類)呼叫invalidate方法只會重繪自身,ViewGroup呼叫則會重繪整個View樹。

 

RequestLayout:
To initiate a layout, call requestLayout(). This method is typically called by a view on itself when it believes that it can no longer fit within its current bounds.——摘自View原始碼

從上面這句話看出,當View的邊界,也可以理解為View的寬高,發生了變化,不再適合現在的區域,可以呼叫requestLayout方法重新對View佈局。

View執行requestLayout方法,會向上遞迴到頂級父View中,再執行這個頂級父View的requestLayout,所以其他View的onMeasure,onLayout也可能會被呼叫。

 

總結:

View繪製分三個步驟,順序是:onMeasure,onLayout,onDraw。經程式碼親測,log輸出顯示:呼叫invalidate方法只會執行onDraw方法;呼叫requestLayout方法只會執行onMeasure方法和onLayout方法,並不會執行onDraw方法。

所以當我們進行View更新時,若僅View的顯示內容發生改變且新顯示內容不影響View的大小、位置,則只需呼叫invalidate方法;若View寬高、位置發生改變且顯示內容不變,只需呼叫requestLayout方法;若兩者均發生改變,則需呼叫兩者,按照View的繪製流程,推薦先呼叫requestLayout方法再呼叫invalidate方法。

 

requestLayout示例:實現可移動元件:https://blog.csdn.net/qq_39658819/article/details/78994308

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
 
/**
 * 步驟一:自定義可移動元件
 * @author NewBies
 * @date 2017/12/26
 */
public class VertexView extends android.support.v7.widget.AppCompatTextView{
 
    private int startX;
    private int startY;
    private int endX;
    private int endY;
    private FrameLayout.LayoutParams layoutParams;
 
    public VertexView(Context context) {
        super(context);
    }
 
    public VertexView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    public VertexView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 
 
    /**
     * 步驟二:重寫onTouchEvent事件
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event){
        //步驟三:獲取手機觸控點的橫座標和縱座標
        endX = (int)event.getX();
        endY = (int)event.getY();
 
        //步驟四:獲取佈局引數例項,注意:xx.LayoutParams這裡的xx應該是該元件的父佈局型別
        //注意:這句話必須在該元件已經新增到父佈局中才會起作用,所以這句話我沒有寫在建構函式中,而是寫在這裡
        layoutParams = (FrameLayout.LayoutParams) this.getLayoutParams();
 
        switch (event.getAction()){
            //監聽按下去的事件,這個事件在每次拖動時,必定會執行,也只執行一次
            case MotionEvent.ACTION_DOWN:
                //將按下去的點記錄為起始點
                startX = endX;
                startY = endY;
                break;
            //步驟五:監聽移動事件,該事件會在拖動時執行N次
            case MotionEvent.ACTION_MOVE:
                //計算移動的距離
                int offsetX = endX - startX;
                int offsetY = endY - startY;
 
                //呼叫layout方法來重新放置它的位置
                layoutParams.setMargins(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
                //重新整理
                requestLayout();
                break;
            //監聽擡起事件,該事件同按下去的時間一樣,只執行一次
            case MotionEvent.ACTION_UP:
                break;
            default:break;
        }
        //這裡應該返回true,這裡涉及到了android的事件攔截機制,大致意思是,我的事件是在哪裡處理,就在那裡的事件返回TRUE
        return  true;
    }
}