最近在研讀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);
}
}
錄屏
二、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()即可,通過改變資料來影響繪制結果,這是實作元件重新整理或實作動畫的基本思路。