Custom sprinkled beans

First on the renderings

The effect of sprinkling beans, a whim, thought the animation was very interesting, so I took the time to write a play

Drawing process:

Define 6 "beans". Each bean has its own attributes, size, throwing speed, etc. then control the direction and state of each bean. The rebound effect uses the differencing device 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));
        //Randomly generate the size of the sphere
        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) {
        //Drop to the right normally. Here, you can also use randomly generated directions. Here, you can fix three on the left and three on the right
        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);
        //Let the ball fall to the left
        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));//Speed value of randomly generated throw

        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);//Move the coordinates first. It's actually changed off Offset value
                beans1.setCx(mWidth / 2 + beans1.getOff());//Refresh X Axis coordinates

                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) {
                //To prevent the sphere from falling to the ground at different levels due to different radius after stopping, unify the horizontal line
                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());//Gravimeter
        va.setDuration(3000);
        va.setRepeatMode(ValueAnimator.RESTART);
        va.start();
    }

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

}

Here, we mainly encapsulate the logic into individual objects, so the view class looks fresh

Here are the beans

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 coordinate*/
    private float cx;
    /**Y coordinate*/
    private float cy;
    /**Offset*/
    private float off;
    /**Randomly generated speed value*/
    private float rand;
    /**Touch edge or not*/
    private int state;
    /**The size of the ball*/
    private float radius;

    /**Move the X-coordinate, and rebound after touching the boundary*/
    public void move(int width){
        if (cx < 0 || state == 1) {//Hit the left edge
            state = 1;
            off += rand;
        } else if (cx >= width || state == 2) {//Touch the right edge
            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;
    }
}

The main logic is concentrated in the move method

The default is to throw it normally, and then bounce back after hitting the edge

Just focus on two methods

 

This is to put the control in a dialog, which I like. Obviously, dialog is not very suitable, or it can be added to the header of the pull-down library. The effect should be good

<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="Loading..."
        android:textColor="@color/color_cccccc"
        android:textSize="@dimen/d43.0" />

</LinearLayout>

 

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

Keywords: Android github

Added by klaibert26 on Fri, 01 May 2020 20:33:28 +0300