Aexi(5)-Glyph的事件處理

         這次的博客主要實現這樣一個功能,當點擊文檔中的某個點時,調整光標Caret的位置.

         那麼到底如何實現這樣的功能呢?剛開始的時候我們肯定先考慮比較直接一點的方法.先使用一個controller類來接收鼠標點擊事件.在相應的處理方法中可以拿到鼠標點擊的座標.然後遍歷所有的page,row,basicGlyph來獲得鼠標點擊的具體圖元,然後再去做具體的處理.

         但是我寫了一會之後,馬上就否決了這個方案,因爲這個方案需要在同一個事件處理函數處理的代碼實在太多了,比如我們需要給caret設置frame.最直接的方法就是挨個遍歷Glyph比較點擊點和Glyph的frame,還要根據是偏左偏右來決定caret是放在左邊還是右邊.

         這是點擊到了Glyph的情況,如果點擊到了某一行的空白區域,就要把caret設置到最右邊.如果點擊了空行,則更加複雜,因爲僅僅設置frame是不可以的,還是更改caret的文檔索引,以便於用戶在caret所在的位置插入文字.

         最最關鍵的是如果後期我們要對鼠標的點擊事件做進一步擴充的話,那麼這個函數就會無休止的變得臃腫,因此肯定要進行封裝.把和每個圖元相關的方法封裝到圖元自己的身上去.

         具體的設計是怎樣的呢?首先我們給GlyphImpl類增加的一個函數public Boolean hitRect(x,y),傳入一個座標值,返回該座標點是否落在本對象內.

具體函數實現代碼如下:

public boolean hitRect(int x, int y) {
    Frame frame = getFrame();
    if (x > (frame.getX() + frame.getWidth()) || x < frame.getX())
        return false;
    return !(y > (frame.getY() + frame.getHeight()) || y < frame.getY());
}

整個文檔結構是分層級的,每個層級的對象都應該有機會處理到點擊事件,並且上層決定事件時候分發給下層,下層接受到事件之後,還要告訴上層是否已經處理完了,如果沒有處理完應該由上層繼續協助處理.

寫到這裏大家應該就非常清楚了,這樣的設計就是借鑑了Android系統的事件處理機制.

         我們給Glyph添加如下的的幾個方法:

@Override
public boolean dispatchClickEvent(MouseEvent e) {
    if (interceptClickEvent(e))
        return true;
    return onClickEvent(e);
}

@Override
public boolean onClickEvent(MouseEvent e) {
    return false;
}

@Override
public boolean interceptClickEvent(MouseEvent e) {
    return false;
}

這是GlyphImpl的默認實現,主要針對的是基本圖元.從dispatchClickEvent()可以看出,首先詢問了本類的interceptClickEvent函數是否截斷該事件的傳遞,如果不截斷,就發送給本類的onclickEvent()函數進行處理.

下面是GlyphImplGroup的默認實現,主要區別在於dispatchClickEvent()方法.

@Override
public boolean dispatchClickEvent(MouseEvent e) {
    if (interceptClickEvent(e))
        return true;
    List<GlyphImpl> children = getChildren();
    Glyph toBeHandledGlyph = null;
    for (GlyphImpl glyph : children) {
        if (glyph.hitRect(e.getX(), e.getY())) {
            toBeHandledGlyph = glyph;
            break;
        }
    }
    //toBeHandledGlyph可能爲空
    if (toBeHandledGlyph == null)
        //進入這裏就表示,點到空白位置了,這裏需要區別對待,如果是row就要把caret放到row的最右邊,如果是page就要把caret放到最後一行.
        //應該直接分發給本類對象的onClickEvent()方法
        return onClickEvent(e);
    if (!toBeHandledGlyph.dispatchClickEvent(e))
        return onClickEvent(e);
    return true;
}

從函數的具體實現可以看出,首先詢問本類的interceptEvent()方法是否截斷,如果不截斷,就遍歷子圖元調用hitrect()方法找到具體擊中的子圖元.如果沒有找到子圖元就發送給本類的onclickEvent()方法.如果找到了就根據子圖元的dispatch方法的返回值決定是否還要繼續分發給本類onclickEvent();

這樣就可以實現上層的對象通過intercept()函數進行控制是否發送事件給下層,下層通過onclickEvent()決定事件是否要拋給上層.

經過這樣的設計,就使得複雜的點擊事件處理變得簡單了,當需要擴充時,可以根據具體的業務邏輯,找到對應的對象並在其onclickEvent()中進行處理了.





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章