天天看點

揭秘 Android Graphics2D 實作動态效果之——invalidate()

最近在研讀Android 自定義控件方面的知識,從剛開始的 建立一個簡單的位圖對象,在上面繪制圖形,到今天的 如何通過繪圖實作動态效果章節,突然感覺自己以前了解的Android 自定義view 僅僅皮毛而已。。。

就着今天所看的内容,大家可以和我重新認識下  invalidate() 方法 在整個View 繪制中到底扮演一個怎樣的角色?以下内容參閱李贊紅老師 自定義元件詳解,如有纰漏,請多指教!

一、Android 如何通過繪圖實作動态效果

使用 Graphics2D 實作動态效果,所說的動态效果主要包括兩方面:1、讓畫面動起來,如實作遊戲中的爆炸動畫,地球儀的自轉和公轉,小鳥翅膀的擺動,手表時針分針秒針的轉運等等,分析可知,可以通過過周期性重畫實作;2、是實作和使用者的互動,使用者通過手指在手機螢幕上移動,在繪圖區繪制曲線、矩形、圓、文字等圖案;

在繪圖過程中,雙緩存技術是一項很重要的技術,一方面能大大提高繪圖的效率,另一方面可以實作繪圖過程與結果分離,擁有身臨其境的使用者體驗。了解和掌握雙緩存技術的作用和意義是我們真正掌握 Graphics2D 的關鍵性因素之一。

 了解更多,請移步:了解 Android 雙緩沖技術繪圖機制

定義一個繼承自 View 的子類,重寫 onDraw(),在該方法中繪圖,當 View 顯示時會回調 onDraw()方法,用于繪制元件的外觀。

/**
 * Created by ${lxb} on 2019/5/15.
 * 郵箱:207***[email protected]
 * TIP:自定義view ,重寫onDraw()繪圖   小球左右移動
 */

public class MovingGraghicView extends View {

    /*小球的垂直位置,固定為 100*/
    private static final int Y = 100;
    /*小球的水準位置*/
    private int x;
    /*小球的半徑*/
    private static final int RADIUS = 30;
    /*小球的顔色*/
    private static final int COLOR = Color.RED;
    /*小球移動的方向*/
    private boolean direction;
    private Paint paint;

    public MovingGraghicView(Context context) {//動态執行個體化view用到;
        super(context);
    }

    public MovingGraghicView(Context context, @Nullable AttributeSet attrs) {//在xml 用到;
        super(context, attrs);
        //初始化畫筆 參數表示去鋸齒
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(COLOR);
        x = RADIUS;
    }

    public MovingGraghicView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {//不會被系統預設調用,需要自己去顯示的調用;
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //根據x,y 坐标畫一個小球
        canvas.drawCircle(x, Y, RADIUS, paint);
        //擷取元件的寬度
        int measuredWidth = this.getMeasuredWidth();

        if (x <= RADIUS) {
            direction = true;
        }
        if (x >= measuredWidth - RADIUS) {
            direction = false;
        }
        x = direction ? x + 5 : x - 5;

    }
}
           
定義了有兩個參數的構造方法,如果在布局檔案中定義了該元件,則會調用此構造方法來建立對象。
activity_plotting_moving_graphic.xml
           
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!--左右移動的小球-->
    <angqin.myapplication.custom_view.MovingGraghicView
        android:id="@+id/mgv_ball"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
           

 那麼,小球是如何水準移動的呢?讓動畫動起來,通過周期性重畫來實作,在 Activity 中恰恰是通過定時器周期性調用了 invalidate()方法不斷重繪元件,也就是不斷調用 onDraw()方法,因為小球的位置由 x 來決定,onDraw()每調用一次,x 的值就會變化一次,小球的位置自然也會跟着改變,小球也就動起來了。

/**
 * Created by ${lxb} on 2019/5/15.
 * 郵箱:207***[email protected]
 * TIP:如何通過繪圖實作動态效果
 * 使用 Graphics2D 實作動态效果
 */

public class PlottingMovingGraphicAty extends AppCompatActivity {
    @Bind(R.id.mgv_ball)
    MovingGraghicView mgvBall;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_plotting_moving_graphic);
        ButterKnife.bind(this);

        //通過定時器,周期性的調用invlidate(),不斷重繪小球,也就是不斷調用onDraw()方法,每調一次,x 值會變化一次,自然小球也就移動起來了
        //通過 Timer 類定義一個計時器,延時 200 毫秒開始計時,每隔 50 毫秒計時一次。
        // 定時任務類 TimerTask 其實就是一個子線程,隻能調用 postInvalidate()方法來重繪元件
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                mgvBall.postInvalidate();
            }
        },200,50);

    }
}
           
揭秘 Android Graphics2D 實作動态效果之——invalidate()

錄屏

二、invalidate()方法 到底幹了什麼?

View 類定義了一組 invalidate()方法:

1、 public  void  invalidate()

2、public  void  invalidate(int  l,  int  t,  int  r,  int  b)

3、public  void  invalidate(Rect  dirty)

 invalidate()用于重繪元件,不帶參數表示重繪整個視圖區域,帶參數表示重繪指定的區域。如果要去追溯該方法的源碼,大概就是将重繪請求一級級往送出到 ViewRoot,調用 ViewRoot的 scheduleTraversals()方法重新發起重繪請求,scheduleTraversals()方法會發送一個異步消息,調用 performTraversals()方法執行重繪,而 performTraversals()方法最終調用 onDraw()方法。是以,簡單來說,調用 View 的 invalidate()方法就相當于調用了 onDraw()方法,而 onDraw()方法中就是我們編寫的繪圖代碼。

特别要注意是:invalidate()方法隻能在 UI 線程中調用,如果是在子線程中重新整理元件,View 類還定義了另一組名為 postInvalidate 的方法:

1、public  void  postInvalidate()

2、public  void  postInvalidate(int  left,  int  top,  int  right,  int  bottom)

小結:要重新整理元件或讓畫面動起來,隻需調用 invaliddate()即可,通過改變資料來影響繪制結果,這是實作元件重新整理或實作動畫的基本思路。

繼續閱讀