frangulyan.dev 24th August 2014, 10:13 AM Junior Member - OP Thanks Meter 10 Posts: 27 Join Date: Aug 2014 Location: Ulm More Info > CONTINUATION of the guide Now lets see how is the mShineGradient created. This is done every time we got a new data from gyroscope: Code: ``` ... // all the magic numbers here and in below function are results of experiments private static final float MAX_ANGLE = (float)(Math.PI / 2.0); private final float mShadowMaxShift = 5.0f * getResources().getDisplayMetrics().density; // 5dp ... public void gyroChanged(float xAngle, float yAngle) { // 1. shadows float loweredMax = MAX_ANGLE / 4; mShadowShiftX = (xAngle / loweredMax) * mShadowMaxShift; mShadowShiftY = (yAngle / loweredMax) * mShadowMaxShift; // put in [-mShadowMaxShift, mShadowMaxShift] range if (mShadowShiftX > mShadowMaxShift) mShadowShiftX = mShadowMaxShift; if (mShadowShiftX < -mShadowMaxShift) mShadowShiftX = -mShadowMaxShift; if (mShadowShiftY > mShadowMaxShift) mShadowShiftY = mShadowMaxShift; if (mShadowShiftY < -mShadowMaxShift) mShadowShiftY = -mShadowMaxShift; // 2. shine float angleX = xAngle / MAX_ANGLE; float angleY = yAngle / MAX_ANGLE; // put in [-1, 1] range if (angleX > 1.0f) angleX = 1.0f; if (angleX < -1.0f) angleX = -1.0f; if (angleY > 1.0f) angleY = 1.0f; if (angleY < -1.0f) angleY = -1.0f; createShineGradient(angleX, angleY); // redraw invalidate(); }``` The numbers and formulas are quite experimental, so you can play around to find the best numbers for your case. The meaning and usage of gyroChanged() function is explained in my previous guide. The basic idea behind is to get the shine position based on device's rotation in X and Y direction. I convert the rotation into a range from -1 to 1 using some max angle that I defined. If both X and Y angles are -1 then the shine line is in the lower left corner of the text, if both are 1 then in upper right corner, otherwise somewhere in between. Here is the createShineGradient() function: Code: ``` ... private static final float SHINE_WIDTH = 0.07f; private static final float SHINE_BLUR_WIDTH = 0.05f; ... private void createShineGradient(float relativeX, float relativeY) { if ((mBounds == null) || (mBounds.width() == 0) || (mBounds.height() == 0)) { mShineGradient = null; return; } // we want to scale the angles' range and take inner part of // length 1 this will speed up the shine without sudden stops final float SPEED_FACTOR = 4.0f; relativeX *= SPEED_FACTOR; relativeY *= SPEED_FACTOR; float boxSize = mBounds.height() * 1.2f; // make the text box a bit bigger float left = mBounds.centerX() - boxSize / 2.0f; float top = mBounds.top; // project the (relativeX, relativeY) point to the diagonal float relative = (relativeX + relativeY) / 2.0f; // shift by 0.5 to get a point from (0, 1) range relative += 0.5f; int[] colors = {mShineNoColor, mShineNoColor, mShineColor, mShineColor, mShineNoColor, mShineNoColor}; float[] positions = {0.0f, clamp(relative - SHINE_WIDTH - SHINE_BLUR_WIDTH), clamp(relative - SHINE_WIDTH), clamp(relative + SHINE_WIDTH), clamp(relative + SHINE_WIDTH + SHINE_BLUR_WIDTH), 1.0f}; mShineGradient = new LinearGradient(left, top + boxSize, left + boxSize, top, colors, positions, Shader.TileMode.CLAMP); } private float clamp(float value) { if (value < 0.0f) { return 0; } if (value > 1.0f) { return 1.0f; } return value; }``` Again, there are a lot of experimental stuff, you might want to play with it to come to a good solution. The LinearGradient shader is explained in my previous guide. However, here we use more colors so that we can have a white stripe in the middle with small color change gradients on borders. The picture below explains everything: The idea is to project the relative angle of the device rotation in X and Y direction to a single point on the View's diagonal through which the shine will pass. This projection should result in continuous and more or less natural movement of the shine line during device rotation, the formula I used is a result of my tries and errors. When a new gradient is created invalidate() is called and the view redraws itself. Finally, to see this code in action, you can have a look at the app itself, its free: Google Play: Direct link: Download It would be nice to get some comments and suggestions, let me know your thoughts! REPLY