1. 程式人生 > >android自定義view粒子效果之雨(not surfaceview)

android自定義view粒子效果之雨(not surfaceview)

首先宣告的是,粒子效果不一定是用surfaceview來實現的,只要可以繪製和更新繪製既可以做到很多精彩的畫面。

簡單的說一下自定義view吧,其實就是繼承View,然後生成幾個構造方法,這樣就是一個簡單的自定義view。

public class MyView extends View{

	public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}

	public MyView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MyView(Context context) {
		super(context);
	}

}
但是它什麼也做不了,什麼也不顯示,很多人都知道,覆蓋onDraw(Canvas)方法,沒錯
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		//做自己想做的事情(繪製)
	}

這樣,一個可以顯示自己繪製的view已經完成一半了,為什麼說一半,因為還不會動,但是這會有很多邏輯,而這些邏輯不應該出現在主執行緒中,所以用Handler來做這部分事情,開啟一個處理邏輯的執行緒通過Handler來更新繪製。
	private Runnable run = new Runnable() {
		public void run() {
			long curTime = 0;
			while (true) {
				
				curTime = System.currentTimeMillis();
				//此處加入邏輯
				
				mHandler.sendEmptyMessage(0);
				curTime = System.currentTimeMillis() - curTime;
				try {
					if(curTime < 30){
						Thread.sleep(30 - curTime);
					}
				} catch (InterruptedException e) {
					break;
				}
			}
		}
	};
	private void logic(){
		//此處加入邏輯處理
	}
	private Handler mHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			invalidate();
		};
	};

好吧,這樣基本上一個自定義view的框架就出來了。

鋪墊好了後面就會好理解了,繪製和動畫基本上都是在onDraw裡面和logic裡面完成,其他的粒子效果也是。

粒子效果在我看來,其實就是具有相同屬性的物件集合運動,說白了,就是一個列表裡面有多個物件,每個物件自己都在動,放在一起,就出現了粒子效果。再簡單點說,只要定義一個類,這個類有繪製和運動就可以了,然後例項化多個,放在列表裡面,繪製的時候遍歷每個元素就可以了。

所以要在自定義view中加入一個ArrayList就可以了,然後在onDraw和logic裡面遍歷list裡面的每個元素。說到這裡了,其實核心也出來了,粒子效果最終追述到的根源,還是單個元素的定製。

那麼粒子效果有大家想象的那麼難嗎,其實沒有,起碼我寫的幾個都是在100行程式碼左右(單個元素),那麼,來看看下雨的效果怎麼實現的吧。

如果在手機上看,會很清楚和流暢,因為我用視訊錄製轉換成gif,這樣解析度就小了很多,而且幀率也變了。

單個雨點實際上就是一條線,線的大小可以根據自己的喜好定製隨機範圍。那麼需要定義一些基本的變數。顯示的寬,高,隨機數。

	/**
	 * 顯示區域的寬度
	 */
	protected int width;
	/**
	 * 顯示區域的高度
	 */
	protected int height;
	/**
	 * 效果元素的隨機物件
	 */
	protected Random rand;

上面的寬高是通過View的getWidth()和getHeight()來傳遞的。rand也是在構造的時候就已經例項化了。

	public EffectItem(int width, int height){
		this.width = width;
		this.height = height;
		rand =new Random();
	}

上面的EffectItem其實就是Rain,只不過我把它抽離出來了,方便以後其他粒子複用,但是不影響閱讀,把它看成Rain就可以了。繪製的區域出來了,那麼繪製的線呢?這就通過rand來生成,先看看系統繪製直線的方法:
public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
        native_drawLine(mNativeCanvas, startX, startY, stopX, stopY, paint.mNativePaint);
    }

需要起始x,y和終點x,y。系統有個Rect的類,它有left, top, right, bottom。所以可以使用這個類來輔助。
	<span style="white-space:pre">	</span>int x = rand.nextInt(width);
		int y = rand.nextInt(height);
		int w = rand.nextInt(size / 2);
		int h = rand.nextInt(size);
		
		w = w > h ? h : w;
		
		point.left = x;
		point.top = y;
		point.right = x - w;
		point.bottom = y + h;

size的值可以自己定,我定的是50畫素,這樣一條線段的座標就出來了,只要draw裡面使用就可以了
	public void draw(Canvas canvas){
		canvas.drawLine(point.left, point.top, point.right, point.bottom, paint);
	}

還記得我前面說的嗎,每個元素都會在自定義的View裡面的onDraw(Canvas)裡面遍歷繪製,所以不用途擔心這裡面的draw不起作用。ok,繪製線出來了,但是,你會發現這個線不動,因為沒有邏輯讓它動起來。

那麼該怎麼動呢,對,用速度,要讓它往下落,那麼y的座標就一直在變,所有y方向有有個速度,但是你又會發現,一個斜槓在往下掉,不符合自然規律,應該讓它按照它的角度和方向落下,聽的好難啊啊,其實就2行程式碼,上面程式碼你會很奇怪有個w,h,這次有用了,稍微在紙上畫畫,你會發現這條線段是按照一定規律,即一個係數(是幾不重要),只要x方向乘以w和y方向乘以h就可以了,所以如果定義速度的話就好辦多了。

	private void reset(){
		int x = rand.nextInt(width);
		int y = rand.nextInt(height);
		int w = rand.nextInt(size / 2);
		int h = rand.nextInt(size);
		
		w = w > h ? h : w;
		
		point.left = x;
		point.top = y;
		point.right = x - w;
		point.bottom = y + h;
		
		int speedX = w;
		int speedY = h;
		
		speedX = speedX == 0  ? 1 : speedX;
		speedY = speedY == 0  ? 1 : speedY;
		speedX = speedX > speedY ? speedY : speedX;
		
		speed.x = -speedX;
		speed.y = speedY;

	}
上面就是生成一個雨點的方法,speed是個Point的物件,這樣可以表示x和y方向上的速度。

生成和繪製都有了,就差運動了

	public void move(){
		point.left += speed.x;
		point.top += speed.y;
		point.right = point.right + speed.x;
		point.bottom = point.bottom + speed.y;
		
		if(point.left < 0 || point.left > width || point.bottom > height){
			reset();
		}
		speed.y += rand.nextBoolean() ? 1 : 0;
	}

上面就是運動的方法,就是簡單的把位置變換了下,但是當出繪製區域的時候,會重新生成一個雨點,所以,螢幕上實際上就是固定數目的雨點的不停的變換位置。最後的y方向的速度是不定時的將y方向加入加速度,這樣就會有重力似的效果。每個元素move方法其實就是在自定義View中的logic中被遍歷的。

到此位置,一個雨點的生成,繪製,運動都完成了,只要加入到list中然後在View裡面遍歷就基本完成了。一個雨點完整的程式碼如下:

public class RainPoint extends EffectItem{

	private Paint paint = new Paint();
	private	final int size = 50;	//長度在0-50畫素
	private Rect point;		//雨點
	private Point speed;		//雨點x,y方向速度
	
	public RainPoint(int width, int height){
		super(width, height);
		point = new Rect();
		speed = new Point();

		paint.setColor(0xffffffff);
		reset();
	}
	
	public void draw(Canvas canvas){
		canvas.drawLine(point.left, point.top, point.right, point.bottom, paint);
	}
	
	public void move(){
		point.left += speed.x;
		point.top += speed.y;
		point.right = point.right + speed.x;
		point.bottom = point.bottom + speed.y;
		
		if(point.left < 0 || point.left > width || point.bottom > height){
			reset();
		}
		speed.y += rand.nextBoolean() ? 1 : 0;
	}
	
	private void reset(){
		int x = rand.nextInt(width);
		int y = rand.nextInt(height);
		int w = rand.nextInt(size / 2);
		int h = rand.nextInt(size);
		
		w = w > h ? h : w;
		
		point.left = x;
		point.top = y;
		point.right = x - w;
		point.bottom = y + h;
		
		int speedX = w;
		int speedY = h;
		
		speedX = speedX == 0  ? 1 : speedX;
		speedY = speedY == 0  ? 1 : speedY;
		speedX = speedX > speedY ? speedY : speedX;
		
		speed.x = -speedX;
		speed.y = speedY;

	}	
	
}
而EffectItem程式碼如下:
<pre name="code" class="java">public abstract class EffectItem implements EffectBase{
	/**
	 * 顯示區域的寬度
	 */
	protected int width;
	/**
	 * 顯示區域的高度
	 */
	protected int height;
	/**
	 * 效果元素的隨機物件
	 */
	protected Random rand;

	
	public EffectItem(int width, int height){
		this.width = width;
		this.height = height;
		rand =new Random();
	}	
}
EffectBase介面定義了兩個方法:
public interface EffectBase {
	/**
	 * 繪製效果
	 * @param canvas
	 */
	public void draw(Canvas canvas);
	
	/**
	 * 效果元素變化
	 */
	public void move();
	
}

這樣,一個粒子的完整過程就出來了,而後面其它的粒子也是按照這個模型就行設計的。剩下的就是在list裡面加入多個例項了,然後按部就班的在onDraw裡面遍歷每個元素的draw方法,在logic裡面遍歷move方法。
<span style="white-space:pre">	</span><span style="color:#ff0000;">最重要的是,因為這是自定義View,可以像使用其他View一樣,放到佈局裡面,所以豐富它,可以有無限的遐想展示空間。</span>
下面我再貼出我豐富的幾個豐富的效果,後面會說到怎麼實現。

變化雨點顏色(在xml中定義屬性就可以了)

每個雨點都隨機顏色(在xml中定義屬性就可以了)


還可以進行區域clip哦


好了,如果誰有好的想法也可以共享一下哦,看看如果能實現,看看效果。詳細程式碼:https://github.com/xianfeng99/Particle

github上面的程式碼可能沒有及時更新。

相關推薦

android定義view粒子效果not surfaceview

首先宣告的是,粒子效果不一定是用surfaceview來實現的,只要可以繪製和更新繪製既可以做到很多精彩的畫面。 簡單的說一下自定義view吧,其實就是繼承View,然後生成幾個構造方法,這樣就是一個簡單的自定義view。 public class MyView exte

Android定義View實戰】定義評價打分控制元件RatingBar,可以定義星星大小和間距

在Android開發中,我們經常會用到對商家或者商品的評價,運用星星進行打分。然而在Android系統中自帶的打分控制元件,RatingBar特別不好用,間距和大小無法改變。所以,我就自定義了一個特別好用的打分控制元件。在專案中可以直接使用,特別簡

Android定義View實戰】定義超簡單SearchView搜尋框

package cn.bluemobi.dylan.searchview; import android.content.Context; import android.text.Editable; import android.text.TextWatcher; import android.util.A

Android 定義View動畫效果進階

RectF oval = new RectF(x, y, x1, y1); paint.setColor(Color.WHITE);//設定圓環的顏色 paint.setStyle(Paint.Style.STROKE);//設定空心 paint.setAntiAlias(true);//消除鋸齒 pain

Android定義View--翻書控制元件

0.前言 最近重看了一遍封神演義,感覺QQ閱讀那個翻書的效果挺好的,準備做一個。上週五下午用了兩個小時只寫了一部分功能,以後有時間再完善 1.分析 先看效果圖 這個空間,說簡單也簡單,說難也難,簡單就在於這個效果主要就是依賴canvas的clippath才見到部分canvas,難就難在裁

Android 定義 HorizontalScrollView 打造再多圖片控制元件也不怕 OOM 的橫向滑動效果

自從Gallery被谷歌廢棄以後,Google推薦使用ViewPager和HorizontalScrollView來實現Gallery的效果。的確HorizontalScrollView可以實現Gallery的效果,但是HorizontalScrollView存在一個很大的問

Android定義View ——畫弧線詳解Rectf放入用法

好久沒有寫部落格了。最近想寫一個關於Android的介面,類似於遙控器按鈕形狀,就是類似於下面這張圖片,但是我想設計的使上下左右四個按鈕不是連在一起的,他們之間是有間隔的。在設計的途中關於畫弧線這個函式,我一直沒有理解,所以就這部分進行了一些測試,大家看過這張圖片之後就明白

Android 定義View練習:雷達圖比重繪製

code: package com.louisgeek.louiscustomviewstudy; import android.content.Context; import android.content.res.Resources; import

android 定義dialog並實現失去焦點背景透明的功能

前言:由於在專案中需要用到更新顯示動畫的需求,所以想到了dialog,自定義dialog不難,網上教程很多,但是在實現dialog背景透明的需求時,遇到了一點問題,網上的一些方法在我的機器上並沒有實現,只能曲折中找到了另一個方法實現。雖然有點麻煩,但畢竟效果不錯。 此方法寫

Android 定義視訊錄製終極解決方案翻轉問題

Android 自定義視訊錄製翻轉問題終極解決方案 自定義視訊錄製 使用系統可用播放器 前後攝像和視訊反轉問題 總結 自定義視訊錄製 mediarecorder = new MediaRecorder();// 建立mediarecorde

android定義View3D索引效果

效果圖: 我的小霸王太卡了。 最近工作比較忙,今天搞了一下午才搞出來這個效果,這種效果有很多種實現方式,最常見的應該是用貝塞爾曲線實現的。今天我們來看另一種不同的實現方式,只需要用到 canvas.scale(),有沒有很好奇是怎麼實現的呢。 首先來說一下思路,只要有了思

android 定義view側滑效果

效果圖: 看網上的都是兩個view拼接,預設右側的不顯示,水平移動的時候把右側的view顯示出來。但是看最新版QQ上的效果不是這樣的,但給人的感覺卻很好,所以獻醜來一發比較高仿的。 知識點: 1、ViewDragHelper 的用法; 2、滑動衝突的解決; 3、自定

Android定義View水波紋顯示進度效果

@Override protected void onDraw(Canvas canvas) { if (null != backgroundBitmap) { canvas.drawBitmap(createImage(), 0, 0, null);

Android 定義ViewTextView跑馬燈效果

public class MarqueeView extends SurfaceView implements SurfaceHolder.Callback{ public Context mContext; private float mTextSize = 100; //字型大小

Android定義ViewListView實現時間軸效果:我只是個送快遞的。

先上效果圖: 實現時間軸的原理 listview的基本使用,相信大家都很熟悉。先在layout下新建一個xml佈局檔案,對應一個子項的listView的顯示內容。在上面的圖我們可以看到,每一項都是 有3個 textview控制元件

Android定義View點選效果

最近在做新版本,各種UI效果都需要自定義,而自定義View點選效果問題一直困擾著我。各種找資料也沒有找到自己想要的東西,可能是我關鍵字打的不對吧。最後在檢視TextView的原始碼時解決了我的問題,由於原始碼功能太多,不易查詢,特此提取記錄。 UI效果

Android定義View使用Path繪製手勢軌跡和水波效果

先看下效果圖: 繪製軌跡 繪製手指的軌跡主要是攔截View的onTouchEvent()方法,並根據手指的軌跡繪製path。path中有兩種可以實現的方法 1、Path.lineTo(x,y)方法 public class MoveP

Android -- 定義view實現keep歡迎頁倒計時效果

super onfinish -m use new getc awt ttr alt 1,最近打開keep的app的時候,發現它的歡迎頁面的倒計時效果還不錯,所以打算自己來寫寫,然後就有了這篇文章。 2,還是老規矩,先看一下我們今天實現的效果   相較於我們常見的倒計時

Android定義View效果目錄

class 重寫 自定義 textview 居中 url 冒泡 and 雷達圖 1、絢麗的loading動效的實現 2、Android自定義View:進度條+冒泡文本 3、Android雷達圖(蜘蛛網圖) 4、Android文本閃爍 5、Android繪制圓形進度條 6、重

Android定義View——實現水波紋效果類似剩余流量球

string 三個點 pre ber block span 初始化 move 理解 最近突然手癢就想搞個貝塞爾曲線做個水波紋效果玩玩,終於功夫不負有心人最後實現了想要的效果,一起來看下吧: 效果圖鎮樓 一:先一步一步來分解一下實現的過程 需要繪制一個正弦曲線(sin