转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/50789666
本文出自:【顾林海的博客】
####扯蛋篇
这几由于智齿又长了出来,痛了好几天还没好,吃饭又不能吃,加上公司里面操蛋的体制,感觉人活着真累,昨天晚上回到住处,我朋友让我帮忙写个唱片播放器,效果大致上如下:
于是乎,利用一个早上做了下,比较粗糙,毕竟帮别人做的,随便做做得了,当然有很多地方投机取巧了。
####原理篇
在做之前看看我们得需要以下素材:
额!!!这个,这个ic_launcher忽律。
现在我们分析这个播放器的几个播放步骤:
- 当我们杆放到碟子上时,碟子需要旋转
- 在碟子旋转时,向上或是向下滑动淡出(PS:这里面我为了方便,放了两层的碟子View,这样上面滑出时,底下的唱片还是显示的)
- 当我们拨动杆时,杆逐渐移动到唱片上,再次触摸杆时,杆回到原来的位置(PS:这里有个问题,就是杆移动到唱片上时,触摸事件其实还是在原来的位置,这里我们在唱片右下角放了一个透明的View,嘿嘿,投机取巧嘛,也是说,我在原来的位置只处理向右移动的操作,这个透明的View只处理杆归位的操作)
讲了这么多,先看看布局吧
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" >
<FrameLayout
android:layout_width="260dp"
android:layout_height="match_parent"
android:layout_centerInParent="true" >
<FrameLayout
android:id="@+id/fl_dvd"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center" >
<FrameLayout
android:id="@+id/fl_dvd1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" >
<ImageView
android:id="@+id/iv_background1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/turntable_bg_anim_highlight" />
<ImageView
android:id="@+id/iv_center1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/default_album" />
</FrameLayout>
<FrameLayout
android:id="@+id/fl_dvd2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" >
<ImageView
android:id="@+id/iv_background2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/turntable_bg_anim_highlight" />
<ImageView
android:id="@+id/iv_center2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/default_album" />
</FrameLayout>
</FrameLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/turntable_bg" />
<ImageView
android:id="@+id/iv_pole"
android:layout_width="50sp"
android:layout_height="180sp"
android:layout_gravity="right|center"
android:src="@drawable/stylus" />
<View
android:id="@+id/id_pole"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginLeft="65dp"
android:layout_marginTop="45dp" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</RelativeLayout>
这里的id为 fl_dvd1 的FrameLayout是底部的唱片View, id为 fl_dvd2的FrameLayout是上面用来做移出动画的唱片View,id为 id_pole的View就是唱片右下角透明的View,旋转的是id 为 fl_dvd的View。
首先定义一个继承LinearLayout的BaseRecordView类:
public abstract class BaseRecordView extends LinearLayout {}
接着重写三个构造器:
public BaseRecordView(Context context) {
this(context, null);
}
public BaseRecordView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BaseRecordView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
init();
initAnimation();
initEvent();
}
init方法里是载入我们刚定义的布局,并初始化控件:
/**
* 初始化View
*
* @param context
*/
private void init() {
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
LayoutInflater.from(mContext).inflate(R.layout.record_layout, this);
fl_dvd = (FrameLayout) findViewById(R.id.fl_dvd);// 两层 碟片的容器
fl_dvd2 = (FrameLayout) findViewById(R.id.fl_dvd2);// 移出唱片动画的View
iv_background2 = (ImageView) findViewById(R.id.iv_background2);// 黑色的唱片(移出唱片动画的View)
iv_center2 = (ImageView) findViewById(R.id.iv_center2);// 中间的圆心图案(移出唱片动画的View)
iv_pole = (ImageView) findViewById(R.id.iv_pole);// 杆
mPole = findViewById(R.id.id_pole);// 唱片右下角透明的View
}
mTouchSlop 拿到的是当面屏幕滑动的最小距离。
第一步先让整个唱片旋转,也就是 **fl_dvd **这个View进行旋转,可以通过Animation进行播放动画,先配置我们的动画文件:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<rotate
android:duration="2400"
android:fillAfter="true"
android:fillBefore="false"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:toDegrees="359" />
</set>
通过设置repeatCount属性为-1,使唱片无限旋转。定义唱片旋转的动画。
/**
* 碟片的播放动画
*/
private void initDVDAnimation() {
mDVDLin = new LinearInterpolator();
mDVDAnimtion = AnimationUtils.loadAnimation(mContext, R.anim.dvd_anim);
mDVDAnimtion.setInterpolator(mDVDLin);
mDVDAnimtion.setFillAfter(true);
mDVDAnimtion.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
杆的拨动到唱片上的效果,是将我们的图片顺时针旋转20度,那么杆的复位是逆时针旋转20度,定义我们的杆拨打的动画文件:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<rotate
android:duration="300"
android:fromDegrees="0"
android:pivotX="45%"
android:pivotY="10%"
android:repeatCount="0"
android:toDegrees="20" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<rotate
android:duration="300"
android:fromDegrees="0"
android:pivotX="45%"
android:pivotY="10%"
android:repeatCount="0"
android:toDegrees="20" />
</set>
老样子初始化我们杆的动画:
/**
* 杆子启动动画
*/
private void initPoleAnimation() {
mPoleAnimtion = AnimationUtils.loadAnimation(mContext, R.anim.pole_anim);
mPoleLin = new LinearInterpolator();
mPoleAnimtion.setFillAfter(true);
mPoleAnimtion.setInterpolator(mPoleLin);
mPoleAnimtion.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
/*
* 杆子启动完毕,说明碟子应该要转动了
*/
isStart = true;
isPole = true;
startDVDAnimation();
mIRecordListener.start();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 杆复位动画
*/
private void initPoleStopAnimation() {
mPoleAnimtion = AnimationUtils.loadAnimation(mContext,R.anim.pole_rest_anim);
mPoleLin = new LinearInterpolator();
mPoleAnimtion.setFillAfter(true);
mPoleAnimtion.setInterpolator(mPoleLin);
mPoleAnimtion.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
/*
* 杆子复位,说明已经停止播放或者播放完毕
*/
isPole = false;
isStart = false;
stopDVDAnimation();
mIRecordListener.stop();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
上面在杆放到唱片上后,调用startDVDAnimation方法进行唱片的旋转,并通过接口回调通知我们的当前处于播放状态;当杆复位时调用stopDVDAnimation方法进行唱片的停止,并通过接口回调通知我们的当前处于停止状态。
接着添加我们杆的触摸事件,注意还有我们投机取巧的透明View的触摸事件:
/**
* 杆触摸事件
*/
iv_pole.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (!isStart) {
/*
* 当停止播放时,拨动杆,播放
*/
isPole = true;
startPoleAnimation();
}
break;
default:
break;
}
return true;
}
});
/**
* 虚拟位置的触摸
*/
mPole.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (isStart) {
/*
* 当播放时,拨动杆,停止
*/
isPole = false;
stopPoleAnimation();
}
break;
default:
break;
}
return true;
}
});
通过触摸杆和透明的View来决定是否进行杆的复位还是移动到唱片上。
最后是唱片本身触摸的事件处理:
/**
* 碟片的触摸事件
*/
private OnTouchListener mOnTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int oldY = (int) event.getX();
int newY = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
oldY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
newY = (int) event.getY();
stopDVDAnimation();
break;
case MotionEvent.ACTION_UP:
if (isStart && oldY - newY > mTouchSlop && (newY < oldY)) {
up();
} else if (isStart && newY - oldY > mTouchSlop && (newY > oldY)) {
down();
} else if (isPole) {
startDVDAnimation();
}
break;
default:
break;
}
return true;
}};
这里面是滑动时的唱片停止处理,以及向上向下滑动时的处理,小于滑动最小距离并离开屏幕时,唱片重新旋转。
最后给出up和down的方法:
/**
* 碟片向上切歌动画
*/
private void initDVDUpAnimation() {
mUpAnimation = AnimationUtils.loadAnimation(mContext, R.anim.up_anim);
mUpAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mIRecordListener.up();
}
@Override
public void onAnimationEnd(Animation animation) {
fl_dvd2.setVisibility(View.VISIBLE);
startDVDAnimation();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 碟片向下切歌动画
*/
private void initDVDDownAnimation() {
mDownAnimation = AnimationUtils.loadAnimation(mContext,R.anim.down_anim);
mDownAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mIRecordListener.down();
}
@Override
public void onAnimationEnd(Animation animation) {
fl_dvd2.setVisibility(View.VISIBLE);
startDVDAnimation();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
完整的代码:
package com.example.recordproject.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.example.recordproject.R;
import com.example.recordproject.widget.interf.IRecordListener;
/**
* 唱片
*
* @author Linhai Gu
*
*/
public abstract class BaseRecordView extends LinearLayout {
/**
* 上下文
*/
protected Context mContext;
/**
* 碟片
*/
private FrameLayout fl_dvd;
/**
* 碟片上面
*/
private FrameLayout fl_dvd2;
/**
* 碟片动画
*/
private Animation mDVDAnimtion;
/**
* 碟片播放动画的速度匀速
*/
private LinearInterpolator mDVDLin;
/**
* 杆
*/
private ImageView iv_pole;
/**
* 杆动画
*/
private Animation mPoleAnimtion;
/**
* 杆播放动画的速度匀速
*/
private LinearInterpolator mPoleLin;
/**
* 向上切歌
*/
private Animation mUpAnimation;
/**
* 向下切歌
*/
private Animation mDownAnimation;
/**
* 滑动的最小距离
*/
private int mTouchSlop;
/**
* 浮层碟片
*/
private ImageView iv_center2;
private ImageView iv_background2;
private View mPole;
/**
* 是否正在播放
*/
private boolean isStart = false;
/**
* 杆是否在碟片上
*/
private boolean isPole = false;
private IRecordListener mIRecordListener;
protected abstract void setRecordListener(IRecordListener listener);
protected abstract void start();
protected abstract void stop();
public BaseRecordView(Context context) {
this(context, null);
}
public BaseRecordView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BaseRecordView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
init();
initAnimation();
initEvent();
}
/**
* 初始化View
*
* @param context
*/
private void init() {
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
LayoutInflater.from(mContext).inflate(R.layout.record_layout, this);
fl_dvd = (FrameLayout) findViewById(R.id.fl_dvd);// 两层 碟片的容器
fl_dvd2 = (FrameLayout) findViewById(R.id.fl_dvd2);// 移出唱片动画的View
iv_background2 = (ImageView) findViewById(R.id.iv_background2);// 黑色的唱片(移出唱片动画的View)
iv_center2 = (ImageView) findViewById(R.id.iv_center2);// 中间的圆心图案(移出唱片动画的View)
iv_pole = (ImageView) findViewById(R.id.iv_pole);// 杆
mPole = findViewById(R.id.id_pole);// 唱片右下角透明的View
}
/**
* <pre>
* {@link #initDVDAnimation}初始化碟片动画
* {@link #initDVDUpAnimation}初始向上切歌的动画
* {@link #initDVDDownAnimation}初始向下切歌的动画
* </pre>
*/
private void initAnimation() {
initDVDAnimation();
initDVDUpAnimation();
initDVDDownAnimation();
}
/**
* 碟片的触摸事件
*/
private OnTouchListener mOnTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int oldY = (int) event.getX();
int newY = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
oldY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
newY = (int) event.getY();
stopDVDAnimation();
break;
case MotionEvent.ACTION_UP:
if (isStart && oldY - newY > mTouchSlop && (newY < oldY)) {
up();
} else if (isStart && newY - oldY > mTouchSlop && (newY > oldY)) {
down();
} else if (isPole) {
startDVDAnimation();
}
break;
default:
break;
}
return true;
}
};
/**
* 初始化事件
*/
private void initEvent() {
/**
* 碟片的触摸事件
*/
iv_background2.setOnTouchListener(mOnTouchListener);
iv_center2.setOnTouchListener(mOnTouchListener);
/**
* 杆触摸事件
*/
iv_pole.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (!isStart) {
/*
* 当停止播放时,拨动杆,播放
*/
isPole = true;
startPoleAnimation();
}
break;
default:
break;
}
return true;
}
});
/**
* 虚拟位置的触摸
*/
mPole.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (isStart) {
/*
* 当播放时,拨动杆,停止
*/
isPole = false;
stopPoleAnimation();
}
break;
default:
break;
}
return true;
}
});
}
/**
* 碟片的播放动画
*/
private void initDVDAnimation() {
mDVDLin = new LinearInterpolator();
mDVDAnimtion = AnimationUtils.loadAnimation(mContext, R.anim.dvd_anim);
mDVDAnimtion.setInterpolator(mDVDLin);
mDVDAnimtion.setFillAfter(true);
mDVDAnimtion.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 碟片向上切歌动画
*/
private void initDVDUpAnimation() {
mUpAnimation = AnimationUtils.loadAnimation(mContext, R.anim.up_anim);
mUpAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mIRecordListener.up();
}
@Override
public void onAnimationEnd(Animation animation) {
fl_dvd2.setVisibility(View.VISIBLE);
startDVDAnimation();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 碟片向下切歌动画
*/
private void initDVDDownAnimation() {
mDownAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.down_anim);
mDownAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mIRecordListener.down();
}
@Override
public void onAnimationEnd(Animation animation) {
fl_dvd2.setVisibility(View.VISIBLE);
startDVDAnimation();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 杆子启动动画
*/
private void initPoleAnimation() {
mPoleAnimtion = AnimationUtils
.loadAnimation(mContext, R.anim.pole_anim);
mPoleLin = new LinearInterpolator();
mPoleAnimtion.setFillAfter(true);
mPoleAnimtion.setInterpolator(mPoleLin);
mPoleAnimtion.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
/*
* 杆子启动完毕,说明碟子应该要转动了
*/
isStart = true;
isPole = true;
startDVDAnimation();
mIRecordListener.start();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 杆复位动画
*/
private void initPoleStopAnimation() {
mPoleAnimtion = AnimationUtils.loadAnimation(mContext,
R.anim.pole_rest_anim);
mPoleLin = new LinearInterpolator();
mPoleAnimtion.setFillAfter(true);
mPoleAnimtion.setInterpolator(mPoleLin);
mPoleAnimtion.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
/*
* 杆子复位,说明已经停止播放或者播放完毕
*/
isPole = false;
isStart = false;
stopDVDAnimation();
mIRecordListener.stop();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 播放碟片动画
*/
private void startDVDAnimation() {
fl_dvd.startAnimation(mDVDAnimtion);
}
/**
* 暂停碟片动画
*/
private void stopDVDAnimation() {
fl_dvd.clearAnimation();
// mDVDAnimtion.cancel();
}
/**
* 播放杆的动画
*/
protected void startPoleAnimation() {
initPoleAnimation();
// mPoleAnimtion.cancel();
iv_pole.startAnimation(mPoleAnimtion);
}
/**
* 暂停杆的动画
*/
protected void stopPoleAnimation() {
// iv_pole.clearAnimation();
initPoleStopAnimation();
if (mPoleAnimtion != null) {
mPoleAnimtion.cancel();
}
iv_pole.startAnimation(mPoleAnimtion);
}
/**
* 向上切歌
*/
private void up() {
fl_dvd2.startAnimation(mUpAnimation);
}
/**
* 向下切歌
*/
private void down() {
fl_dvd2.startAnimation(mDownAnimation);
}
/**
* 监听
*
* @param listener
*/
protected void initListener(IRecordListener listener) {
this.mIRecordListener = listener;
}
}
package com.example.recordproject.widget;
import com.example.recordproject.widget.interf.IRecordListener;
import android.content.Context;
import android.util.AttributeSet;
public class RecordView extends BaseRecordView {
public RecordView(Context context) {
this(context, null);
}
public RecordView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RecordView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setRecordListener(IRecordListener listener) {
super.initListener(listener);
}
@Override
public void start() {
startPoleAnimation();
}
@Override
public void stop() {
stopPoleAnimation();
}
}
package com.example.recordproject.widget.interf;
public interface IRecordListener {
/**
* 开始
*/
public void start();
/**
* 关闭
*/
public void stop();
/**
* 向上切歌
*/
public void up();
/**
* 向下切歌
*/
public void down();
}
package com.example.recordproject;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.recordproject.widget.RecordView;
import com.example.recordproject.widget.interf.IRecordListener;
public class MainActivity extends Activity {
private RecordView mRecordView;
private int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews() {
mRecordView = (RecordView) findViewById(R.id.mRecordView);
mRecordView.setRecordListener(new IRecordListener() {
@Override
public void stop() {
Log.e("TAG", "-----停止-----");
}
@Override
public void start() {
Log.e("TAG", "-----开启-----");
}
@Override
public void up() {
Log.e("TAG", "-----向上切歌-----");
}
@Override
public void down() {
Log.e("TAG", "-----向下切歌-----");
}
});
}
}
以下是项目的完整github地址。
github项目源码地址:点击【项目源码】