Android自定義view實現(xiàn)雪花特效實例代碼
一、前言
這個冬天,老家一直沒有下雨, 正好圣誕節(jié),就想著制作一個下雪的特效。
圣誕祝福:平安夜,舞翩阡。雪花飄,飛滿天。心與心,永相伴。
圣誕節(jié)是傳統(tǒng)的宗教節(jié)日,對于基 督徒,那是慶祝耶穌的誕生,紀念耶穌和發(fā)揚基督精神?,F(xiàn)在整個西方社會都在過圣誕節(jié),像許多宗教節(jié)日一樣,它已經(jīng)越來越民俗化了。
盡管如此,圣誕節(jié)依然倍受尊重。人們在圣誕快樂中懷有對耶穌的敬仰,歡樂的節(jié)慶里含有莊嚴肅穆的神念。歡度圣誕佳節(jié)的人都不拒絕耶穌的教誨,要仁愛、善良、誠實、忍耐、感恩……在信神的國度,不是基 督徒的人們,也都知道人應該感恩,心存謝意。對需要幫助的人給予關(guān)愛;對他人的幫助給予感謝。這是西方社會價值觀的一部份,而不是說圣誕夜就只是一家坐在壁爐前,共進有火雞或烤鵝的圣誕大餐或是冬季里開的一個最熱鬧的大派對。
二、創(chuàng)意名
Android實現(xiàn)雪花特效自定義view
三、效果展示
四、實現(xiàn)步驟
1.創(chuàng)建一個view,里面加載雪花類的集合,有一個死循環(huán)線程,一直執(zhí)行動畫
public class myRunnable implements Runnable { @Override public void run() { while (true){ Canvas canvas =null; try { synchronized (holder){ canvas = holder.lockCanvas(); //清除畫布 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); for (Snowflake snowflake :list){ snowflake.draw(canvas); snowflake.update(); } } }catch (Exception e){ e.printStackTrace(); }finally { if (canvas!=null){ holder.unlockCanvasAndPost(canvas); } } } } }
這樣的話,可以讓所有的雪花圖片動起來
2.創(chuàng)建雪花類,其實就是一個bitmap,然后設置不同尺寸和動畫
這步相對來說簡單一些,其實就是將bitmap繪制到畫布上面
public void reset(){ size = randomizer.randomInt(sizeMinInPx, sizeMaxInPx, true); if (image!=null){ if (bitmap==null){ bitmap = Bitmap.createScaledBitmap(image, size, size, false); } } float speed = (float)(size - sizeMinInPx) / (sizeMaxInPx - sizeMinInPx) * (speedMax - speedMin) + speedMin; double angle = Math.toRadians(randomizer.randomDouble(angleMax) * randomizer.randomSignum()); speedX = speed* Math.sin(angle); speedY = speed* Math.cos(angle); alpha = randomizer.randomInt(alphaMin, alphaMax, false); paint.setAlpha(alpha); positionX = randomizer.randomDouble(parentWidth); this.positionY=randomizer.randomDouble(parentHeight); if (!alreadyFalling){ this.positionY = this.positionY-parentHeight-size; } }
3.界面展示
實現(xiàn)manifest加載視圖即可
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000" tools:context="com.marvin.snowfall_master.MainActivity"> <com.itbird.SnowfallView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/sf_snow" app:snowflakesNum="200" app:snowflakeAlphaMin="150" app:snowflakeAlphaMax="255" app:snowflakeAngleMax="5" app:snowflakeSizeMin="2dp" app:snowflakeSizeMax="40dp" app:snowflakeSpeedMin="2" app:snowflakeSpeedMax="10" app:snowflakesFadingEnabled="true" app:snowflakesAlreadyFalling="false" app:snowflakeImage="@mipmap/snowflake" /> </android.support.constraint.ConstraintLayout>
五、編碼實現(xiàn)
界面類SnowfallView
public class SnowfallView extends SurfaceView implements SurfaceHolder.Callback { private int DEFAULT_SNOWFLAKES_NUM = 200; private int DEFAULT_SNOWFLAKE_ALPHA_MIN = 150; private int DEFAULT_SNOWFLAKE_ALPHA_MAX = 250; private int DEFAULT_SNOWFLAKE_ANGLE_MAX = 10; private int DEFAULT_SNOWFLAKE_SIZE_MIN_IN_DP = 2; private int DEFAULT_SNOWFLAKE_SIZE_MAX_IN_DP = 8; private int DEFAULT_SNOWFLAKE_SPEED_MIN = 2; private int DEFAULT_SNOWFLAKE_SPEED_MAX = 8; private boolean DEFAULT_SNOWFLAKES_FADING_ENABLED = false; private boolean DEFAULT_SNOWFLAKES_ALREADY_FALLING = false; private int snowflakesNum; private Bitmap snowflakeImage; private int snowflakeAlphaMin; private int snowflakeAlphaMax; private int snowflakeAngleMax; private int snowflakeSizeMinInPx; private int snowflakeSizeMaxInPx; private int snowflakeSpeedMin; private int snowflakeSpeedMax; private boolean snowflakesFadingEnabled; private boolean snowflakesAlreadyFalling; //雪花類集合 private ArrayList<Snowflake> list =new ArrayList<>(); private SnowfallView.myRunnable myRunnable = new myRunnable(); private Thread myThread; private SurfaceHolder holder; public SnowfallView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context,attrs); } private void init(Context context, AttributeSet attributeSet) { TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.SnowfallView); snowflakesNum = typedArray.getInt(R.styleable.SnowfallView_snowflakesNum, DEFAULT_SNOWFLAKES_NUM); snowflakeImage = drawable2Bitmap(typedArray.getDrawable(R.styleable.SnowfallView_snowflakeImage)); snowflakeAlphaMin = typedArray.getInt(R.styleable.SnowfallView_snowflakeAlphaMin, DEFAULT_SNOWFLAKE_ALPHA_MIN); snowflakeAlphaMax = typedArray.getInt(R.styleable.SnowfallView_snowflakeAlphaMax, DEFAULT_SNOWFLAKE_ALPHA_MAX); snowflakeAngleMax = typedArray.getInt(R.styleable.SnowfallView_snowflakeAngleMax, DEFAULT_SNOWFLAKE_ANGLE_MAX); snowflakeSizeMinInPx = typedArray.getDimensionPixelSize(R.styleable.SnowfallView_snowflakeSizeMin, dp2Px(DEFAULT_SNOWFLAKE_SIZE_MIN_IN_DP)); snowflakeSizeMaxInPx = typedArray.getDimensionPixelSize(R.styleable.SnowfallView_snowflakeSizeMax, dp2Px(DEFAULT_SNOWFLAKE_SIZE_MAX_IN_DP)); snowflakeSpeedMin = typedArray.getInt(R.styleable.SnowfallView_snowflakeSpeedMin, DEFAULT_SNOWFLAKE_SPEED_MIN); snowflakeSpeedMax = typedArray.getInt(R.styleable.SnowfallView_snowflakeSpeedMax, DEFAULT_SNOWFLAKE_SPEED_MAX); snowflakesFadingEnabled = typedArray.getBoolean(R.styleable.SnowfallView_snowflakesFadingEnabled, DEFAULT_SNOWFLAKES_FADING_ENABLED); snowflakesAlreadyFalling = typedArray.getBoolean(R.styleable.SnowfallView_snowflakesAlreadyFalling, DEFAULT_SNOWFLAKES_ALREADY_FALLING); typedArray.recycle(); holder = this.getHolder(); holder.addCallback(this); //設置背景為透明 setZOrderOnTop(true); holder.setFormat(PixelFormat.TRANSPARENT); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //獲取雪花集合 for (int i=0;i<snowflakesNum;i++){ list.add(new Snowflake(w, h, snowflakeImage, snowflakeAlphaMin, snowflakeAlphaMax , snowflakeAngleMax, snowflakeSizeMinInPx, snowflakeSizeMaxInPx, snowflakeSpeedMin, snowflakeSpeedMax , snowflakesFadingEnabled, snowflakesAlreadyFalling)); } } @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (changedView==this&&visibility==GONE){ //初始化雪花類 try { for (Snowflake snowflake :list){ snowflake.reset(); } }catch (Exception e){ e.printStackTrace(); } } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isInEditMode()){ return; } } /** * dp轉(zhuǎn)px * @param dp * @return */ private int dp2Px(int dp){ return (int) (dp*getResources().getDisplayMetrics().density); } /** * drawble轉(zhuǎn)Bitmap * @param drawable * @return */ private Bitmap drawable2Bitmap(Drawable drawable){ Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { if (myThread==null){ myThread = new Thread(myRunnable); } if(!myThread.isAlive()){ myThread.start(); } } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { if (myThread!=null){ myThread.interrupt(); } } public class myRunnable implements Runnable { @Override public void run() { while (true){ Canvas canvas =null; try { synchronized (holder){ canvas = holder.lockCanvas(); //清除畫布 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); for (Snowflake snowflake :list){ snowflake.draw(canvas); snowflake.update(); } } }catch (Exception e){ e.printStackTrace(); }finally { if (canvas!=null){ holder.unlockCanvasAndPost(canvas); } } } } } }
雪花類Snowflake
public class Snowflake { private int parentWidth; private int parentHeight; private int alphaMin; private int alphaMax; private int angleMax; private int sizeMinInPx; private int sizeMaxInPx; private int speedMin; private int speedMax; private Bitmap image; private boolean fadingEnabled; private boolean alreadyFalling; private int size = 0 ; private int alpha = 255; private Bitmap bitmap = null; private double speedX= 0.0; private double speedY = 0.0; private double positionX = 0.0; private double positionY = 0.0; private final Randomizer randomizer; private Paint paint; public Snowflake(int parentWidth, int parentHeight, Bitmap image ,int alphaMin,int alphaMax,int angleMax,int sizeMinInPx,int sizeMaxInPx, int speedMin,int speedMax,boolean fadingEnabled,boolean alreadyFalling ){ this.parentWidth = parentWidth; this.parentHeight = parentHeight; this.alphaMin = alphaMin; this.alphaMax = alphaMax; this.angleMax = angleMax; this.sizeMinInPx = sizeMinInPx; this.sizeMaxInPx = sizeMaxInPx; this.speedMin = speedMin; this.speedMax = speedMax; this.image = image; this.fadingEnabled=fadingEnabled; this.alreadyFalling=alreadyFalling; randomizer = new Randomizer(); initPaint(); reset(); } /** * 初始化畫筆 */ private void initPaint() { paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.rgb(255,255,255)); paint.setStyle(Paint.Style.FILL); } public void reset(double positionY){ size = randomizer.randomInt(sizeMinInPx, sizeMaxInPx, true); if (image!=null){ if (bitmap==null){ bitmap = Bitmap.createScaledBitmap(image, size, size, false); } } float speed = (float)(size - sizeMinInPx) / (sizeMaxInPx - sizeMinInPx) * (speedMax - speedMin) + speedMin; double angle = Math.toRadians(randomizer.randomDouble(alphaMax) * randomizer.randomSignum()); if (angle<-1||angle>1){ angle = 0; } speedX = speed* Math.sin(angle); speedY = speed* Math.cos(angle); alpha = randomizer.randomInt(alphaMin, alphaMax, false); paint.setAlpha(alpha); positionX = randomizer.randomDouble(parentWidth); this.positionY = positionY; } public void reset(){ size = randomizer.randomInt(sizeMinInPx, sizeMaxInPx, true); if (image!=null){ if (bitmap==null){ bitmap = Bitmap.createScaledBitmap(image, size, size, false); } } float speed = (float)(size - sizeMinInPx) / (sizeMaxInPx - sizeMinInPx) * (speedMax - speedMin) + speedMin; double angle = Math.toRadians(randomizer.randomDouble(angleMax) * randomizer.randomSignum()); speedX = speed* Math.sin(angle); speedY = speed* Math.cos(angle); alpha = randomizer.randomInt(alphaMin, alphaMax, false); paint.setAlpha(alpha); positionX = randomizer.randomDouble(parentWidth); this.positionY=randomizer.randomDouble(parentHeight); if (!alreadyFalling){ this.positionY = this.positionY-parentHeight-size; } } public void update(){ positionX = positionX+speedX; positionY = positionY+speedY; if (positionY>parentHeight){ positionY = -(double)size; reset(positionY); } if (fadingEnabled){ paint.setAlpha((int) (alpha * ((float) (parentHeight - positionY) / parentHeight))); } } public void draw(Canvas canvas){ if (bitmap!=null){ canvas.drawBitmap(bitmap,(float)positionX,(float)positionY,paint); }else { canvas.drawCircle((float)positionX,(float)positionY,(float)size,paint); } } }
總結(jié)
到此這篇關(guān)于Android自定義view實現(xiàn)雪花特效的文章就介紹到這了,更多相關(guān)Android實現(xiàn)雪花特效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android開發(fā)方式之Java+html+javascript混合開發(fā)
這篇文章主要為大家詳細介紹了Android開發(fā)方式的其中一種Java+html+javascript混合開發(fā),感興趣的小伙伴們可以參考一下2016-06-06淺析Android圓形進度條ProgressBar如何實現(xiàn)固定進度
之前遇到一個問題,發(fā)現(xiàn)Android里的圓形進度條無法固定一個進度,所以這篇文章就來和大家探索一下圓形進度條ProgressBar如何實現(xiàn)固定進度,希望對大家有所幫助2024-03-03解決Eclipse啟動出錯:Failed to create the Java Virtual Machine
這篇文章主要介紹了解決Eclipse啟動出錯:Failed to create the Java Virtual Machine的相關(guān)資料,這里說明出錯原因及查找錯誤和解決辦法,需要的朋友可以參考下2017-07-07Android 自定義圓形頭像CircleImageView支持加載網(wǎng)絡圖片的實現(xiàn)代碼
這篇文章主要介紹了Android 自定義圓形頭像CircleImageView支持加載網(wǎng)絡圖片的實現(xiàn)代碼,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-10-10完美解決關(guān)于禁止ViewPager預加載的相關(guān)問題
本篇文章主要介紹了完美解決關(guān)于禁止ViewPager預加載的相關(guān)問題,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09