|
Two days before we head to the side of me that there is a gif dynamic efficiency is very good, can be considered to do the project in the Loading, I asked if I could achieve, looked at the effect is really good, there is still relatively new, complexity is not very high, so we took the time to do it, let's look together original gif Figure effect:
In effect, we need to consider the following questions:
1. leaves randomly generated;
2. With leaves a cosine curve shifts;
3. leaves move when the rotation, random rotation direction, clockwise or counterclockwise positive;
4. leaves encountered progress bar seems to be a fusion enter;
5. Leaves not exceed the leftmost arc angle;
7. When the leaf angle smell is not the same, the amplitude of the curve to go there are differences, otherwise too much regularity, the lack of beauty;
Total look, you need to pay attention and troublesome place mainly above points, of course, there are more details, such as the far left is a circular arc, and so on;
Then the next we will effect decomposition, then one by one break:
The whole effect, what we need is a diagram showing the main flying leaves small and right rotation of the fan, the other part can be drawn with the color value, of course, we have to facilitate, and even cut the bottom of the box together;
Start gif Turi the little leaves and the right of flying rotating fan, pull out the bottom of the box
There are two parts we need to address:
1. With the progress of the forward draw a progress bar;
2. continue to fly out of small leaves;
Let's deal with the first part - drawn forward with the progress of the progress bar:
The position of the progress bar is calculated based on the outer layer of the incoming progress, the figure can be divided into three stages 1,2,3:
1. When progress is small, the current calculated distance is still within the arc, we need to draw an arc as shown in Figure 1 region, and the rest is filled with white;
2. When the progress calculated distance to 2, we need to draw a brown semi-circular arc, and the rest is filled with white rectangles;
3. When the progress calculated distance to 3:00, we need to draw a brown semi-circular arc, rectangle brown, white rectangle;
4. When the progress calculated distance to the end, we need to draw a semi-circular arc brown, brown rectangle; (can be combined to 3)
First, according to the width of the current progress and the progress of the overall progress of the calculated current location:
// MProgressWidth for the progress bar width, according to the current schedule calculates the position of the progress bar
mCurrentProgressPosition = mProgressWidth * mProgress / TOTAL_PROGRESS;
Then follow the above logic draw, which need to calculate the angle of the image above the red arc, calculated as follows:
// Unilateral angle
int angle = (int) Math.toDegrees (Math.acos ((mArcRadius - mCurrentProgressPosition) / (float) mArcRadius));
Math.acos () - cosine function;
Math.toDegrees () - converted to angle in radians, Math.toRadians angle is converted to radians
So the starting point of the arc as follows:
int startAngle = 180 - angle;
Across the arc angle:
2 * angle
This piece of code is as follows:
// MProgressWidth for the progress bar width, according to the current schedule calculates the position of the progress bar
mCurrentProgressPosition = mProgressWidth * mProgress / TOTAL_PROGRESS;
// That is the current position within the range shown in FIG. 1
if (mCurrentProgressPosition
Log.i (TAG, "mProgress =" + mProgress + "--- mCurrentProgressPosition ="
+ MCurrentProgressPosition
+ "--mArcProgressWidth" + MArcRadius);
// 1. Draw white ARC, draw orange ARC
// 2. Draw a white rectangle
// 1. Draw a white ARC
canvas.drawArc (mArcRectF, 90, 180, false, mWhitePaint);
// 2. Draw a white rectangle
mWhiteRectF.left = mArcRightLocation;
canvas.drawRect (mWhiteRectF, mWhitePaint);
// 3. Draw brown ARC
// Unilateral angle
int angle = (int) Math.toDegrees (Math.acos ((mArcRadius - mCurrentProgressPosition)
/ (Float) mArcRadius));
// Starting position
int startAngle = 180 - angle;
// Sweep angle
int sweepAngle = 2 * angle;
Log.i (TAG, "startAngle =" + startAngle);
canvas.drawArc (mArcRectF, startAngle, sweepAngle, false, mOrangePaint);
} Else {
Log.i (TAG, "mProgress =" + mProgress + "--- transfer ----- mCurrentProgressPosition ="
+ MCurrentProgressPosition
+ "--mArcProgressWidth" + MArcRadius);
// 1. Draw white RECT
// 2. Draw Orange ARC
// 3. Draw orange RECT
// 1. Draw white RECT
mWhiteRectF.left = mCurrentProgressPosition;
canvas.drawRect (mWhiteRectF, mWhitePaint);
// 2. Draw Orange ARC
canvas.drawArc (mArcRectF, 90, 180, false, mOrangePaint);
// 3. Draw orange RECT
mOrangeRectF.left = mArcRightLocation;
mOrangeRectF.right = mCurrentProgressPosition;
canvas.drawRect (mOrangeRectF, mOrangePaint);
}
Next, look at the leaves part:
First, the basic situation is determined based on the performance of the curve function, standard function equation: y = A (wx + Q) + h, where w affect the cycle, A affect the amplitude, period T = 2 * Math.PI / w;
According to the effect it can be seen that the total cycle time is approximately the length of the schedule, so make sure w = (float) ((float) 2 * Math.PI / mProgressWidth);
Careful observation results, we can see that the leaves fluttering in the process of the amplitude is not exactly the same, resulting in a scattered effect, in that case, we give a definition of the leaves Type, determine different amplitudes according to Type;
We create a leaf objects:
private class Leaf {
// Draw the position of the
float x, y;
// Control the amplitude of the leaves flutter
StartType type;
// Rotation angle
int rotateAngle;
// Rotate clockwise direction --0 representatives, 1 counterclockwise
int rotateDirection;
// Start time (ms)
long startTime;
}
Using enumeration type to define, in fact, it used to distinguish between different amplitudes:
private enum StartType {
LITTLE, MIDDLE, BIG
}
Create a LeafFactory class is used to create one or more leaf information:
private class LeafFactory {
private static final int MAX_LEAFS = 6;
Random random = new Random ();
// Generate a message leaves
public Leaf generateLeaf () {
Leaf leaf = new Leaf ();
int randomType = random.nextInt (3);
// Type at any time - amplitude random
StartType type = StartType.MIDDLE;
switch (randomType) {
case 0:
break;
case 1:
type = StartType.LITTLE;
break;
case 2:
type = StartType.BIG;
break;
default:
break;
}
leaf.type = type;
// Random starting rotation angle
leaf.rotateAngle = random.nextInt (360);
// Random rotation direction (clockwise or counterclockwise)
leaf.rotateDirection = random.nextInt (2);
// To create the staggering feeling, so that the start time of a certain randomness
mAddTime + = random.nextInt ((int) (LEAF_FLOAT_TIME * 1.5));
leaf.startTime = System.currentTimeMillis () + mAddTime;
return leaf;
}
// Generate information based on the maximum number of leaf foliage
public List generateLeafs () {
return generateLeafs (MAX_LEAFS);
}
// Generate information based on the number of leaf foliage incoming
public List generateLeafs (int leafSize) {
List leafs = new LinkedList ();
for (int i = 0; i
leafs.add (generateLeaf ());
}
return leafs;
}
}
Always define two were recorded moderate amplitude and the amplitude difference between:
// Moderate amplitude
private static final int MIDDLE_AMPLITUDE = 13;
// Amplitude disparity between different types
private static final int AMPLITUDE_DISPARITY = 5;
// Moderate amplitude
private int mMiddleAmplitude = MIDDLE_AMPLITUDE;
// Amplitude difference
private int mAmplitudeDisparity = AMPLITUDE_DISPARITY;
With the above information, we can get to the Y value leaves:
// Get the current value of the Y leaf by leaf information
private int getLocationY (Leaf leaf) {
// Y = A (wx + Q) + h
float w = (float) ((float) 2 * Math.PI / mProgressWidth);
float a = mMiddleAmplitude;
switch (leaf.type) {
case LITTLE:
// Small amplitude = Moderate amplitude - the amplitude difference
a = mMiddleAmplitude - mAmplitudeDisparity;
break;
case MIDDLE:
a = mMiddleAmplitude;
break;
case BIG:
// Small amplitude amplitude + = moderate amplitude difference
a = mMiddleAmplitude + mAmplitudeDisparity;
break;
default:
break;
}
Log.i (TAG, "--- a =" + a + "--- w =" + w + "--leaf.x =" + leaf.x);
return (int) (a * Math.sin (w * leaf.x)) + mArcRadius * 2/3;
}
Next, we start drawing the leaf:
/ **
* Draw leaves
*
* @param Canvas
* /
private void drawLeafs (Canvas canvas) {
long currentTime = System.currentTimeMillis ();
for (int i = 0; i
Leaf leaf = mLeafInfos.get (i);
if (currentTime> leaf.startTime && leaf.startTime! = 0) {
// Draw leaf - derived leaf leaves depending on the type and the current time (x, y)
getLeafLocation (leaf, currentTime);
// Calculate the angle of rotation according to the time
canvas.save ();
// Leaf rotates through Matrix control
Matrix matrix = new Matrix ();
float transX = mLeftMargin + leaf.x;
float transY = mLeftMargin + leaf.y;
Log.i (TAG, "left.x =" + leaf.x + "--leaf.y =" + leaf.y);
matrix.postTranslate (transX, transY);
// By the time associated with the rotation angle, you can directly LEAF_ROTATE_TIME adjusted by modifying the speed of rotation of the leaf
float rotateFraction = ((currentTime - leaf.startTime)% LEAF_ROTATE_TIME)
/ (Float) LEAF_ROTATE_TIME;
int angle = (int) (rotateFraction * 360);
// Determine the rotation angle of the leaves according to the rotational direction of the leaves
int rotate = leaf.rotateDirection == 0 angle + leaf.rotateAngle:? -angle
+ Leaf.rotateAngle;
matrix.postRotate (rotate, transX
+ MLeafWidth / 2, transY + mLeafHeight / 2);
canvas.drawBitmap (mLeafBitmap, matrix, mBitmapPaint);
canvas.restore ();
} Else {
continue;
}
}
}
Finally, the outer layer is exposed to several interfaces:
/ **
* Set moderate amplitude
*
* @param Amplitude
* /
public void setMiddleAmplitude (int amplitude) {
this.mMiddleAmplitude = amplitude;
}
/ **
* Set the amplitude difference
*
* @param Disparity
* /
public void setMplitudeDisparity (int disparity) {
this.mAmplitudeDisparity = disparity;
}
/ **
* Get moderate amplitude
*
* @param Amplitude
* /
public int getMiddleAmplitude () {
return mMiddleAmplitude;
}
/ **
* Get the amplitude difference
*
* @param Disparity
* /
public int getMplitudeDisparity () {
return mAmplitudeDisparity;
}
/ **
* Setting the pace
*
* @param Progress
* /
public void setProgress (int progress) {
this.mProgress = progress;
postInvalidate ();
}
/ **
* Set a leaf floating End cycle time spent
*
* @param Time
* /
public void setLeafFloatTime (long time) {
this.mLeafFloatTime = time;
}
/ **
* Set rotation leaves the time spent
*
* @param Time
* /
public void setLeafRotateTime (long time) {
this.mLeafRotateTime = time;
These interfaces are used to doing it? For our dynamic efficiency made entirely manually adjustable, doing what good is it?
1. products easier, shoot wet chicken viewing, avoid YY, to manually adjust, you will not want to appear over and over again to change the parameters of the installation, view, change again, and then view the ... ... N times later said, "This seems not what I want "- earth shattering moment, incredibly hard, the feeling of being abandoned by the world;
2. Easy reflect your consideration is a comprehensive, careful thinking, will be programmed, will design artists, of course, this is purely YY, mainly to facilitate;
As a result, they only need to wet chicken shoot continuously adjusted in real time to see the show effect, and finally just need to come to a final parameter feedback, everything is all right, once and for all;
Of course, if the other party is a beautiful sister, and you suffer no chance to strike up a conversation, when I did not say the above, do not enjoy the required write it, she would come to you, and maybe even have, in turn, invited ... ...
Well, Closer to home, the finishing touches part, we get all the parameters can be adjusted up:
The rest of the layout and activity posted:
activity:
public class LeafLoadingActivity extends Activity implements OnSeekBarChangeListener,
OnClickListener {
Handler mHandler = new Handler () {
public void handleMessage (Message msg) {
switch (msg.what) {
case REFRESH_PROGRESS:
if (mProgress <40) {
mProgress + = 1;
// Random within 800ms refresh
mHandler.sendEmptyMessageDelayed (REFRESH_PROGRESS,
new Random () nextInt (800)).;
mLeafLoadingView.setProgress (mProgress);
} Else {
mProgress + = 1;
// Random within 1200ms refresh
mHandler.sendEmptyMessageDelayed (REFRESH_PROGRESS,
new Random () nextInt (1200)).;
mLeafLoadingView.setProgress (mProgress);
}
break;
default:
break;
}
};
};
private static final int REFRESH_PROGRESS = 0x10;
private LeafLoadingView mLeafLoadingView;
private SeekBar mAmpireSeekBar;
private SeekBar mDistanceSeekBar;
private TextView mMplitudeText;
private TextView mDisparityText;
private View mFanView;
private Button mClearButton;
private int mProgress = 0;
private TextView mProgressText;
private View mAddProgress;
private SeekBar mFloatTimeSeekBar;
private SeekBar mRotateTimeSeekBar;
private TextView mFloatTimeText;
private TextView mRotateTimeText;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.leaf_loading_layout);
initViews ();
mHandler.sendEmptyMessageDelayed (REFRESH_PROGRESS, 3000);
}
private void initViews () {
mFanView = findViewById (R.id.fan_pic);
RotateAnimation rotateAnimation = DXAnimationUtils.initRotateAnimation (false, 1500, true,
Animation.INFINITE);
mFanView.startAnimation (rotateAnimation);
mClearButton = (Button) findViewById (R.id.clear_progress);
mClearButton.setOnClickListener (this);
mLeafLoadingView = (LeafLoadingView) findViewById (R.id.leaf_loading);
mMplitudeText = (TextView) findViewById (R.id.text_ampair);
mMplitudeText.setText (getString (R.string.current_mplitude,
mLeafLoadingView.getMiddleAmplitude ()));
mDisparityText = (TextView) findViewById (R.id.text_disparity);
mDisparityText.setText (getString (R.string.current_Disparity,
mLeafLoadingView.getMplitudeDisparity ()));
mAmpireSeekBar = (SeekBar) findViewById (R.id.seekBar_ampair);
mAmpireSeekBar.setOnSeekBarChangeListener (this);
mAmpireSeekBar.setProgress (mLeafLoadingView.getMiddleAmplitude ());
mAmpireSeekBar.setMax (50);
mDistanceSeekBar = (SeekBar) findViewById (R.id.seekBar_distance);
mDistanceSeekBar.setOnSeekBarChangeListener (this);
mDistanceSeekBar.setProgress (mLeafLoadingView.getMplitudeDisparity ());
mDistanceSeekBar.setMax (20);
mAddProgress = findViewById (R.id.add_progress);
mAddProgress.setOnClickListener (this);
mProgressText = (TextView) findViewById (R.id.text_progress);
mFloatTimeText = (TextView) findViewById (R.id.text_float_time);
mFloatTimeSeekBar = (SeekBar) findViewById (R.id.seekBar_float_time);
mFloatTimeSeekBar.setOnSeekBarChangeListener (this);
mFloatTimeSeekBar.setMax (5000);
mFloatTimeSeekBar.setProgress ((int) mLeafLoadingView.getLeafFloatTime ());
mFloatTimeText.setText (getResources (). getString (R.string.current_float_time,
mLeafLoadingView.getLeafFloatTime ()));
mRotateTimeText = (TextView) findViewById (R.id.text_rotate_time);
mRotateTimeSeekBar = (SeekBar) findViewById (R.id.seekBar_rotate_time);
mRotateTimeSeekBar.setOnSeekBarChangeListener (this);
mRotateTimeSeekBar.setMax (5000);
mRotateTimeSeekBar.setProgress ((int) mLeafLoadingView.getLeafRotateTime ());
mRotateTimeText.setText (getResources (). getString (R.string.current_float_time,
mLeafLoadingView.getLeafRotateTime ()));
}
@Override
public void onProgressChanged (SeekBar seekBar, int progress, boolean fromUser) {
if (seekBar == mAmpireSeekBar) {
mLeafLoadingView.setMiddleAmplitude (progress);
mMplitudeText.setText (getString (R.string.current_mplitude,
progress));
} Else if (seekBar == mDistanceSeekBar) {
mLeafLoadingView.setMplitudeDisparity (progress);
mDisparityText.setText (getString (R.string.current_Disparity,
progress));
} Else if (seekBar == mFloatTimeSeekBar) {
mLeafLoadingView.setLeafFloatTime (progress);
mFloatTimeText.setText (getResources (). getString (R.string.current_float_time,
progress));
}
else if (seekBar == mRotateTimeSeekBar) {
mLeafLoadingView.setLeafRotateTime (progress);
mRotateTimeText.setText (getResources (). getString (R.string.current_rotate_time,
progress));
}
}
@Override
public void onStartTrackingTouch (SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch (SeekBar seekBar) {
}
@Override
public void onClick (View v) {
if (v == mClearButton) {
mLeafLoadingView.setProgress (0);
mHandler.removeCallbacksAndMessages (null);
mProgress = 0;
} Else if (v == mAddProgress) {
mProgress ++;
mLeafLoadingView.setProgress (mProgress);
mProgressText.setText (String.valueOf (mProgress));
}
}
}
layout:
< ? Xml version = "1.0" encoding = "utf-8"?>
< LinearLayout xmlns: Android = "http://schemas.android.com/apk/res/android"
android: layout_width = "match_parent"
android: layout_height = "match_parent"
android: background = "# fed255"
android: orientation = "vertical">
< TextView
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: layout_gravity = "center_horizontal"
android: layout_marginTop = "100dp"
android: text = "loading ..."
android: textColor = "# FFA800"
android: textSize = "30dp" />
< RelativeLayout
android: id = "@ + id / leaf_content"
android: layout_width = "match_parent"
android: layout_height = "wrap_content"
android: layout_marginTop = "50dp">
< Com.baidu.batterysaverDemo.ui.LeafLoadingView
android: id = "@ + id / leaf_loading"
android: layout_width = "302dp"
android: layout_height = "61dp"
android: layout_centerHorizontal = "true" />
< ImageView
android: id = "@ + id / fan_pic"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: layout_alignParentRight = "true"
android: layout_centerVertical = "true"
android: layout_marginRight = "35dp"
android: src = "@ drawable / fengshan" />
< / RelativeLayout>
< ScrollView
android: layout_width = "match_parent"
android: layout_height = "match_parent">
< LinearLayout
android: layout_width = "match_parent"
android: layout_height = "match_parent"
android: orientation = "vertical">
< LinearLayout
android: id = "@ + id / seek_content_one"
android: layout_width = "match_parent"
android: layout_height = "wrap_content"
android: layout_marginLeft = "15dp"
android: layout_marginRight = "15dp"
android: layout_marginTop = "15dp">
< TextView
android: id = "@ + id / text_ampair"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: layout_gravity = "center_vertical"
android: textColor = "# ffffa800"
android: textSize = "15dp" />
< SeekBar
android: id = "@ + id / seekBar_ampair"
android: layout_width = "0dp"
android: layout_height = "wrap_content"
android: layout_marginLeft = "5dp"
android: layout_weight = "1" />
< / LinearLayout>
< LinearLayout
android: layout_width = "match_parent"
android: layout_height = "wrap_content"
android: layout_marginLeft = "15dp"
android: layout_marginRight = "15dp"
android: layout_marginTop = "15dp"
android: orientation = "horizontal">
< TextView
android: id = "@ + id / text_disparity"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: layout_gravity = "center_vertical"
android: textColor = "# ffffa800"
android: textSize = "15dp" />
< SeekBar
android: id = "@ + id / seekBar_distance"
android: layout_width = "0dp"
android: layout_height = "wrap_content"
android: layout_marginLeft = "5dp"
android: layout_weight = "1" />
< / LinearLayout>
< LinearLayout
android: layout_width = "match_parent"
android: layout_height = "wrap_content"
android: layout_marginLeft = "15dp"
android: layout_marginRight = "15dp"
android: layout_marginTop = "15dp"
android: orientation = "horizontal">
< TextView
android: id = "@ + id / text_float_time"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: layout_gravity = "center_vertical"
android: textColor = "# ffffa800"
android: textSize = "15dp" />
< SeekBar
android: id = "@ + id / seekBar_float_time"
android: layout_width = "0dp"
android: layout_height = "wrap_content"
android: layout_marginLeft = "5dp"
android: layout_weight = "1" />
< / LinearLayout>
< LinearLayout
android: layout_width = "match_parent"
android: layout_height = "wrap_content"
android: layout_marginLeft = "15dp"
android: layout_marginRight = "15dp"
android: layout_marginTop = "15dp"
android: orientation = "horizontal">
< TextView
android: id = "@ + id / text_rotate_time"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: layout_gravity = "center_vertical"
android: textColor = "# ffffa800"
android: textSize = "15dp" />
< SeekBar
android: id = "@ + id / seekBar_rotate_time"
android: layout_width = "0dp"
android: layout_height = "wrap_content"
android: layout_marginLeft = "5dp"
android: layout_weight = "1" />
< / LinearLayout>
< Button
android: id = "@ + id / clear_progress"
android: layout_width = "match_parent"
android: layout_height = "wrap_content"
android: layout_marginTop = "15dp"
android: text = "removed progress bar, Fun arc"
android: textSize = "18dp" />
< LinearLayout
android: layout_width = "match_parent"
android: layout_height = "wrap_content"
android: layout_marginLeft = "15dp"
android: layout_marginRight = "15dp"
android: layout_marginTop = "15dp"
android: orientation = "horizontal">
< Button
android: id = "@ + id / add_progress"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: text = "increase in Progress:"
android: textSize = "18dp" />
< TextView
android: id = "@ + id / text_progress"
android: layout_width = "wrap_content"
android: layout_height = "wrap_content"
android: layout_gravity = "center_vertical"
android: textColor = "# ffffa800"
android: textSize = "15dp" />
< / LinearLayout>
< / LinearLayout>
< / ScrollView>
< / LinearLayout>
Final results are as follows, originally recorded 20 + s, but PS can only turn 5s, so we are interested in their own play run: |
|
|
|