经过一个学期的安卓学习,又到了课程设计答辩环节。为了做好一个项目,我们分好组分配好任务。因为安卓基础薄弱,开始的时候什么都不会,每天百度查资料,翻课本,从最基础的做起。虽然每天都有那么几分钟崩溃的时刻,但我们小组成员之间互相鼓励,谁也没说过放弃,经过了三四周的努力,我们完成了这个项目。
功能需求及性能分析
功能需求分析:
在开始编码之前,我们需要先对程序进行需求分析,设计一个界面简洁干净的录音软件,具有双向的Activity操作,一个是语音播放界面返回到录音界面,另外一个是可以从录音界面返回到语音播放界面,并且显示含有mp4后缀的音乐文件。
这里我认为Sound Recorder中应实现以下功能:
(1)启动主菜单应用程序;
(2)设置界面:进行语音、播放界面的切换,能够简单的对程序进行操作;
(3)具体功能:录音,播放,暂停,停止功能,同时可以设置录音质量,并实现分享,重命名以及删除等功能。
性能分析:
能够迅速简洁的使用录音软件,达到使用方便,检验简单的程度,无路何时何地都可以自由应用。
用户界面设计
根据软件程序的需求分析,这个应用程序应该要包含两个主要的用户界面,在这里,就是更进一步的来分析在这两个的用户界面里面,每一个用户界面需要包括哪一些的内容,应该怎么样的来添加使用。 在录音界面中,在录音的过程中程序需要提示用户此次录音文件的保存地址和文件的保存文件名,以方便用户寻找和使用关于此次录音的播放文件。在进入录音界面之后,可以点击实现录音的效果,在录音界面的Activity按钮可以返回播放界面,同时还设立了返回值,可以回到首页面去进行播放的验证,相当是设置了一条快捷通道。 在录音保存列表中,需要在列表里显示出所有的在手机SD卡根目录下的录音文件,所以需要划分一块区域来显示,其中就显示为文件名加录音文件格式的后缀。 在录音的程序里面,设置了给用户看的录音文件的保存提示界面,可以让用户看见文件的自动保存文件名以及录音文件的保存地址,让用户在使用录音文件的时候更加的方便,也使录音程序更加的贴心,下面就是根据对用户界面显示内容的分析,绘制出用户界面的草图,如图所示:
Sound Recorder常用方法介绍
(1)Activity的相关方法及函数
onStart():开始或继续播放音频。
onPause():暂停播放音频。
onDestroy():销毁活动,停止播放音频,调用该方法后mMediaPlayer对象将无法再播放音频。
onPlay(boolean isPlaying):判断当前mMediaPlayer对象是否正在播放音频。
setDataSource():设置要播放的音频的位置。
onCreate(Bundle):首次启用时调用,接受参数:Null 或 savedInstanceState(保存的以前的某些状态信息)。
renameFileDialog():对保存的音频文件重命名。
deleteFileDialog():删除已保存的音频文件。
shareFileDialog():分享已保存的音频文件。
getPrefHignQuality(Context context):设置高质量播放,获取context对象。
onRecord(boolean start):开始播放保存的音频记录。
onPauseRecord(boolean pause):暂停播放保存的音频记录。
(2)两个Activity之间的跳转
通过Intent类实现屏幕之间的跳转(包含没有参数关系和需要传递参数两种情况)。
程序模块设计
在整个的大体结构上来说,程序的播放是整个程序的中转界面,因为在跳转和打开应用的时候都是要显示自定义播放的界面。播放器在打开前就已经开始进行了SD卡内指定根目录下相应的mp4文件进行相应的搜索。而处在后台的搜索服务就进行工作。
录音界面时进入主界面的默认界面,用户点击开始即可进行相应的录音操作,录音结束后,用户可以选择播放、放弃、保存。
播放界面是有录音界面中的SAVED RECORDINGS跳入的,在后台时是一直保持关闭状态的,只有在进入播放界面后才可以对相应的录音文件进行基本播放操作。 在完成了用户的界面设计和录音界面之后,整个程序的设计阶段也就到此基本完成了。
工程目录图
用户界面
用户交互界面:
在MainActivity中继承了ActionBarActivity,ActionBar位于Activity的顶部,可用来显示activity的标题、
Icon、Actions和一些用于交互的View。它也可被用于应用的导航。
定义的viewPager为页卡,能够实现视图的滑动效果。PagerSlidingTabStrip是交互页面指示器控件,配合ViewPager
控件使用,当ViewPager的popupTheme继承FragmentPagerAdapter方法回掉时,PagerSlidingTabStrip也随之变动。
Toolbar是一个工具条,能够实现两个activity之间的转换。
定义的onCreateOptionsMenu()方法,此方法用于初始化菜单,其中menu参数就是即将要显示的Menu实例。 返回true则显示该menu,
false 则不显示; onOptionsItemSelected()方法,菜单项被点击时调用,也就是菜单项的监听方法。
代码如下:
public class MainActivity extends ActionBarActivity{
private static final String LOG_TAG = MainActivity.class.getSimpleName();
private PagerSlidingTabStrip tabs;
private ViewPager pager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new MyAdapter(getSupportFragmentManager()));
tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
tabs.setViewPager(pager);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setPopupTheme(R.style.ThemeOverlay_AppCompat_Light);
if (toolbar != null) {
setSupportActionBar(toolbar);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_settings:
Intent i = new Intent(this, SettingsActivity.class);
startActivity(i);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public class MyAdapter extends FragmentPagerAdapter {
private String[] titles = { getString(R.string.tab_title_record),
getString(R.string.tab_title_saved_recordings) };
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch(position){
case 0:{
return RecordFragment.newInstance(position);
}
case 1:{
return FileViewerFragment.newInstance(position);
}
}
return null;
}
@Override
public int getCount() {
return titles.length;
}
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
}
public MainActivity() {
}
}
显示录音列表:
这里定义了一个FileViewerFragment类继承自Fragment,Android系统提供了两个Fragment类,继承android.support.v4.app.Fragment类可以兼容低版本的Android系统,所以这里我们继承了这个类。
Fragment跟Activity相似,需回调以下两种方法:
onCreate():系统在创建Fragment的时候调用这个方法,这里应该初始化相关的组件, 一些即便是被暂停或者被停止时依然需要保留的东西。
onCreateView():当第一次绘制Fragment的UI时系统调用这个方法,必须返回一个View, 如果Fragment不提供UI也可以返回null。注意,如果继承自ListFragment,onCreateView()默认的实现会返回一个ListView,所以不用自己实现。
代码如下:
public class FileViewerFragment extends Fragment{
private static final String ARG_POSITION = "position";
private static final String LOG_TAG = "FileViewerFragment";
private int position;
private FileViewerAdapter mFileViewerAdapter;
public static FileViewerFragment newInstance(int position) {
FileViewerFragment f = new FileViewerFragment();
Bundle b = new Bundle();
b.putInt(ARG_POSITION, position);
f.setArguments(b);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
position = getArguments().getInt(ARG_POSITION);
observer.startWatching();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_file_viewer, container, false);
RecyclerView mRecyclerView = (RecyclerView) v.findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
llm.setOrientation(LinearLayoutManager.VERTICAL);
//newest to oldest order (database stores from oldest to newest)
llm.setReverseLayout(true);
llm.setStackFromEnd(true);
mRecyclerView.setLayoutManager(llm);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mFileViewerAdapter = new FileViewerAdapter(getActivity(), llm);
mRecyclerView.setAdapter(mFileViewerAdapter);
return v;
}
FileObserver observer =
new FileObserver(android.os.Environment.getExternalStorageDirectory().toString()
+ "/SoundRecorder") {
// set up a file observer to watch this directory on sd card
@Override
public void onEvent(int event, String file) {
if(event == FileObserver.DELETE){
String filePath = android.os.Environment.getExternalStorageDirectory().toString()
+ "/SoundRecorder" + file + "]";
Log.d(LOG_TAG, "File deleted ["
+ android.os.Environment.getExternalStorageDirectory().toString()
+ "/SoundRecorder" + file + "]");
// remove file from database and recyclerview
mFileViewerAdapter.removeOutOfApp(filePath);
}
}
};
}
音频录入
在音频录入模块,定义了一个RecordFragment类,继承自Fragment.Fragment代表了Activity的子模块,因此可以把Fragment理解成Activity片段。Fragment用于自己的生命周期,也可接受它自己的输入事件。
Oncreate()用来表示一个窗口正在生成,其不产生窗口,只是在窗口显示前设置窗口的属性如风格,位置颜色等。
Super.onCreate(savedInstanceState)调用了父类的onCreate构造函数,savedInstanceState是保存当前Activity的状态信息。
setOnclickListener()实现事件监听。
RecordFragment的功能是实现音频录制。
mRecordButton 录制按钮 mPauseButton 暂停按钮 mchronometer 清零。
Start()开始或继续播放音频。
Pause()暂停播放音频。
运行效果图如下:
代码如下:
public class RecordFragment extends Fragment {
private static final String ARG_POSITION = "position";
private static final String LOG_TAG = RecordFragment.class.getSimpleName();
private int position;
private FloatingActionButton mRecordButton