最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android利用SurfaceView實(shí)現(xiàn)下雨的天氣動(dòng)畫效果

 更新時(shí)間:2017年03月09日 10:41:21   作者:Melodyxxx  
這篇文章主要介紹了Android利用SurfaceView實(shí)現(xiàn)下雨天氣效果的相關(guān)資料,文中詳細(xì)介紹 SurfaceView 和 View 的區(qū)別,以及一些需要使用到 SurfaceView 的場(chǎng)景。需要的朋友可以參考借鑒,下面來一起看看吧。

首先是最終實(shí)現(xiàn)的效果圖:

先分析一下雨滴的實(shí)現(xiàn):

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

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

初識(shí) SurfaceView

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

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

要想訪問下面的 Surface ,可以通過 Android 提供給我們的 SurfaceHolder 接口??梢哉{(diào)用 SurfaceView 的 getHolder() 來獲取。

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

那么下面看下 SurfaceHolder.Callback 接口

public interface Callback {

// SurfaceView 創(chuàng)建時(shí)調(diào)用(SurfaceView的窗口可見時(shí))
 public void surfaceCreated(SurfaceHolder holder);

// SurfaceView 改變時(shí)調(diào)用 
 public void surfaceChanged(SurfaceHolder holder, int format, int width,
 int height);

// SurfaceView 銷毀時(shí)調(diào)用(SurfaceView的窗口不可見時(shí))
 public void surfaceDestroyed(SurfaceHolder holder);

 }

我們的繪制代碼需要在 surfaceCreated 和 surfaceDestroyed 之間執(zhí)行,否則無效,SurfaceHolder.Callback的回調(diào)方法是執(zhí)行在 UI 線程中的,繪制線程需要我們自己手動(dòng)創(chuàng)建。

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

View 和 SurfaceView 的使用場(chǎng)景

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

使用 SurfaceView(實(shí)現(xiàn))

這里我們和自定義 View 類似,寫一個(gè)類 DynamicWeatherView 繼承自 SurfaceView,然后為了監(jiān)聽 SurfaceView 的狀態(tài),所以我們還需要實(shí)現(xiàn) SurfaceHolder.Callback 接口來監(jiān)聽 SurfaceView 的狀態(tài),接口的回調(diào)具體時(shí)機(jī)上面也已經(jīng)介紹過了。

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 創(chuàng)建時(shí)調(diào)用(可見)
 @Override
 public void surfaceCreated(SurfaceHolder holder) {

 }

 // SurfaceView 銷毀時(shí)調(diào)用(不可見)
 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {

 }

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

 }

}

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

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

然后實(shí)現(xiàn)我們的繪制線程:

private class DrawThread extends Thread {

 // 用來停止線程的標(biāo)記
 private boolean isRunning = false;

 public void setRunning(boolean running) {
 isRunning = running;
 }

 @Override
 public void run() {
 Canvas canvas;
 // 無限循環(huán)繪制
 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 的更新流程具體為:

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

繪制線程代碼量不多,因?yàn)榫唧w的繪制代碼在 mType.onDraw(canvas)中,mType 是我們自己定義的一個(gè)接口,代表一種天氣類型:

public interface WeatherType {
 void onDraw(Canvas canvas);

 void onSizeChanged(Context context, int w, int h);
}

這樣要想實(shí)現(xiàn)不同的天氣類型,只要實(shí)現(xiàn)這個(gè)接口重寫 onDraw 和 onSizeChanged 方法即可,這里我們實(shí)現(xiàn)的是下雨的效果,所以實(shí)現(xiàn)了一個(gè) 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);
 // 這里雨滴的寬度統(tǒng)一為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);
 // 畫出集合中的雨點(diǎn)
 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);
 }
 // 將集合中的點(diǎn)按自己的速度偏移
 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 {
 /**
 * 雨點(diǎn) x 軸坐標(biāo)
 */
 int x;
 /**
 * 雨點(diǎn) y 軸坐標(biāo)
 */
 int y;
 /**
 * 雨點(diǎn)長(zhǎng)度
 */
 int l;
 /**
 * 雨點(diǎn)移動(dòng)速度
 */
 int s;
 /**
 * 雨點(diǎn)透明度
 */
 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 對(duì)象代表一個(gè)雨滴,每繪制一次然后改變雨滴的位置,然后準(zhǔn)備下一次繪制,來實(shí)現(xiàn)雨滴的移動(dòng)。

BaseType 類是我們的一個(gè)抽象基類,實(shí)現(xiàn)了 DynamicWeatherView.WeatherType 接口,內(nèi)部有一些公共方法,具體可以看 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));
 }
}

今后要想實(shí)現(xiàn)不同的天氣類型,只需要繼承 BaseType 類重寫相關(guān)方法即可。

源碼下載:點(diǎn)擊這里

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)各位Android開發(fā)者們能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Android 加載GIF圖最佳實(shí)踐方案

    Android 加載GIF圖最佳實(shí)踐方案

    最近在項(xiàng)目中遇到需要在界面上顯示一個(gè)本地的 GIF 圖的功能,下面通過本文給大家分享Android 加載GIF圖最佳實(shí)踐方案,需要的朋友參考下吧
    2017-08-08
  • 簡(jiǎn)析Android多種AlertDialog對(duì)話框效果

    簡(jiǎn)析Android多種AlertDialog對(duì)話框效果

    這篇文章主要為大家介紹了Android多種AlertDialog對(duì)話框效果,四種對(duì)話框效果一一為大家展示,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android編程實(shí)現(xiàn)圓角邊框的方法

    Android編程實(shí)現(xiàn)圓角邊框的方法

    這篇文章主要介紹了Android編程實(shí)現(xiàn)圓角邊框的方法,涉及Android針對(duì)樣式布局的相關(guān)設(shè)置技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-11-11
  • MVVM和MVVMLight框架介紹及在項(xiàng)目中的使用詳解

    MVVM和MVVMLight框架介紹及在項(xiàng)目中的使用詳解

    這篇文章主要為大家介紹了MVVM和MVVMLight的介紹及在項(xiàng)目中的使用詳解有需要的朋友可以借鑒參考下,祝大家除夕快樂多多進(jìn)步
    2022-01-01
  • Android使用Handler實(shí)現(xiàn)定時(shí)器與倒計(jì)時(shí)器功能

    Android使用Handler實(shí)現(xiàn)定時(shí)器與倒計(jì)時(shí)器功能

    Handler的最常見應(yīng)用場(chǎng)景之一便是通過Handler在子線程中間接更新UI。這篇文章主要介紹了Android使用Handler實(shí)現(xiàn)定時(shí)器與倒計(jì)時(shí)器功能,需要的朋友可以參考下
    2018-02-02
  • 實(shí)例講解Android應(yīng)用開發(fā)中TabHost的使用要點(diǎn)

    實(shí)例講解Android應(yīng)用開發(fā)中TabHost的使用要點(diǎn)

    這篇文章主要介紹了Android應(yīng)用開發(fā)中TabHost的使用要點(diǎn),文中以實(shí)例講解了TabHost與Tab的布局方法,需要的朋友可以參考下
    2016-04-04
  • Android基于OpenCV實(shí)現(xiàn)霍夫直線檢測(cè)

    Android基于OpenCV實(shí)現(xiàn)霍夫直線檢測(cè)

    霍夫變換利用點(diǎn)與線之間的對(duì)偶性,將圖像空間中直線上離散的像素點(diǎn)通過參數(shù)方程映射為霍夫空間中的曲線,并將霍夫空間中多條曲線的交點(diǎn)作為直線方程的參數(shù)映射為圖像空間中的直線。給定直線的參數(shù)方程,可以利用霍夫變換來檢測(cè)圖像中的直線。本文簡(jiǎn)單講解Android的實(shí)現(xiàn)
    2021-06-06
  • Android viewpager 3D畫廊的實(shí)現(xiàn)方法

    Android viewpager 3D畫廊的實(shí)現(xiàn)方法

    ViewPager在開發(fā)中的使用頻率非常的高,接下來通過本文給大家分享android viewpager 3D畫廊的實(shí)現(xiàn)方法,需要的朋友參考下吧
    2017-02-02
  • Android中多行文本末尾添加圖片排版問題的解決方法

    Android中多行文本末尾添加圖片排版問題的解決方法

    這篇文章主要給大家介紹了關(guān)于Android中多行文本末尾添加圖片排版問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • Android編程之include文件的使用方法

    Android編程之include文件的使用方法

    這篇文章主要介紹了Android編程之include文件的使用方法,實(shí)例分析了Android使用include引入文件的技巧,有助于增加代碼的重用性,提高程序設(shè)計(jì)效率,需要的朋友可以參考下
    2016-01-01

最新評(píng)論

寻乌县| 井冈山市| 贺兰县| 金堂县| 游戏| 霞浦县| 永定县| 揭东县| 浦城县| 全椒县| 普兰店市| 新乡县| 北辰区| 明溪县| 萨嘎县| 东城区| 越西县| 尚义县| 怀远县| 梧州市| 南溪县| 孟连| 宁波市| 河曲县| 杭锦旗| 贵德县| 宣汉县| 明水县| 游戏| 乌拉特前旗| 信宜市| 滁州市| 阿图什市| 尚义县| 宾川县| 榆树市| 仁怀市| 德安县| 邹平县| 榕江县| 天峻县|