自定義灑豆子

来源:https://www.cnblogs.com/LiuZhen/archive/2018/01/23/8337101.html
-Advertisement-
Play Games

先上效果圖 灑豆子的效果,突發奇想,覺得這個動畫挺有意思的,就抽空寫了一個玩玩 繪製流程: 定義6個‘’豆子‘’,每個豆子有各自的屬性,大小,拋出的速度等,然後控制每個的方向和狀態,回彈效果使用差值器 BounceInterpolator package com.fragmentapp.view.b ...


 

 

先上效果圖

灑豆子的效果,突發奇想,覺得這個動畫挺有意思的,就抽空寫了一個玩玩

繪製流程:

  定義6個‘’豆子‘’,每個豆子有各自的屬性,大小,拋出的速度等,然後控制每個的方向和狀態,回彈效果使用差值器 BounceInterpolator

package com.fragmentapp.view.beans;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.BounceInterpolator;

import com.fragmentapp.R;
import com.fragmentapp.helper.RandomUtil;

/**
 * Created by liuzhen on 2017/1/17.
 */

public class BeansView extends View {

    private Paint paint;
    private int mWidth;
    private int mHeight;
    private int top;

    private ValueAnimator va;

    private Beans beans1,beans2,beans3,beans4,beans5,beans6;

    public BeansView(Context context) {
        this(context, null);
    }

    public BeansView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init() {
        Log.e("tag","init");
        setWillNotDraw(false);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(getResources().getColor(R.color.color_ff9c19));
        //隨機生成球體的大小、
        beans1 = new Beans(RandomUtil.random(5,15));
        beans2 = new Beans(RandomUtil.random(5,15));
        beans3 = new Beans(RandomUtil.random(5,15));
        beans4 = new Beans(RandomUtil.random(5,15));
        beans5 = new Beans(RandomUtil.random(5,15));
        beans6 = new Beans(RandomUtil.random(5,15));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            mWidth = getWidth();
            mHeight = getHeight();
            this.top = top;
            startAnim();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //正常向右掉落,這裡也可以利用隨機生成方向,這裡就固定左邊三個右邊三個
        canvas.drawCircle(beans1.getCx(), beans1.getCy(), beans1.getRadius(), paint);
        canvas.drawCircle(beans2.getCx(), beans2.getCy(), beans2.getRadius(), paint);
        canvas.drawCircle(beans3.getCx(), beans3.getCy(), beans3.getRadius(), paint);
        //讓球往左邊掉落
        canvas.drawCircle(-beans4.getCx()+mWidth, beans4.getCy(), beans4.getRadius(), paint);
        canvas.drawCircle(-beans5.getCx()+mWidth, beans5.getCy(), beans5.getRadius(), paint);
        canvas.drawCircle(-beans6.getCx()+mWidth, beans6.getCy(), beans6.getRadius(), paint);

    }

    public void startAnim() {
        if (mWidth == 0) return;

        beans1.setState(0);
        beans1.setOff(0);
        beans1.setRand(RandomUtil.random(20));//隨機生成拋出的速度值

        beans2.setState(0);
        beans2.setOff(0);
        beans2.setRand(RandomUtil.random(20));

        beans3.setState(0);
        beans3.setOff(0);
        beans3.setRand(RandomUtil.random(20));

        beans4.setState(0);
        beans4.setOff(0);
        beans4.setRand(RandomUtil.random(20));

        beans5.setState(0);
        beans5.setOff(0);
        beans5.setRand(RandomUtil.random(20));

        beans6.setState(0);
        beans6.setOff(0);
        beans6.setRand(RandomUtil.random(20));

        va = ValueAnimator.ofFloat(top, mHeight - top);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

                float val = (float)animation.getAnimatedValue();

                beans1.setCy(val);
                beans1.move(mWidth);//先移動坐標,實際上是改變了off偏移量的值
                beans1.setCx(mWidth / 2 + beans1.getOff());//刷新X軸坐標

                beans2.setCy(val);
                beans2.move(mWidth);
                beans2.setCx(mWidth / 2 + beans2.getOff());

                beans3.setCy(val);
                beans3.move(mWidth);
                beans3.setCx(mWidth / 2 + beans3.getOff());

                beans4.setCy(val);
                beans4.move(mWidth);
                beans4.setCx(mWidth / 2 + beans4.getOff());

                beans5.setCy(val);
                beans5.move(mWidth);
                beans5.setCx(mWidth / 2 + beans5.getOff());

                beans6.setCy(val);
                beans6.move(mWidth);
                beans6.setCx(mWidth / 2 + beans6.getOff());

                invalidate();
            }
        });
        va.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {
                //防止停止後球體因為半徑的不一樣而降落到地面的水平不一樣,統一水平線
                beans1.setCy(mHeight - beans1.getRadius());

                beans2.setCy(mHeight - beans2.getRadius());

                beans3.setCy(mHeight - beans3.getRadius());

                beans4.setCy(mHeight - beans4.getRadius());

                beans5.setCy(mHeight - beans5.getRadius());

                beans6.setCy(mHeight - beans6.getRadius());

                invalidate();
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        va.setInterpolator(new BounceInterpolator());//重力差值器
        va.setDuration(3000);
        va.setRepeatMode(ValueAnimator.RESTART);
        va.start();
    }

    public void stopAnim() {
        va.cancel();
        va = null;
    }

}
View Code

這裡主要把邏輯封裝到單獨的對象裡面去了,所以view類看起來很清爽

下麵是豆子類

package com.fragmentapp.view.beans;

import android.util.Log;

/**
 * Created by liuzhen on 2018/1/18.
 */

public class Beans {

    public Beans(){ }

    public Beans(int radius){
        this.radius = radius;
    }

    /**X坐標*/
    private float cx;
    /**Y坐標*/
    private float cy;
    /**偏移量*/
    private float off;
    /**隨機生成的速度值*/
    private float rand;
    /**是否碰到邊緣*/
    private int state;
    /**圓球的大小*/
    private float radius;

    /**移動 X 坐標,並且碰到邊界後回彈*/
    public void move(int width){
        if (cx < 0 || state == 1) {//碰到左邊的邊緣
            state = 1;
            off += rand;
        } else if (cx >= width || state == 2) {//碰到右邊的邊緣
            state = 2;
            off -= rand;
        }else if(state == 0) {
            state = 0;
            off += rand;
        }
//        Log.e("tag","-- cx "+(int)cx  + " width "+width + " state "+state);
    }

    public float getCx() {
        return cx;
    }

    public void setCx(float cx) {
        this.cx = cx;
    }

    public float getOff() {
        return off;
    }

    public void setOff(float off) {
        this.off = off;
    }

    public float getRand() {
        return rand;
    }

    public void setRand(float rand) {
        this.rand = rand;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public float getCy() {
        return cy;
    }

    public void setCy(float cy) {
        this.cy = cy;
    }

    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }
}
View Code

主要邏輯集中在move方法中

預設是正常拋出,然後碰到邊緣後改變狀態往回彈

使用上只關註兩個方法就行了

 

 這裡是把控制項放在了一個dialog裡面,這個看個人喜歡,顯然dialog不是很適合,或者可以加到下拉庫的頭部上去,效果應該不錯

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/shape_dialog_bg"
    android:padding="@dimen/d20.0"
    android:orientation="vertical"
    android:id="@+id/root">

    <com.fragmentapp.view.beans.BeansView
        android:id="@+id/beans"
        android:layout_width="@dimen/d350.0"
        android:layout_height="@dimen/d300.0"
        android:layout_gravity="center_horizontal" />

    <!--<View-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="@dimen/d1.0"-->
        <!--android:background="@color/white"/>-->

    <TextView
        android:id="@+id/tv_val"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="@dimen/d20.0"
        android:text="載入中..."
        android:textColor="@color/color_cccccc"
        android:textSize="@dimen/d43.0" />

</LinearLayout>
View Code

 

GitHub:https://github.com/1024477951/FragmentApp


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 後端開發:1、高級java軟體架構師實戰培訓視頻教程2、大型SpringMVC,Mybatis,Redis,Solr,Nginx,SSM分散式電商項目視頻教程3、Spark Streaming實時流處理項目實戰4、Java校招面試 Google面試官親授5、Java開發企業級許可權管理系統6、Java ...
  • 1、設置密碼 set password for 用戶名@localhost = password('密碼'); 2、取消密碼 set password for 用戶名@localhost = password(''); 3、創建賬號 create user admin identified by ' ...
  • wm_concat(列名)函數,能把指定的列的值,(按照group by 中指定的分隔方法),一個個用逗號鏈接起來,例: shopping表: id goods num 1 蘋果 22 梨子 32 西瓜 41 葡萄 53 香蕉 63 橘子 7 select id,wm_concat(goods) f ...
  • sql server2008資料庫複製實現數據同步常見問題 在原作者基礎上追加 "sql server2008資料庫複製實現數據同步常見問題" 23.發佈 'xx' 的併發快照不可用,因為該快照尚未完全生成,或者日誌讀取器代理未運行,無法激活它。如果併發快照的生成過程中斷,則必須重新啟動用於該發佈的 ...
  • 在mysql5.1以後開始支持事件功能,主要是定期或指定的時間執行一條命令。 mysql預設是關閉事件功能 檢查是否開啟事件功能: show variables llike 'event_scheduler'; 開啟事件: set global event_scheduler = on; 關閉事件: ...
  • SQL的不同版本在Windows環境啟動配置方法不同,此處僅介紹 5.7.20的配置方法; 1、登錄mysql官網下載windows環境下的工具壓縮包 http://dev.mysql.com/downloads/mysql/ 個人電腦是64位的機器,所以此處演示選擇如下的版本 2、解壓到本地電腦的 ...
  • 1.創建函數 delimiter // create function function_name(形參名 數據類型) returns 數據類型 #必須指定返回的數據類型。 begin stmt; end // delimiter ; 2.刪除函數 drop function function_na ...
  • 我錄屏的方式是分別錄製音頻和視頻,最後合併成mp4格式,比較麻煩,因為網上完整的教程比較少,所以我打算寫一個完整版的,照著我的代碼寫完之後,至少是能夠實現功能的,而不是簡單的介紹下用法。 1既然是錄製視頻,我們應該有一個按鈕控制開始和結束。 2在錄製之前,需要先判斷一下Android系統的版本是否大 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...