第三天 音乐播放器

  工作忙学习忙,天天都是忙啊。今天要记录的是一个仿QQ的音乐播放器,时间有限,功能不是很齐全,但基本雏形和功能都已完成。作为一个中期的过度。下次再开始真正的大项目。

  音乐播放器,基本上算是android程序员都做过的一个练习项目了。不是很难,但想做的很漂亮,很完美,也不是那么容易的。本文拿它来讲,一方面是典型,把android四大组件都用上了,还用到各种布局,以及Notification和Toast等等信息提示类的内容。让我们开始吧,每天进步一点点!本文采用原型模型进行讲解。

  原型模型又称快速原型,主要是为了应对需求模糊的项目。百度百科这样说:快速原型模型需要迅速建造一个可以运行的软件原型 ,以便理解和澄清问题,使开发人员与用户达成共识,最终在确定的客户需求基础上开发客户满意的软件产品。

 

一、快速分析

一个音乐播放器的基本需求就是对音乐的播放控制,音乐内容的展示,还有音乐的选择。

在此基础上,可以加入,网络音乐资源,定时关闭,摇一摇切歌,音乐锁屏等等提高用户体验的功能。

功能需求:

  播放控制:播放 暂停 上一曲 下一曲 滑动快进 及 通知栏控制等

  内容展示:歌名 歌手 时间 歌词 专辑等

  音乐选择:音乐列表等


二、构造原型

   本程序仿QQ音乐播放器:原型包括以下activity(欢迎界面 音乐列表界面 音乐播放界面) 以及后台播放服务(音乐播放服务) 和通知栏控制通知的设计和实现。

知识点预研:

1、动画详解

  Android的动画主要包括三种:Tween Animation、Frame Animation Property Animation。

  Tween Animation也是最常见的动画效果。主要有Alpha Scale Rotate Translate四种,实现方式可以使用java代码:AnimationSet和相应的AlphaAnimation ScaleAnimation RotateAnimation TranslateAnimation类;也可以使用xml文件实现:set rotate translate alpha scale标签实现。

  本项目中的使用实例:

// 旋转动画
RotateAnimation rotate = new RotateAnimation(0, 360,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
rotate.setDuration(DELAY);
// 渐变动画
AlphaAnimation alpha = new AlphaAnimation(0.1f, 1.0f);
alpha.setDuration(DELAY);
image_splash.startAnimation(rotate);
wordsa_splash.startAnimation(alpha);
wordsb_splash.startAnimation(alpha);

关于动画可参考:

http://blog.csdn.net/huxueyan521/article/details/8179036

http://blog.csdn.net/actual_/article/details/6943556

http://blog.csdn.net/lmj623565791/article/details/38067475

http://blog.csdn.net/banketree/article/details/25773243

 

2、布局详解

Android系统主要包括五大布局:LinearLayout RelativeLayout FrameLayout AbsoluteLayout TableLayout; 最常用的是前三个, 第四个现在已经废弃;它们之前的继承关系如图:


具体可参考:

http://blog.csdn.net/xujinsmile/article/details/8393647

http://blog.csdn.net/qinlicang/article/details/6091807

 

3、四大组件

这个大家太熟悉了。Activity Service ContentProvider BroadcastReceiver。别人已经讲解的很详细了,我就不再赘述了。请参考:

http://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html

http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html


4、ListView

这个是必须要好好掌握的一个控件。这是新手向老手进化的一个小标志。在iOS开发中,UITableView和这个控件的功能相似,其在iOS中占据非常非常重要的作用,一般的iOS书中都会发一大章节去讲解。所以在android里,一定要掌握这个控件。

在android里,其实它很简单,主要有两个重点,一个是控件本身,另一个是Adapter。

实现ListView常用两种Adapter 一个是SimpleAdapter;另一个是extends BaseAdapter。推荐后者。

可参考:

http://blog.csdn.net/xiazdong/article/details/7536115

http://blog.csdn.net/ygc87/article/details/7389603

http://blog.csdn.net/tianfeng701/article/details/7557819

 

5、MediaPlayer

这个是音乐播放的核心。但也是最简单实用的,但其原理却很复杂。实现方法请参考下面的代码。原理附图一张:


具体请参考:

http://blog.csdn.net/murongshusheng/article/details/7580689

http://blog.csdn.net/eustoma/article/details/6706492

http://blog.csdn.net/cankingapp/article/details/7972725

 

6、信息提示

信息提示主要包括:Notification Toast Dialog请参考

http://blog.csdn.net/qwm8777411/article/details/45585603

 

   由于时间篇幅和能力的问题,我没有作详细讲解,非常抱歉。但,所有内容,如果大家有所疑问,我一定尽我所能为大家讲解。还是那句话重要的是学习能力。

一点小经验:我搜索时是这样的:比如我想学习一下activity,我会这样搜索:android activity详解;如果是我想知道StringBuilder类 我会这样:java StringBuilder

有时我也会用google的高级搜索。大家可以百度google hacking学习一下google的高级搜索,此搜索同样可以用在百度里。

   希望对您有所帮助。谢谢。

程序包结构:

 

 

AndroidManifest.xml

 

<?xml version="1.0" encoding="utf-8"?>
<!-- 第一版 对于一个真正实用的产品 版本非常重要 -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.leo.lovemusic"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="21" />

    <!-- 需要从本地读取歌曲 所以需要SD卡的读权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <!-- 自定义自己的Application通过android:name指定 -->
    <application
        android:name="com.leo.lovemusic.AppContext"
        android:allowBackup="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.NoActionBar.TranslucentDecor" >

        <!-- 欢迎界面Splash -->
        <activity
            android:name=".SplashActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- 播放音乐Activity -->
        <activity android:name="com.leo.lovemusic.activity.PlayActivity" />
        <!-- 音乐列表Activity -->
        <activity android:name="com.leo.lovemusic.activity.MusicActivity" />
        <!-- 后台音乐服务 -->
        <service android:name="com.leo.lovemusic.service.PlayService" />
    </application>

</manifest>

SplashActivity.java

 

package com.leo.lovemusic;

import com.leo.lovemusic.activity.MusicActivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.TextView;

/**
 * @time 2015-09-14
 * @author Leo
 * @describe 欢迎界面 logo 理念 动画等
 * 
 */
public class SplashActivity extends Activity {
	private static final String TAG = SplashActivity.class.getSimpleName();

	private static final int DELAY = 1500;// 动画时间

	private ImageView image_splash = null;// 要展示的图片
	private TextView wordsa_splash = null;// 要展示的内容
	private TextView wordsb_splash = null;// 要展示的内容

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_splash);
		init();// 初始化数据
	}

	/**
	 * 初始化数据
	 */
	private void init() {
		findViewsByIds();
		setAnimation();
	}

	/**
	 * 得到控件
	 */
	private void findViewsByIds() {
		image_splash = (ImageView) findViewById(R.id.iv_image_splash);
		wordsa_splash = (TextView) findViewById(R.id.tv_wordsa_splash);
		wordsb_splash = (TextView) findViewById(R.id.tv_wordsb_splash);
	}

	/**
	 * 设置动画效果
	 */
	private void setAnimation() {
		// 旋转动画
		RotateAnimation rotate = new RotateAnimation(0, 360,
				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
				0.5f);
		rotate.setDuration(DELAY);
		// 渐变动画
		AlphaAnimation alpha = new AlphaAnimation(0.1f, 1.0f);
		alpha.setDuration(DELAY);
		image_splash.startAnimation(rotate);
		wordsa_splash.startAnimation(alpha);
		wordsb_splash.startAnimation(alpha);
		// 动画监听
		rotate.setAnimationListener(new AnimationListener() {

			@Override
			public void onAnimationStart(Animation arg0) {
				// TODO Auto-generated method stub
				Log.d(TAG, "animation start");
			}

			@Override
			public void onAnimationRepeat(Animation arg0) {
				// TODO Auto-generated method stub
				Log.d(TAG, "animation repeat");
			}

			@Override
			public void onAnimationEnd(Animation arg0) {
				// TODO Auto-generated method stub
				Log.d(TAG, "animation end");
				Intent intent = new Intent(SplashActivity.this,
						MusicActivity.class);
				startActivity(intent);
				finish();
			}
		});
	}

}

PlayService.java

 

package com.leo.lovemusic.service;

import java.io.IOException;
import java.util.ArrayList;

import com.leo.lovemusic.R;
import com.leo.lovemusic.activity.PlayActivity;
import com.leo.lovemusic.bean.Music;
import com.leo.lovemusic.util.Constant;
import com.leo.lovemusic.util.Tool;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.RemoteViews;
import android.widget.Toast;

/**
 * 音乐播放的核心服务 有广播接收器,用于接收事件 有通知Notification
 * 
 * @author Leo time:2015-05-01
 */
@SuppressLint("HandlerLeak")
public class PlayService extends Service {
	public ArrayList<Music> musics = null;// 所有音乐信息
	public int current_position = 0;// 当前播放位置

	private MediaPlayer mMediaPlayer = null;// 音乐播放类
	private SharedPreferences sp = null;// 得到及存储播放位置
	private boolean isFirstTime = true;// 第一次播放
	private boolean isPlaying = false;// 是否正在播放
	private int play_mode = 0;// 播放模式 目录0 单曲1 随机2

	private NotificationManager mNotificationManager = null;// 通知管理器
	private Notification notification = null;// 通知
	private static final int ID = 111;// 通知ID

	private static PlayService myself = null;// 我自己

	private Handler mHandler = null;// 处理数据
	private static final int DELAY = 1000;// delay

	/**
	 * 获取Service实例来取得音乐信息和当前位置
	 * 
	 * @return
	 */
	public static PlayService getMyself() {
		return myself;
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		myself = this;
		initData();// 初始化数据
		mHandler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				// super.handleMessage(msg);
				if (msg.what == Constant.WHAT_EMPTY) {
					if (mMediaPlayer != null) {
						Intent intent = new Intent(Constant.ACTION_CURRENT_TIME);
						intent.putExtra(Constant.KEY_CURRENT_TIME,
								mMediaPlayer.getCurrentPosition());
						sendBroadcast(intent);
					}
				}
			}

		};
	}

	/**
	 * @time 2015-09-14
	 * @author Leo
	 * @describe 定时更新seekbar显示音乐当前时间
	 * 
	 */
	private class DelayThread extends Thread {
		public void run() {
			while (isPlaying) {
				try {
					mHandler.sendEmptyMessage(Constant.WHAT_EMPTY);
					Thread.sleep(DELAY);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 获取播放模式 单曲 随机 还是顺序
	 * 
	 * @return
	 */
	private int getPlayMode() {
		int mode = sp.getInt(Constant.KEY_PLAY_MODE, 0);
		return mode;
	}

	/**
	 * 用SharedPreference保存当前播放模式 用于退出时用 下次开启时获取
	 */
	private void setPlayMode() {
		Editor editor = sp.edit();
		editor.putInt(Constant.KEY_PLAY_MODE, play_mode);
		editor.commit();
	}

	/**
	 * 得到上次播放到哪一首歌曲
	 * 
	 * @return
	 */
	private int getPosition() {
		int position = sp.getInt(Constant.KEY_POSITION, 0);
		return position;
	}

	/**
	 * 存储歌曲播放位置
	 */
	private void setPosition() {
		Editor editor = sp.edit();
		editor.putInt(Constant.KEY_POSITION, current_position);
		editor.commit();
	}

	/**
	 * 初始化通知
	 */
	@SuppressWarnings("deprecation")
	private void initNotification() {
		// 取得通知管理器
		mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		// 实例通知
		notification = new Notification(R.drawable.icon, "爱你音乐",
				System.currentTimeMillis());
		// 通过RemoteViews来取得自定义布局
		RemoteViews contentView = new RemoteViews(getPackageName(),
				R.layout.notification);
		// 为上一曲绑定通知事件
		Intent lastIntent = new Intent(Constant.ACTION_LAST);
		PendingIntent last = PendingIntent.getBroadcast(this, 0, lastIntent, 0);
		contentView.setOnClickPendingIntent(R.id.iv_last_notification, last);
		// 为下一曲绑定通知事件
		Intent nextIntent = new Intent(Constant.ACTION_NEXT);
		PendingIntent next = PendingIntent.getBroadcast(this, 0, nextIntent, 0);
		contentView.setOnClickPendingIntent(R.id.iv_next_notification, next);
		// 为播放和暂停绑定通知事件
		Intent controlIntent = new Intent(Constant.ACTION_CONTROL);
		PendingIntent control = PendingIntent.getBroadcast(this, 0,
				controlIntent, 0);
		contentView.setOnClickPendingIntent(R.id.iv_control_notification,
				control);
		// 为整个通知绑定单击事件
		Intent notificationIntent = new Intent(PlayService.this,
				PlayActivity.class);
		PendingIntent contentIntent = PendingIntent.getActivity(
				PlayService.this, 0, notificationIntent, 0);
		// 为notification设置自定义布局和事件
		notification.contentView = contentView;
		notification.contentIntent = contentIntent;
		// 设置通知不 可清除且为紧急
		notification.flags = Notification.FLAG_NO_CLEAR;
		notification.flags = Notification.FLAG_INSISTENT;
		// 启动通知
		mNotificationManager.notify(ID, notification);
	}

	/**
	 * 显示通知
	 * 
	 * @param bitmap
	 * @param title
	 * @param artist
	 */
	private void showNotification(Bitmap bitmap, String title, String artist) {
		// 更新缩略图
		if (bitmap != null) {
			notification.contentView.setImageViewBitmap(
					R.id.iv_image_notification, bitmap);
		}
		// 更新歌曲名
		if (title != null) {
			notification.contentView.setTextViewText(
					R.id.tv_title_notification, title);
		}
		// 更新歌手名
		if (artist != null) {
			notification.contentView.setTextViewText(
					R.id.tv_artist_notification, artist);
		}
		mNotificationManager.notify(ID, notification);
	}

	private void closeNotification() {
		mNotificationManager.cancel(ID);
	}

	private void registerTheReceiver() {
		// 注册广播接收器
		IntentFilter filter = new IntentFilter();
		filter.addAction(Constant.ACTION_LAST);
		filter.addAction(Constant.ACTION_NEXT);
		filter.addAction(Constant.ACTION_CONTROL);
		filter.addAction(Constant.ACTION_CLOSE_NOTIFICATION);
		filter.addAction(Constant.ACTION_PLAY_MODE);
		filter.addAction(Constant.ACTION_SEEK_TO);
		filter.setPriority(Integer.MAX_VALUE);
		registerReceiver(mReceiver, filter);
	}

	/**
	 * 提示
	 * 
	 * @param text
	 */
	private void toast(String text) {
		Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
	}

	/**
	 * 初始化数据
	 */
	private void initData() {
		musics = Tool.findMusic(this);
		if ((musics == null) || musics.size() <= 0) {
			toast("非常抱歉,没有音乐");
			return;
		}
		sp = getSharedPreferences(Constant.SP_NAME, Context.MODE_PRIVATE);
		current_position = getPosition();
		play_mode = getPlayMode();
		registerTheReceiver();
		// 实例化MediaPlayer
		mMediaPlayer = new MediaPlayer();
		// 绑定歌曲完成事件
		mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {

			@Override
			public void onCompletion(MediaPlayer arg0) {
				// TODO Auto-generated method stub
				if (play_mode == 0) {
					nextMusic();
				} else if (play_mode == 1) {
					playMusic(current_position);
				} else if (play_mode == 2) {
					current_position = Tool.getRandomPosition(musics.size());
					playMusic(current_position);
				}
			}
		});
		// 初始化通知
		if ((musics != null) && current_position < musics.size()) {
			Music music = musics.get(current_position);
			initNotification();
			showNotification(null, music.getTitle(), music.getArtist());
		}
	}

	/**
	 * 实现自己的广播接收器
	 */
	private BroadcastReceiver mReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context arg0, Intent arg1) {
			// TODO Auto-generated method stub
			String action = arg1.getAction();
			if (Constant.ACTION_CONTROL.equals(action)) {
				playOrPauseMusic();
			} else if (Constant.ACTION_LAST.equals(action)) {
				lastMusic();
			} else if (Constant.ACTION_NEXT.equals(action)) {
				nextMusic();
			} else if (Constant.ACTION_CLOSE_NOTIFICATION.equals(action)) {
				closeNotification();
			} else if (Constant.ACTION_PLAY_MODE.equals(action)) {
				play_mode = ++play_mode % 3;
				Intent broadcast = new Intent(Constant.ACTION_CHANGED_MODE);
				sendBroadcast(broadcast);
			} else if (Constant.ACTION_SEEK_TO.equals(action)) {
				current_position = arg1.getIntExtra(
						Constant.KEY_MUSIC_POSITION, current_position);
				playMusic(current_position);
			}
		}
	};

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		suiside();
	}

	/**
	 * 自杀
	 */
	private void suiside() {
		isPlaying = false;
		if (mMediaPlayer != null) {
			if (mMediaPlayer.isPlaying()) {
				mMediaPlayer.stop();
			}
			mMediaPlayer.release();
		}
		unregisterReceiver(mReceiver);
		setPosition();
		setPlayMode();
		if (mHandler != null) {
			mHandler.removeCallbacksAndMessages(null);
			mHandler = null;
		}
	}

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 启动Delay线程,用于显示当前歌曲时间
	 */
	private void startDelayThread() {
		isPlaying = true;
		new DelayThread().start();
	}

	/**
	 * 开始暂停
	 */
	private void playOrPauseMusic() {
		if (mMediaPlayer == null) {
			return;
		}
		if (mMediaPlayer.isPlaying()) {
			isPlaying = false;
			mMediaPlayer.pause();
			// 改变通知里控制按钮为播放
			notification.contentView.setImageViewResource(
					R.id.iv_control_notification, R.drawable.notification_play);
			mNotificationManager.notify(ID, notification);
		} else {
			// 改变通知里控制按钮为暂停
			notification.contentView
					.setImageViewResource(R.id.iv_control_notification,
							R.drawable.notification_pause);
			mNotificationManager.notify(ID, notification);
			if (isFirstTime) {
				playMusic(current_position);
				isFirstTime = false;
				return;
			}
			mMediaPlayer.start();
			startDelayThread();
		}
	}

	/**
	 * 播放音乐MediaPlayer
	 * 
	 * @param position
	 */
	private void playMusic(int position) {
		if (mMediaPlayer == null) {
			return;
		}
		if (musics == null || musics.size() <= 0) {
			return;
		}
		if (position < 0) {
			position = musics.size() - 1;
		}
		if (position >= musics.size()) {
			position = 0;
		}
		Music music = musics.get(position);
		try {
			mMediaPlayer.reset();
			mMediaPlayer.setDataSource(music.getData());
			mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
			mMediaPlayer.prepare();
			mMediaPlayer.start();
			startDelayThread();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		showNotification(null, music.getTitle(), music.getArtist());
		/**
		 * 通知PlayActivity
		 */
		Intent broadcast = new Intent(Constant.ACTION_INFORMATION);
		sendBroadcast(broadcast);
	}

	/**
	 * 上一曲
	 */
	private void lastMusic() {
		int position = current_position - 1;
		if (position < 0) {
			position = musics.size() - 1;
		}
		current_position = position;
		playMusic(position);
	}

	/**
	 * 下一曲
	 */
	private void nextMusic() {
		int position = current_position + 1;
		if (position >= musics.size()) {
			position = 0;
		}
		current_position = position;
		playMusic(position);
	}

	public boolean getPlayState() {
		return isPlaying;
	}

	public int getNowPlayMode() {
		return play_mode;
	}

	/**
	 * 快进到相应的位置
	 * 
	 * @param process
	 */
	public void seekTo(int process) {
		mMediaPlayer.seekTo(process);
	}
}

PlayActivity.java

 

package com.leo.lovemusic.activity;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

import com.leo.lovemusic.R;
import com.leo.lovemusic.bean.Music;
import com.leo.lovemusic.service.PlayService;
import com.leo.lovemusic.util.Constant;
import com.leo.lovemusic.util.Tool;

@SuppressLint("HandlerLeak")
public class PlayActivity extends Activity {
	// 附加功能控件
	private View back_play = null;
	private View more_play = null;
	private View favourite_play = null;
	private View cycle_play = null;
	private View download_play = null;
	private View share_play = null;
	private View list_play = null;
	// 显示内容控件
	private TextView title_play = null;
	private TextView current_play = null;
	private TextView duration_play = null;
	// 进度控件
	private SeekBar time_play = null;
	// 控制控件
	private Button last_play = null;
	private Button next_play = null;
	private Button control_play = null;

	// private RelativeLayout container_play = null;
	private RelativeLayout container_play = null;
	// 更新UI
	private Handler mHandler = null;

	private PlayService mPlayService = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_play);
		mHandler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				// super.handleMessage(msg);
				switch (msg.what) {
				case Constant.WHAT_INFORMATION:
					showInformation();
					break;
				case Constant.WHAT_CONTROL:
					changeControl();
					break;
				case Constant.WHAT_CURRENT_TIME:
					setSeekBar(msg.arg1);
					break;
				}
			}

		};
		initData();
	}

	private void setSeekBar(int progress) {
		time_play.setProgress(progress);
		current_play.setText(Tool.formatTime(progress));
	}

	/**
	 * 初始化数据
	 */
	private void initData() {
		findViewsById();
		registerTheReceiver();
		mPlayService = PlayService.getMyself();
		showInformation();
		changeControl();
		changePlayMode();
	}

	private void registerTheReceiver() {
		IntentFilter filter = new IntentFilter();
		filter.addAction(Constant.ACTION_CONTROL);
		filter.addAction(Constant.ACTION_INFORMATION);
		filter.addAction(Constant.ACTION_CURRENT_TIME);
		filter.addAction(Constant.ACTION_CHANGED_MODE);
		registerReceiver(mBroadcastReceiver, filter);
	}

	private void changeControl() {

		if (mPlayService.getPlayState()) {
			control_play.setBackgroundResource(R.drawable.play_play_bg);
		} else {
			control_play.setBackgroundResource(R.drawable.play_pause_bg);
		}
	}

	/**
	 * 显示音乐内容
	 * 
	 * @param duration
	 */
	private void showInformation() {
		if (mPlayService.musics == null || mPlayService.musics.size() <= 0) {
			return;
		}
		if (mPlayService.current_position < 0) {
			mPlayService.current_position = mPlayService.musics.size() - 1;
		}
		if (mPlayService.current_position >= mPlayService.musics.size()) {
			mPlayService.current_position = mPlayService.musics.size() - 1;
		}

		Music music = mPlayService.musics.get(mPlayService.current_position);
		if (music == null) {
			return;
		}
		title_play.setText(music.getTitle());
		current_play.setText(Tool.formatTime(0));
		duration_play.setText(Tool.formatTime(music.getDuration()));
		time_play.setMax(music.getDuration());
		time_play.setProgress(0);
	}

	private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context arg0, Intent arg1) {
			// TODO Auto-generated method stub
			String action = arg1.getAction();
			if (Constant.ACTION_CONTROL.equals(action)) {
				mHandler.sendEmptyMessage(Constant.WHAT_CONTROL);
			} else if (Constant.ACTION_INFORMATION.equals(action)) {
				int duration = arg1.getIntExtra(Constant.KEY_DURATION, 0);
				Message msg = new Message();
				msg.what = Constant.WHAT_INFORMATION;
				msg.arg1 = duration;
				mHandler.sendMessage(msg);
			} else if (Constant.ACTION_CURRENT_TIME.equals(action)) {
				int time = arg1.getIntExtra(Constant.KEY_CURRENT_TIME, 0);
				Message msg = new Message();
				msg.what = Constant.WHAT_CURRENT_TIME;
				msg.arg1 = time;
				mHandler.sendMessage(msg);
			} else if (Constant.ACTION_CHANGED_MODE.equals(action)) {
				changePlayMode();
			}
		}
	};

	private float beginX = 0;
	private float endX = 0;

	private void changeContent() {
		if (Math.abs(endX - beginX) >= 10) {
			Toast.makeText(this, "滑动了:" + (endX - beginX), Toast.LENGTH_SHORT)
					.show();
		}
	}

	/**
	 * 得到所有需要用到的控件并绑定事件
	 */
	private void findViewsById() {
		container_play = (RelativeLayout) findViewById(R.id.rl_container_play);
		container_play.setOnTouchListener(new OnTouchListener() {

			@SuppressLint("ClickableViewAccessibility")
			@Override
			public boolean onTouch(View arg0, MotionEvent event) {
				// TODO Auto-generated method stub
				int action = event.getAction();
				switch (action) {
				case MotionEvent.ACTION_DOWN:
					beginX = event.getX();
					break;
				case MotionEvent.ACTION_UP: {
					endX = event.getX();
					changeContent();
				}
					break;
				default:
					break;
				}
				return false;
			}
		});
		// addition
		back_play = findViewById(R.id.v_back_play);
		more_play = findViewById(R.id.v_more_play);
		favourite_play = findViewById(R.id.v_favourite_play);
		cycle_play = findViewById(R.id.v_cycle_play);
		download_play = findViewById(R.id.v_download_play);
		share_play = findViewById(R.id.v_share_play);
		list_play = findViewById(R.id.v_list_play);
		// show
		title_play = (TextView) findViewById(R.id.tv_title_play);
		current_play = (TextView) findViewById(R.id.tv_current_play);
		duration_play = (TextView) findViewById(R.id.tv_duration_play);
		// time
		time_play = (SeekBar) findViewById(R.id.sb_time_play);
		// control
		last_play = (Button) findViewById(R.id.btn_last_play);
		next_play = (Button) findViewById(R.id.btn_next_play);
		control_play = (Button) findViewById(R.id.btn_control_play);

		back_play.setOnClickListener(mOnClickListener);
		more_play.setOnClickListener(mOnClickListener);
		favourite_play.setOnClickListener(mOnClickListener);
		cycle_play.setOnClickListener(mOnClickListener);
		download_play.setOnClickListener(mOnClickListener);
		share_play.setOnClickListener(mOnClickListener);
		list_play.setOnClickListener(mOnClickListener);

		last_play.setOnClickListener(mOnClickListener);
		next_play.setOnClickListener(mOnClickListener);
		control_play.setOnClickListener(mOnClickListener);

		time_play.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

			@Override
			public void onStopTrackingTouch(SeekBar arg0) {
				// TODO Auto-generated method stub
				mPlayService.seekTo(arg0.getProgress());
			}

			@Override
			public void onStartTrackingTouch(SeekBar arg0) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
				// TODO Auto-generated method stub

			}
		});
	}

	/**
	 * 单击事件监听器
	 */
	private View.OnClickListener mOnClickListener = new OnClickListener() {

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			int id = arg0.getId();
			switch (id) {
			case R.id.v_back_play:
				Toast.makeText(PlayActivity.this, "back", Toast.LENGTH_SHORT)
						.show();
				break;
			case R.id.v_more_play:
				Toast.makeText(PlayActivity.this, "more", Toast.LENGTH_SHORT)
						.show();
				break;
			case R.id.v_favourite_play:
				Toast.makeText(PlayActivity.this, "favourite",
						Toast.LENGTH_SHORT).show();
				break;
			case R.id.v_cycle_play: {
				Intent intent = new Intent(Constant.ACTION_PLAY_MODE);
				sendBroadcast(intent);
			}
				break;
			case R.id.v_download_play:
				Toast.makeText(PlayActivity.this, "download",
						Toast.LENGTH_SHORT).show();
				break;
			case R.id.v_share_play:
				Toast.makeText(PlayActivity.this, "share", Toast.LENGTH_SHORT)
						.show();
				break;
			case R.id.v_list_play:
				Toast.makeText(PlayActivity.this, "list", Toast.LENGTH_SHORT)
						.show();
				break;

			case R.id.btn_last_play: {
				Intent intent = new Intent(Constant.ACTION_LAST);
				sendBroadcast(intent);
			}
				break;
			case R.id.btn_next_play: {
				Intent intent = new Intent(Constant.ACTION_NEXT);
				sendBroadcast(intent);
			}
				break;
			case R.id.btn_control_play: {
				Intent intent = new Intent(Constant.ACTION_CONTROL);
				sendBroadcast(intent);
			}
				break;
			default:
				Toast.makeText(PlayActivity.this, "我真的不知道你对我做了什么",
						Toast.LENGTH_SHORT).show();
				break;
			}
		}
	};

	private void changePlayMode() {
		int mode = mPlayService.getNowPlayMode();
		if (mode == 0) {
			cycle_play.setBackgroundResource(R.drawable.play_mode_cycle);
		} else if (mode == 1) {
			cycle_play.setBackgroundResource(R.drawable.play_mode_single);
		} else if (mode == 2) {
			cycle_play.setBackgroundResource(R.drawable.play_mode_random);
		}
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		unregisterReceiver(mBroadcastReceiver);
		if (mHandler != null) {
			mHandler.removeCallbacksAndMessages(null);
			mHandler = null;
		}
	}

}

MyAdapter.java

 

package com.leo.lovemusic.adapter;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.leo.lovemusic.R;
import com.leo.lovemusic.bean.Music;
import com.leo.lovemusic.letter.LetterBaseListAdapter;

/**
 * @time 2015-09-15
 * @describe 显示音乐列表的Adapter 此列表同时显示ABCDE等索引
 * @author r00kie
 *
 */
public class MyAdapter extends LetterBaseListAdapter<Music> {
	private static final String LETTER_KEY = "letter";//索引的key
	private LayoutInflater mLayoutInflater;//为了布局

	/**
	 * 构造数据源
	 * @param context
	 * @param data
	 */
	public MyAdapter(Context context, List<Music> data) {
		super();
		mLayoutInflater = LayoutInflater.from(context);
		setContainerList(data);
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return list.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public String getItemString(Music t) {
		// TODO Auto-generated method stub
		return t.getTitle();
	}

	@Override
	public Music create(char letter) {
		// TODO Auto-generated method stub
		return new Music(LETTER_KEY, String.valueOf(letter));
	}

	/**
	 * 判断是不是索引
	 */
	@Override
	public boolean isLetter(Music t) {
		// TODO Auto-generated method stub
		return LETTER_KEY.equals(t.getTitle());
	}

	/**
	 * 得到索引布局
	 */
	@Override
	public View getLetterView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		// 这里是字母的item界面设置.
		ViewHolder mViewHolder;
		if (null == convertView) {
			mViewHolder = new ViewHolder();
			convertView = mLayoutInflater.inflate(R.layout.letter_index_item,
					null);
			mViewHolder.index_name = (TextView) convertView
					.findViewById(R.id.index_name);
			convertView.setTag(mViewHolder);
		} else {
			mViewHolder = (ViewHolder) convertView.getTag();
		}
		mViewHolder.index_name.setText(list.get(position).getLetter());
		return convertView;
	}

	/**
	 * 得到内容的布局
	 */
	@Override
	public View getContainerView(int position, View convertView,
			ViewGroup parent) {
		// TODO Auto-generated method stub
		ViewHolder mViewHolder;//这样写是为了优化列表
		if (null == convertView) {
			mViewHolder = new ViewHolder();
			convertView = mLayoutInflater.inflate(R.layout.letter_music_item,
					null);
			mViewHolder.item_name = (TextView) convertView
					.findViewById(R.id.item_name);
			mViewHolder.item_value = (TextView) convertView
					.findViewById(R.id.item_value);
			convertView.setTag(mViewHolder);
		} else {
			mViewHolder = (ViewHolder) convertView.getTag();
		}
		mViewHolder.item_name.setText(list.get(position).getTitle());
		mViewHolder.item_value.setText(list.get(position).getArtist());
		return convertView;
	}

	private class ViewHolder {
		public TextView index_name;

		public TextView item_name;
		public TextView item_value;
	}
}

三、运行原型


四、评价原型

优点:

  挺好看的,嘿嘿。我又自恋了。其实就是还好啊,里面的图片都是从QQ音乐里抠图出来的。可以完成基本的功能。

缺点:

  有Bugs。

  功能不完善,比如:频谱显示没有 摇一摇没有 定时没有等等。

  对于原型评价必须要客观,这个需要用户去协助完成。我们快速做出原型就是为了让用户去真真正正的感受,让他们去对真正需要的东西有一个真实的体验,没有的要加,有的不好的要改。这样真的需求明了了,咱们的项目也就好完成了。原型出来了,核心代码也差不多了。


五、修改原型

这个就留给大家去完成了。大家可以根据自己的需要添加好玩的功能。

 

源码:http://download.csdn.net/detail/zhaicaixiansheng/9109717

 

六、项目总结

让我们先看一下百度百科里的快速原型的步骤:

快速分析

在分析人员与用户密切配合下,迅速确定系统的基本需求,根据原型所要体现的特征描述基本需求以满足开发原型的需要。

构造原型

在快速分析的基础上,根据基本需求说明尽快实现一个可行的系统。这里要求具有强有力的软件工具的支持,并忽略最终系统在某些细节上的要求,如安全性、坚固性、例外处理等等,主要考虑原型系统能够充分反映所要评价的特性,而暂时删除一切次要内容。

运行原型

这是发现问题、消除误解、开发者与用户充分协调的一个步骤。

评价原型

在运行的基础上,考核评价原型的特性,分析运行效果是否满足用户的愿望,纠正过去交互中的误解与分析中的错误,增添新的要求,并满足因环境变化或用户的新想法引起的系统要求变动,提出全面的修改意见。

原型修改

根据评价原型的活动结果进行修改。若原型未满足需求说明的要求,说明对需求说明存在不一致的理解或实现方案不够合理,则根据明确的要求迅速修改原型。

   现说说项目吧!对于初学者,本项目可以起到如下作用:1、android基本知识点的巩固;2、android知识点的混合使用;3、项目的基本结构;对于大神,我就不知道了。不要吵笑我就是了,我会努力的。

   对于一个项目有如下告诫:

   1、一定要按照命名规则进行命名,比如布局文件要以activity_xx.xml命名,ListView的item要以item_xx.xml命名,id要以tv_show_main命名等。这个可以参考:。然后根据自己的习惯稍作调整。然后坚持使用;

   2、一定要习惯注释,因为你做一个项目要好久,到后期你有可能又回来改你的代码,这样可以很快速的知道方法的作用等,还有就是项目交接时,方便接收人快速消化;

   3、一定要做好项目的包组织结构,这样程序才不会乱,你才能够有条不紊的进行开发。

   这些就是你的代码风格!!!有自己的风格,才有自己的天地,才有自己的乐趣。

   为成为一名优秀的程序员而奋斗!!!今天就到这里,谢谢您的阅读。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬李先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值