很多时候我们写程序的时候经常用到mvc模式,我们可以在程序里面细心的品味这个模式,
当然也许程序写的view和controler是在一起写的,或者model和controler是在一起写的,
如果按照这个模式读代码会感觉很容易读,同时据说一个人同时一次最多了解到1w行代码,
对于小菜比而言这个数字也许只有1k,所以不能贪多,按照类区分模块一个模块一个模块的记忆,
会比一下子记忆很多容易许多。
/**
*
*/
package com.ldw.music.activity;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.Dialog;
import android.app.PendingIntent;
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.support.v4.app.FragmentActivity;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.ldw.music.MusicApp;
import com.ldw.music.R;
import com.ldw.music.db.MusicInfoDao;
import com.ldw.music.fragment.MainFragment;
import com.ldw.music.fragment.MenuFragment;
import com.ldw.music.slidemenu.SlidingMenu;
import com.ldw.music.utils.MusicUtils;
import com.ldw.music.utils.SplashScreen;
/**
* 主类,首次进入应用会到这里
* 该类提供了首页MainFragment的显示和侧滑MenuFragment的显示
* @author longdw(longdawei1988@gmail.com)
*
*/
@SuppressLint("HandlerLeak")
public class MainContentActivity extends FragmentActivity implements IConstants {
/**
* 主Activity做了3件事
* 1、弹出启动页,在上一篇和上上一篇已经介绍
* 2、初始化UI,使用了SlidingMenu(在该应用讲解的差不多的时候再谈这个应用广泛的开源库)
* 3、finish或者exit,moveTaskToBack该activity(设置闹钟的最终目的是结束该应用)
*/
//ALARM_CLOCK_BROADCAST在该Activity用了2次,一次设置闹钟,一次接受闹钟的提醒,
public static final String ALARM_CLOCK_BROADCAST = "alarm_clock_broadcast";
/**
* SlidingMenu
* 一个非常著名的开源库,该网址讲了它基本的用法
* http://blog.csdn.net/caiwenfeng_for_23/article/details/34411815
* 另外这个地址里面才是能直接用的demo
* http://download.csdn.net/detail/caiwenfeng_for_23/7548019
*/
public SlidingMenu mSlidingMenu;
/**
* 通过该list判断能不能退出
*/
private List<OnBackListener> mBackListeners = new ArrayList<OnBackListener>();
public MainFragment mMainFragment;
private Handler mHandler;
private MusicInfoDao mMusicDao;
private SplashScreen mSplashScreen;
private int mScreenWidth;
public interface OnBackListener {
public abstract void onBack();
}
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
mScreenWidth = metric.widthPixels;
initSDCard();
IntentFilter filter = new IntentFilter();
filter.addAction(ALARM_CLOCK_BROADCAST);
registerReceiver(mAlarmReceiver, filter);
setContentView(R.layout.frame_main);
mSplashScreen = new SplashScreen(this);
mSplashScreen.show(R.drawable.image_splash_background,
SplashScreen.SLIDE_LEFT);
// set the Above View
mMainFragment = new MainFragment();
//设置了左侧的主fragment,关于片段也会专门讲述,比开源库简单些
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_main, mMainFragment).commit();
// configure the SlidingMenu,参数都可以在上面给的网址找到
mSlidingMenu = new SlidingMenu(this);
mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
//“Sets which side the SlidingMenu should appear on.”
mSlidingMenu.setMode(SlidingMenu.RIGHT);
mSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);
mSlidingMenu.setShadowDrawable(R.drawable.shadow);
mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);
mSlidingMenu.setFadeDegree(0.35f);
//布局控件都要让context管理,所以传入this
mSlidingMenu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);
//R.layout.frame_menu该文件作为容器,打开该文件你发现里面除了根节点,就是跟节点的id
mSlidingMenu.setMenu(R.layout.frame_menu);
//事务的特点就是每次使用就得新begintransaction
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_menu, new MenuFragment()).commit();
mMusicDao = new MusicInfoDao(this);
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mSplashScreen.removeSplashScreen();
}
};
getData();
}
/**
* 正如开始所说该Activity3个功能,第三个就是结束该activity
*/
private void initSDCard() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.setPriority(1000);// 设置最高优先级
intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);// sd卡被插入,且已经挂载
intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);// sd卡存在,但还没有挂载
intentFilter.addAction(Intent.ACTION_MEDIA_REMOVED);// sd卡被移除
intentFilter.addAction(Intent.ACTION_MEDIA_SHARED);// sd卡作为
// USB大容量存储被共享,挂载被解除
intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);// sd卡已经从sd卡插槽拔出,但是挂载点还没解除
// intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);// 开始扫描
// intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);// 扫描完成
intentFilter.addDataScheme("file");
registerReceiver(sdCardReceiver, intentFilter);// 注册监听函数
}
private void getData() {
new Thread(new Runnable() {
@Override
public void run() {
if (mMusicDao.hasData()) {
// 如果有数据就等三秒跳转
mHandler.sendMessageDelayed(mHandler.obtainMessage(), 3000);
} else {
//如果没数据初始化后再进入,时间非常短
/**
* 进入方法后我们知道其实没做什么必须要做的,
* 下面的4行代码可以删掉
*/
MusicUtils.queryMusic(MainContentActivity.this,
START_FROM_LOCAL);
MusicUtils.queryAlbums(MainContentActivity.this);
MusicUtils.queryArtist(MainContentActivity.this);
MusicUtils.queryFolder(MainContentActivity.this);
mHandler.sendEmptyMessage(1);
}
}
}).start();
}
public void registerBackListener(OnBackListener listener) {
if (!mBackListeners.contains(listener)) {
mBackListeners.add(listener);
}
}
public void unRegisterBackListener(OnBackListener listener) {
mBackListeners.remove(listener);
}
//通过返回键moveTaskToBack
@Override
public void onBackPressed() {
if (mSlidingMenu.isMenuShowing()) {
mSlidingMenu.showContent();
} else {
if (mBackListeners.size() == 0) {
// super.onBackPressed();
// 在activity中调用 moveTaskToBack (boolean nonRoot)方法即可将activity
// 退到后台,注意不是finish()退出。
// 参数为false代表只有当前activity是task根,指应用启动的第一个activity时,才有效;
moveTaskToBack(true);
}
for (OnBackListener listener : mBackListeners) {
listener.onBack();
}
}
}
/**
* 如果没有sd卡就没有音乐,以前的手机会把音乐等大文件放到sd卡上,现在的rom已经很大,已经取代了sd卡的位置,
* android会检查target sdk,如果apk的target sdk版本小于android手机的版本,系统会自动适配以前的模式,不影响程序的运行
* 这就是程序的向后兼容性
*/
private final BroadcastReceiver sdCardReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("android.intent.action.MEDIA_REMOVED")// 各种未挂载状态
|| action.equals("android.intent.action.MEDIA_UNMOUNTED")
|| action.equals("android.intent.action.MEDIA_BAD_REMOVAL")
|| action.equals("android.intent.action.MEDIA_SHARED")) {
finish();
Toast.makeText(MainContentActivity.this, "SD卡以外拔出,本地数据没法初始化!",
Toast.LENGTH_SHORT).show();
}
}
};
//显示自定义的dialog
//双击showSleepDialog选中,Ctrl+Alt+H,show call hierarchy,点击下面控制台旁边call hierarchy 的onClick方法,然后就知道哪里调用该方法了,自定义dialog有多钟方式,大多数都需要infalter来找到view,进而通过view.findViwById找到该空间,对该控件进行监控或者进行设置
public void showSleepDialog() {
if (MusicApp.mIsSleepClockSetting) {
cancleSleepClock();
Toast.makeText(getApplicationContext(), "已取睡眠模式!",
Toast.LENGTH_SHORT).show();
return;
}
View view = View.inflate(this, R.layout.sleep_time, null);
final Dialog dialog = new Dialog(this, R.style.lrc_dialog);
dialog.setContentView(view);
//摸dialog的外面不能取消
dialog.setCanceledOnTouchOutside(false);
Window dialogWindow = dialog.getWindow();
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
dialogWindow.setGravity(Gravity.CENTER);
// lp.x = 100; // 新位置X坐标
// lp.y = 100; // 新位置Y坐标
lp.width = (int) (mScreenWidth * 0.7); // 宽度
// lp.height = 400; // 高度
// 当Window的Attributes改变时系统会调用此函数,可以直接调用以应用上面对窗口参数的更改,也可以用setAttributes
// dialog.onWindowAttributesChanged(lp);
dialogWindow.setAttributes(lp);
dialog.show();
final Button cancleBtn = (Button) view.findViewById(R.id.cancle_btn);
final Button okBtn = (Button) view.findViewById(R.id.ok_btn);
final EditText timeEt = (EditText) view.findViewById(R.id.time_et);
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
if (v == cancleBtn) {
dialog.dismiss();
} else if (v == okBtn) {
String timeS = timeEt.getText().toString();
if (TextUtils.isEmpty(timeS)
|| Integer.parseInt(timeS) == 0) {
Toast.makeText(getApplicationContext(), "输入无效!",
Toast.LENGTH_SHORT).show();
return;
}
setSleepClock(timeS);
dialog.dismiss();
}
}
};
cancleBtn.setOnClickListener(listener);
okBtn.setOnClickListener(listener);
}
/**
* 设置睡眠闹钟
*
* @param timeS
*/
private void setSleepClock(String timeS) {
Intent intent = new Intent(ALARM_CLOCK_BROADCAST);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
MainContentActivity.this, 0, intent, 0);
// 设置time时间之后退出程序
int time = Integer.parseInt(timeS);
long longTime = time * 60 * 1000L;
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC, System.currentTimeMillis() + longTime,
pendingIntent);
MusicApp.mIsSleepClockSetting = true;
Toast.makeText(getApplicationContext(), "将在"+timeS+"分钟后退出软件", Toast.LENGTH_SHORT)
.show();
}
/**
* 取消睡眠闹钟
*/
private void cancleSleepClock() {
Intent intent = new Intent(ALARM_CLOCK_BROADCAST);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
MainContentActivity.this, 0, intent, 0);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.cancel(pendingIntent);
MusicApp.mIsSleepClockSetting = false;
}
private BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//退出程序
finish();
}
};
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(sdCardReceiver);
unregisterReceiver(mAlarmReceiver);
MusicApp.mServiceManager.exit();
MusicApp.mServiceManager = null;
MusicUtils.clearCache();
cancleSleepClock();
System.exit(0);//exit和finish是不同的,详情百度“如何退出一个android应用”
}
}
其实这个Activity里面许多东西都是值得,继续深究的,比如:自定义dialog的几种方法,结束掉应用或者Activity的方法,开源库SlidingMenu的几种用法等
关于Dao层将在下一节讲述