转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/50789666
本文出自:【顾林海的博客】
这几由于智齿又长了出来,痛了好几天还没好,吃饭又不能吃,加上公司里面操蛋的体制,感觉人活着真累,昨天晚上回到住处,我朋友让我帮忙写个唱片播放器,效果大致上如下:
于是乎,利用一个早上做了下,比较粗糙,毕竟帮别人做的,随便做做得了,当然有很多地方投机取巧了。
在做之前看看我们得需要以下素材:
额!!!这个,这个ic_launcher忽律。
现在我们分析这个播放器的几个播放步骤:
讲了这么多,先看看布局吧
<?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项目源码地址:点击【项目源码】