国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android利用SurfaceView實現下雨的天氣動畫效果

2019-12-12 03:26:08
字體:
來源:轉載
供稿:網友

首先是最終實現的效果圖:

先分析一下雨滴的實現:

  1. 每個雨滴其實就是一條線,通過 canvas.drawLine() 繪制
  2. 線(雨滴)的長度、寬度、下落速度、透明度以及位置都是在一定范圍內隨機生成
  3. 每 draw 一次然后改變雨滴的位置然后重繪即可實現雨滴的下落效果

分析完了,那么可以直接寫一個類直接繼承 View ,然后重寫 onDraw() 嗎?可以看到效果圖中的雨滴的下落速度很快,那么意味著每一幀都要調用 onDraw() 一次使其重新繪制一次,假如你的 onDraw() 方法里面的渲染代碼稍微有點費時,而 View 的 onDraw() 方法調用是在 UI 線程中,那么繪制出來的效果就不會那么流暢,甚至還會阻塞 UI 線程,所以為了更流暢的效果并且不阻塞 UI 線程,我們這里使用 SurfaceView 來實現。

初識 SurfaceView

SurfaceView 直接繼承自 View,View 必須在 UI 線程中繪制,而 SurfaceView 不同于 View,它可以在非 UI 線程中繪制并顯示在界面上,這意味著你可以自己新開一個線程,然后把繪制渲染的代碼放在該線程中。

Surface 是 Z 軸排序的,SurfaceView 的 Z 軸位置小于它的宿主 Window,代表它總是在自己所在 Window 的后面,既然在后面,那么是怎么顯示的呢?SurfaceView 在其 Window 中打出一個“孔”(其實就是在其宿主 Window 上設置了一塊透明區域來使其能夠顯示),意味著他的兄弟節點的 View 會覆蓋它,例如你可以在 SurfaceView 上方放置按鈕,文本等控件。

要想訪問下面的 Surface ,可以通過 Android 提供給我們的 SurfaceHolder 接口。可以調用 SurfaceView 的 getHolder() 來獲取。

SurfaceView 是有生命周期的,我們必須在它生命周期期間進行執行繪制代碼,所以我們需要監聽 SurfaceView 的狀態(例如創建以及銷毀),這里 Android 為我們提供了 SurfaceHolder.Callback 這個接口來可以讓我們方便的監聽 SurfaceView 的狀態。

那么下面看下 SurfaceHolder.Callback 接口

public interface Callback {// SurfaceView 創建時調用(SurfaceView的窗口可見時) public void surfaceCreated(SurfaceHolder holder);// SurfaceView 改變時調用  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height);// SurfaceView 銷毀時調用(SurfaceView的窗口不可見時) public void surfaceDestroyed(SurfaceHolder holder); }

我們的繪制代碼需要在 surfaceCreated 和 surfaceDestroyed 之間執行,否則無效,SurfaceHolder.Callback的回調方法是執行在 UI 線程中的,繪制線程需要我們自己手動創建。

具體可看官方文檔:https://developer.android.google.cn/reference/android/view/SurfaceView.html

View 和 SurfaceView 的使用場景

  • View 適合那些與用戶交互并且渲染時間不是很長的控件,因為 View 的繪制和用戶交互都處在 UI 線程中。
  • SurfaceView 適合迅速的更新界面或者渲染時間比較長以至于影響到用戶體驗的場景。

使用 SurfaceView(實現)

這里我們和自定義 View 類似,寫一個類 DynamicWeatherView 繼承自 SurfaceView,然后為了監聽 SurfaceView 的狀態,所以我們還需要實現 SurfaceHolder.Callback 接口來監聽 SurfaceView 的狀態,接口的回調具體時機上面也已經介紹過了。

public class DynamicWeatherView extends SurfaceView implements SurfaceHolder.Callback{ public DynamicWeatherView(Context context) { this(context, null); } public DynamicWeatherView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DynamicWeatherView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } // SurfaceView 創建時調用(可見) @Override public void surfaceCreated(SurfaceHolder holder) { } // SurfaceView 銷毀時調用(不可見) @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }}

上面也提到了,控制 Surface 我們需要 SurfaceHolder 對象,調用 SurfaceView 的 getHolder() 即可獲得,然后為這個 SurfaceHolder 添加一個 SurfaceHolder.Callback 回調,這里就是 DynamicWeatherView 當前對象

private SurfaceHolder mHolder;
mHolder = getHolder();mHolder.addCallback(this);mHolder.setFormat(PixelFormat.TRANSPARENT);

然后實現我們的繪制線程:

private class DrawThread extends Thread { // 用來停止線程的標記 private boolean isRunning = false; public void setRunning(boolean running) { isRunning = running; } @Override public void run() { Canvas canvas; // 無限循環繪制 while (isRunning) { if (mType != null && mViewWidth != 0 && mViewHeight != 0) { canvas = mHolder.lockCanvas(); if (canvas != null) { mType.onDraw(canvas); if (isRunning) { mHolder.unlockCanvasAndPost(canvas); } else { // 停止線程 break; } SystemClock.sleep(1); } } } }}

從上面的代碼可以看出 SurfaceView 的更新流程具體為:

// 鎖定畫布并獲得 canvascanvas = mHolder.lockCanvas();// 在 canvas 上進行繪制mType.onDraw(canvas);// 解除鎖定并提交更改mHolder.unlockCanvasAndPost(canvas);

繪制線程代碼量不多,因為具體的繪制代碼在 mType.onDraw(canvas)中,mType 是我們自己定義的一個接口,代表一種天氣類型:

public interface WeatherType { void onDraw(Canvas canvas); void onSizeChanged(Context context, int w, int h);}

這樣要想實現不同的天氣類型,只要實現這個接口重寫 onDraw 和 onSizeChanged 方法即可,這里我們實現的是下雨的效果,所以實現了一個 RainTypeImpl 類:

public class RainTypeImpl extends BaseType { // 背景 private Drawable mBackground; // 雨滴集合 private ArrayList<RainHolder> mRains; // 畫筆 private Paint mPaint; public RainTypeImpl(Context context, DynamicWeatherView dynamicWeatherView) { super(context, dynamicWeatherView); init(); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.WHITE); // 這里雨滴的寬度統一為3 mPaint.setStrokeWidth(3); mRains = new ArrayList<>(); } @Override public void generate() { mBackground = getContext().getResources().getDrawable(R.drawable.rain_sky_night); mBackground.setBounds(0, 0, getWidth(), getHeight()); for (int i = 0; i < 60; i++) { RainHolder rain = new RainHolder( getRandom(1, getWidth()), getRandom(1, getHeight()), getRandom(dp2px(9), dp2px(15)), getRandom(dp2px(5), dp2px(9)), getRandom(20, 100) ); mRains.add(rain); } } private RainHolder r; @Override public void onDraw(Canvas canvas) { clearCanvas(canvas); // 畫背景 mBackground.draw(canvas); // 畫出集合中的雨點 for (int i = 0; i < mRains.size(); i++) { r = mRains.get(i); mPaint.setAlpha(r.a); canvas.drawLine(r.x, r.y, r.x, r.y + r.l, mPaint); } // 將集合中的點按自己的速度偏移 for (int i = 0; i < mRains.size(); i++) { r = mRains.get(i); r.y += r.s; if (r.y > getHeight()) { r.y = -r.l; } } } private class RainHolder { /** * 雨點 x 軸坐標 */ int x; /** * 雨點 y 軸坐標 */ int y; /** * 雨點長度 */ int l; /** * 雨點移動速度 */ int s; /** * 雨點透明度 */ int a; public RainHolder(int x, int y, int l, int s, int a) { this.x = x; this.y = y; this.l = l; this.s = s; this.a = a; } }}

代碼不難,基本都有注釋,RainHolder 對象代表一個雨滴,每繪制一次然后改變雨滴的位置,然后準備下一次繪制,來實現雨滴的移動。

BaseType 類是我們的一個抽象基類,實現了 DynamicWeatherView.WeatherType 接口,內部有一些公共方法,具體可以看 Demo 中的代碼。

最后我們的 Activity 代碼:

public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DynamicWeatherView mDynamicWeatherView = (DynamicWeatherView) findViewById(R.id.dynamic_weather_view); mDynamicWeatherView.setType(new RainTypeImpl(this, mDynamicWeatherView)); }}

今后要想實現不同的天氣類型,只需要繼承 BaseType 類重寫相關方法即可。

源碼下載:點擊這里

總結

以上就是這篇文章的全部內容了,希望本文的內容對各位Android開發者們能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 桐柏县| 鄯善县| 镇安县| 尼勒克县| 慈利县| 衡山县| 夏邑县| 柳河县| 肇东市| 夏邑县| 黑山县| 五寨县| 皮山县| 义马市| 绥滨县| 微山县| 娱乐| 广东省| 天津市| 定兴县| 利辛县| 旺苍县| 札达县| 东丽区| 东城区| 托里县| 鲜城| 腾冲县| 兴安县| 济源市| 东山县| 鞍山市| 沧源| 开江县| 三明市| 紫云| 花莲县| 四子王旗| 仪征市| 平谷区| 黄陵县|