android 水纹上涨与水滴滴下效果
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 水纹上涨与水滴滴下效果相关的知识,希望对你有一定的参考价值。
这两天项目比较紧,本来打算完成以后再写博客的,今天终于实现了一个炫的功能,怀着激动的心情,趁热打铁,把项目经验记录一下,效果图如下:
对功能的几点说明:
1.圆形边框旋转
2.水纹上涨
3.水滴滴下
4.方向始终朝向重力
先分析一下思路,首先旋转比较好实现,只需要旋转动画就可以了,第二个水纹效果,参考了一篇博客,http://www.jianshu.com/p/e711e22e053e
在源码基础上进行了修改。第三个水滴滴下效果,参考了这个源码: https://github.com/recruit-lifestyle/WaveSwipeRefreshLayout
并且根据功能在源码基础上进行了修改,第四个方向始终朝向重力方向,需要判断重力方向,然后根据角度旋转图片,感谢这篇博客,里边角度的换算非常有用, http://www.cnblogs.com/bpasser/archive/2011/10/17/2214517.html
现在就一步一步实现这个功能吧
布局文件
现在先把这块功能的布局文件贴上,所有操作都在这个布局上进行。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:id="@+id/id_framelayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@mipmap/wave_default">
<ImageView
android:id="@+id/id_imageRota"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@mipmap/wave_1" />
<jp.co.recruit_lifestyle.android.widget.WaveSwipeRefreshLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/main_swipe"
android:layout_gravity="center"
>
<View
android:layout_width="1dp"
android:layout_height="1dp"
/>
</jp.co.recruit_lifestyle.android.widget.WaveSwipeRefreshLayout>
<com.gelitenight.waveview.library.WaveView
android:id="@+id/wave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<TextView
android:id="@+id/id_textViewProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="40sp"
android:textColor="#5aa828"
android:textStyle="bold"/>
</FrameLayout>
</LinearLayout>
先解释一下布局,两张图片:wave_default,
wave_1
首先使用FrameLayout布局,将两个图片重叠,对wave_1进行旋转操作,实现动画。WaveSwipeRefreshLayout控件是水滴效果,因为不允许布局内为空,所以设置一个view,WaveView则是水滴效果,id_textViewProgress是水滴上写的进度数字,说明一下,这里都写成自适应的,wrap_content,具体宽高则在代码中进行控制。
旋转图片的实现
这个比较简单,只需要给view进行旋转动画的操作就可以了,贴一下这部分功能的代码
final RotateAnimation ra = new RotateAnimation(0, 359, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
ra.setDuration(1000);
ra.setRepeatCount(-1);
//匀速
LinearInterpolator lin = new LinearInterpolator();
ra.setInterpolator(lin);
imageViewRota.startAnimation(ra);
我这里设置了匀速旋转
水波效果的实现
这个需要导入第三方库,导入方法我就不说了,然后在源代码基础上进行修改,具体的效果是这样的,颜色和透明度之类的,在源代码中进行修改,修改的部分比较多,我把修改后的源码贴一下
public class WaveView extends View {
/**
* +------------------------+
* |<--wave length-> |______
* | /\\ | /\\ | |
* | / \\ | / \\ | amplitude
* | / \\ | / \\ | |
* |/ \\ |/ \\|__|____
* | \\ / | |
* | \\ / | |
* | \\ / | |
* | \\/ | water level
* | | |
* | | |
* +------------------------+__|____
*/
private static final float DEFAULT_AMPLITUDE_RATIO = 0.05f;
private static final float DEFAULT_WATER_LEVEL_RATIO = 0.5f;
private static final float DEFAULT_WAVE_LENGTH_RATIO = 1.0f;
private static final float DEFAULT_WAVE_SHIFT_RATIO = 0.0f;
public static final int DEFAULT_BEHIND_WAVE_COLOR = Color.parseColor("#addef8");
public static final int DEFAULT_FRONT_WAVE_COLOR = Color.parseColor("#addef8");
public static final ShapeType DEFAULT_WAVE_SHAPE = ShapeType.CIRCLE;
public enum ShapeType {
CIRCLE,
SQUARE
}
// if true, the shader will display the wave
private boolean mShowWave;
// shader containing repeated waves
private BitmapShader mWaveShader;
// shader matrix
private Matrix mShaderMatrix;
// paint to draw wave
private Paint mViewPaint;
// paint to draw border
private Paint mBorderPaint;
private float mDefaultAmplitude;
private float mDefaultWaterLevel;
private float mDefaultWaveLength;
private double mDefaultAngularFrequency;
private float mAmplitudeRatio = DEFAULT_AMPLITUDE_RATIO;
private float mWaveLengthRatio = DEFAULT_WAVE_LENGTH_RATIO;
private float mWaterLevelRatio = DEFAULT_WATER_LEVEL_RATIO;
private float mWaveShiftRatio = DEFAULT_WAVE_SHIFT_RATIO;
private int mBehindWaveColor = DEFAULT_BEHIND_WAVE_COLOR;
private int mFrontWaveColor = DEFAULT_FRONT_WAVE_COLOR;
private ShapeType mShapeType = DEFAULT_WAVE_SHAPE;
public WaveView(Context context) {
super(context);
init();
}
public WaveView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public WaveView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mShaderMatrix = new Matrix();
mViewPaint = new Paint();
mViewPaint.setAntiAlias(true);
}
public float getWaveShiftRatio() {
return mWaveShiftRatio;
}
/**
* Shift the wave horizontally according to <code>waveShiftRatio</code>.
*
* @param waveShiftRatio Should be 0 ~ 1. Default to be 0.
* <br/>Result of waveShiftRatio multiples width of WaveView is the length to shift.
*/
public void setWaveShiftRatio(float waveShiftRatio) {
if (mWaveShiftRatio != waveShiftRatio) {
mWaveShiftRatio = waveShiftRatio;
invalidate();
}
}
public float getWaterLevelRatio() {
return mWaterLevelRatio;
}
/**
* Set water level according to <code>waterLevelRatio</code>.
*
* @param waterLevelRatio Should be 0 ~ 1. Default to be 0.5.
* <br/>Ratio of water level to WaveView height.
*/
public void setWaterLevelRatio(float waterLevelRatio){
if (mWaterLevelRatio != waterLevelRatio) {
mWaterLevelRatio = waterLevelRatio;
invalidate();
}
}
public float getAmplitudeRatio() {
return mAmplitudeRatio;
}
/**
* Set vertical size of wave according to <code>amplitudeRatio</code>
*
* @param amplitudeRatio Default to be 0.05. Result of amplitudeRatio + waterLevelRatio should be less than 1.
* <br/>Ratio of amplitude to height of WaveView.
*/
public void setAmplitudeRatio(float amplitudeRatio) {
if (mAmplitudeRatio != amplitudeRatio) {
mAmplitudeRatio = amplitudeRatio;
invalidate();
}
}
public float getWaveLengthRatio() {
return mWaveLengthRatio;
}
/**
* Set horizontal size of wave according to <code>waveLengthRatio</code>
*
* @param waveLengthRatio Default to be 1.
* <br/>Ratio of wave length to width of WaveView.
*/
public void setWaveLengthRatio(float waveLengthRatio) {
mWaveLengthRatio = waveLengthRatio;
}
public boolean isShowWave() {
return mShowWave;
}
public void setShowWave(boolean showWave) {
mShowWave = showWave;
}
public void setBorder(int width, int color) {
if (mBorderPaint == null) {
mBorderPaint = new Paint();
mBorderPaint.setAntiAlias(true);
mBorderPaint.setStyle(Style.STROKE);
}
mBorderPaint.setColor(color);
mBorderPaint.setStrokeWidth(width);
invalidate();
}
public void setWaveColor(int behindWaveColor, int frontWaveColor) {
mBehindWaveColor = behindWaveColor;
mFrontWaveColor = frontWaveColor;
// need to recreate shader when color changed
mWaveShader = null;
createShader();
invalidate();
}
public void setShapeType(ShapeType shapeType) {
mShapeType = shapeType;
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
createShader();
}
/**
* Create the shader with default waves which repeat horizontally, and clamp vertically
*/
private void createShader() {
mDefaultAngularFrequency = 2.0f * Math.PI / DEFAULT_WAVE_LENGTH_RATIO / getWidth();
mDefaultAmplitude = getHeight() * DEFAULT_AMPLITUDE_RATIO;
mDefaultWaterLevel = getHeight() * DEFAULT_WATER_LEVEL_RATIO;
mDefaultWaveLength = getWidth();
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint wavePaint = new Paint();
wavePaint.setStrokeWidth(2);
wavePaint.setAntiAlias(true);
wavePaint.setAlpha(178);
// Draw default waves into the bitmap
// y=Asin(ωx+φ)+h
final int endX = getWidth() + 1;
final int endY = getHeight() + 1;
float[] waveY = new float[endX];
wavePaint.setColor(mBehindWaveColor);
wavePaint.setAlpha(127);
for (int beginX = 0; beginX < endX; beginX++) {
double wx = beginX * mDefaultAngularFrequency;
float beginY = (float) (mDefaultWaterLevel + mDefaultAmplitude * Math.sin(wx));
canvas.drawLine(beginX, beginY, beginX, endY, wavePaint);
waveY[beginX] = beginY;
}
wavePaint.setColor(mFrontWaveColor);
final int wave2Shift = (int) (mDefaultWaveLength / 4);
for (int beginX = 0; beginX < endX; beginX++) {
canvas.drawLine(beginX, waveY[(beginX + wave2Shift) % endX], beginX, endY, wavePaint);
}
// use the bitamp to create the shader
mWaveShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
mViewPaint.setShader(mWaveShader);
}
@Override
protected void onDraw(Canvas canvas) {
// modify paint shader according to mShowWave state
if (mShowWave && mWaveShader != null) {
// first call after mShowWave, assign it to our paint
if (mViewPaint.getShader() == null) {
mViewPaint.setShader(mWaveShader);
}
// sacle shader according to mWaveLengthRatio and mAmplitudeRatio
// this decides the size(mWaveLengthRatio for width, mAmplitudeRatio for height) of waves
mShaderMatrix.setScale(
mWaveLengthRatio / DEFAULT_WAVE_LENGTH_RATIO,
mAmplitudeRatio / DEFAULT_AMPLITUDE_RATIO,
0,
mDefaultWaterLevel);
// translate shader according to mWaveShiftRatio and mWaterLevelRatio
// this decides the start position(mWaveShiftRatio for x, mWaterLevelRatio for y) of waves
mShaderMatrix.postTranslate(
mWaveShiftRatio * getWidth(),
(DEFAULT_WATER_LEVEL_RATIO - mWaterLevelRatio) * getHeight());
// assign matrix to invalidate the shader
mWaveShader.setLocalMatrix(mShaderMatrix);
float borderWidth = mBorderPaint == null ? 0f : mBorderPaint.getStrokeWidth();
switch (mShapeType) {
case CIRCLE:
if (borderWidth > 0) {
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f,
(getWidth() - borderWidth) / 2f - 1f, mBorderPaint);
}
float radius = getWidth() / 2f - borderWidth;
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, mViewPaint);
break;
case SQUARE:
if (borderWidth > 0) {
canvas.drawRect(
borderWidth / 2f,
borderWidth / 2f,
getWidth() - borderWidth / 2f -