2015-8-6 15:07:54
词库
找不到词库,暂时找到一个带音标和释义的考研单词excel(估计是好几年前的大纲词汇),就先用这个吧。
excel不能显示音标的话,还得下载字体TOPhonetic.ttf。
数据库
excel导入SQLite
试了几个可视化工具,就SQLiyeStudio比较满意,也没有乱码。
开始时把excel另存为.csv文件,系统的分隔符是逗号,但是去控制面板改了,excel导出时还是逗号,不知道为什么,可能需要重启电脑吧,懒得重启。竟然忘记replace了!所以excel → 制表符分隔的txt → replace分隔符,就ok了^_^
优化——延迟切换fragment
public void switchContent(Fragment fragment) {
contentFragment = fragment;
getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).commit();
// sm.showContent();
Handler h = new Handler();
h.postDelayed(new Runnable() {
public void run() {
sm.showContent();
}
}, 50);
}
2015-8-10
单词本界面想做成卡片来滑动
RecyclerView
关于引入V7包:http://wp.aesean.com/?p=185
2015-8-11
- 继承RecyclerView.Adapter出现The hierarchy of the type ViewHolder is inconsistent,因为菜单用的SlidingMenu,support-v4包可能不是最新的,将SlidingMenu的libs下的v4包替换成最新的就可以了。
-
Call requires API level 21 (current min is 17): android.content.Context#getDrawable
解决ContextCompat.getDrawable(this, R.drawable.your_drawable)
2015-8-12
在actionbar添加spinner下拉列表
1.actionbar.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
android:id="@+id/action_bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉列表" />
<!-- 下拉列表 -->
<Spinner
android:id="@+id/action_bar_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
</Spinner>
</LinearLayout>
2.在activity的onCreate()或fragment的onCreateView()中添加代码,初始化下拉列表数据
//使自定义的普通View能在title栏显示,actionBar.setCustomView能起作用
getActivity().getActionBar().setDisplayShowCustomEnabled(true);
//初始化下拉列表
View actionbarLayout = view.inflate(activity,R.layout.actionbar, null);
mActionbarSpinner = (Spinner) actionbarLayout.findViewById(R.id.actionbar_spinner);
//初始化下拉列表数据
//方法一
initSpinnerMethod1();
//方法二
//initSpinnerMethod2();
//事件监听
mActionbarSpinner.setOnItemSelectedListener(new SpinnerItemSelectedListener());
//在Bar上显示定制view
actionBar.setCustomView(actionbarLayout);
初始化下拉列表数据:
(1) 在strings.xml
添加数组
<string-array name="spinner_list" >
<item>item1</item>
<item>item2</item>
<item>item3</item>
</string-array>
代码:
private void initSpinnerMethod1() {
String[] mItems = getResources().getStringArray(R.array.spinner_list);
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(activity,android.R.layout.simple_spinner_item, mItems);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mActionbarSpinner.setAdapter(spinnerAdapter);
}
(2)在代码中添加数组
private List<String> getData(){
List<String> data = new ArrayList<String>();
data.add("item1");
data.add("item2");
data.add("item3");
return data;
}
private void initSpinnerMethod2() {
mActionbarSpinner.setAdapter(
new ArrayAdapter<String>(activity,
android.R.layout.simple_expandable_list_item_1,getData()));
}
3.监听action_bar的spinner的item选择事件
private class SpinnerItemSelectedListener implements OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> arg0, View view, int position,long arg3) {
String str= arg0.getItemAtPosition(position).toString();
Toast.makeText(MainActivity.this, "你选择的是:"+str, 2000).show();
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {}
}
同一个activity的不同fragment显示不同的actionbar
在单词本界面的actionbar想显示一个spinner下拉选择单词本(暂时未加入切换单词本的功能),在WordBookFragment的onCreateView()
里设置自定义actionbar
getActivity().getActionBar().setDisplayShowCustomEnabled(true);
别的fragment里设置
getActivity().getActionBar().setDisplayShowCustomEnabled(false);
或者加载另外的view。
不同fragment的标题
分别在onCreateView()
里设置
getActivity().setTitle("标题");
若写成
getActivity().getActionBar().setTitle("标题");
会将所有fragment设置成同一标题
在fragment里获取actionbar
cannot convert from android.support.v7.app.ActionBar to android.app.ActionBar
ActionBar actionBar = getActivity.getActionBar();
使用quick fix:
android.app.ActionBar actionBar;
2015-8-18
控件左右对齐
使用RelativeLayout
android:layout_alignParentRight="true"
2015-8-19
判断程序是否首次运行
使用SharedPreferences
,在onCreate()
里:
SharedPreferences preferences;
Editor editor;
if (preferences.getBoolean("firststart", true)) { //获取boolean值,可为缺省值,若缺省,则返回参数二的值
editor = preferences.edit();
editor.putBoolean("firststart", false); //若是首次,则改为false
editor.commit(); //提交
}
2015-8-20
状态栏通知
可删除
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!");
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, ResultActivity.class);
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(ResultActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());
常驻状态栏(显示在“进行中/ongoing”)
Notification
-
低于API 11
notification.flags |= Notification.FLAG_NO_CLEAR;
-
高于API 11 或者 使用Android Support Library
//获取状态通知栏管理 NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //实例化通知栏构造器 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this); mBuilder.setSmallIcon(R.drawable.ic_launcher);//设置通知小图标 .setContentTitle("标题") //设置通知栏标题 .setContentText("内容") //设置通知栏显示内容 .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图 .setNumber(number) //设置通知集合的数量 .setTicker("通知来啦") //通知首次出现在通知栏,带上升动画效果的 .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示,一般是系统获取到的时间 .setPriority(Notification.PRIORITY_DEFAULT) //设置该通知优先级 .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消 .setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接) .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合 //Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
service前台服务
在service里
startForeground(mID, notification); //设置前台服务
关于服务service
- 启动service的时候,
onCreate
方法只有第一次会调用,onStartCommand
和onStart
(已被淘汰)每次都被调用。onStartCommand
会告诉系统如何重启服务,如判断是否异常终止后重新启动,在何种情况下异常终止。 - 2.0 API level之后,实现onStart等同于重写onStartCommand并返回START_STICKY 。
- 2.0 API level之后,onStart()方法被onStartCommand()取代了。
-
必须在AndroidManifest.xml中注册
<service android:name="完整包名.ServiceNotification" />
启动服务
mContext.startService(intent);
关闭服务
mContext.stopService(intent);
保存、恢复spinner的选中项
保存
在onItemSelected()
中
int userChoice = spinner.getSelectedItemPosition();
SharedPreferences sharedPref = getSharedPreferences("FileName",0);
SharedPreferences.Editor prefEditor = sharedPref.edit();
prefEditor.putInt("userChoiceSpinner",usersChoice);
prefEditor.commit();
恢复
SharedPreferences sharedPref = getSharedPreferences("FileName",MODE_PRIVATE);
int spinnerValue = sharedPref.getInt"userChoiceSpinner",-1);
if(spinnerValue != -1)
spinner.setSelection(spinnerValue);
关于Activity生命周期该做的事情
onCreate()
- 使用
onCreate()
初始化你的Activity:创建UI、为类的变量分配引用、绑定数据到控件、创建Service和线程。 - 为避免快速的创建和销毁对象引发额外的垃圾回收,如果你的应用程序正常创建一套对象,建议它们在
onCreate()
中创建,因为在Activity的生命周期中它只被调用一次。 onCreate()
里面尽量少做事情,避免程序启动太久都看不到界面。
onResume()
当从Paused
状态恢复activity时,系统会调用onResume()
方法。
系统每次调用onResume()
时,activity都处于前台,包括第一次创建的时候。所以,应该实现onResume()
来初始化那些在onPause
方法里面释放掉的组件,并执行那些activity每次进入Resumed state
都需要的初始化动作 (例如开始动画与初始化那些只有在获取用户焦点时才需要的组件)
onPause()
- 不应使用
onPause()
来保存用户改变的数据 (例如填入表格中的个人信息) 到永久存储(File或者DB)上。仅仅当确认用户期待那些改变能够被自动保存的时候(例如正在撰写邮件草稿),才把那些数据存到永久存储 。 - 避免在
onPause()
时执行CPU-intensive 的工作,例如写数据到DB,因为它会导致切换到下一个activity变得缓慢(应该把那些heavy-load的工作放到onStop()去做)。 - 如果activity实际上是要被Stop,为了切换的顺畅应减少在OnPause()方法里面的工作量。
- 停止动画或者是其他正在运行的操作,那些都会导致CPU的浪费.
- 提交在用户离开时期待保存的内容(例如邮件草稿).
- 释放系统资源,例如broadcast receivers, sensors (比如GPS), 或者是其他任何会影响到电量的资源。
onStop()
- 当activity调用
onStop()
方法, activity不再可见,并且应该释放那些不再需要的所有资源。一旦activity停止了,系统会在需要内存空间时摧毁它的实例。极端情况下,系统会直接杀死我们的app进程,并不执行activity的onDestroy()
回调方法, 因此我们需要使用onStop()
来释放资源,从而避免内存泄漏。
所以我们应该使用onStop()
来执行那些耗时的释放资源的操作,例如往数据库写信息。
-无论什么原因导致activity停止,系统总是会在onStop()
之前调用onPause()
方法。
onDestroy()
大多数app并不需要实现这个方法,因为局部类的references会随着activity的销毁而销毁,并且我们的activity应该在onPause()
与onStop()
中执行清除activity资源的操作。然而,如果activity含有在onCreate调用时创建的后台线程,或者是其他有可能导致内存泄漏的资源,则应该在OnDestroy()
时进行资源清理,杀死后台线程。
除非程序在onCreate()
方法里面就调用了finish()
方法,系统通常是在执行了onPause()
与onStop()
之后再调用onDestroy()
。在某些情况下,例如我们的activity只是做了一个临时的逻辑跳转的功能,它只是用来决定跳转到哪一个activity,这样的话,需要在onCreate里面调用finish
方法,这样系统会直接调用onDestory
,跳过生命周期中的其他方法。
与Activity生命周期结合的应用场景
- 与广播(Broadcast)结合
在onResume
注册广播(registerLinstener),在onPause
注销广播(unregisterLinstener)。
例如:做”摇一摇”功能(传感器)、监听网络变化,就可以在onResume中注册监听,在onPause里注销掉,已节省资源提高效率。 - 与服务(Service)结合
在onStartCommand
绑定服务(bindService),在onStop
中取消绑定(unbindService)。
例如:需要通过Service定时更新UI上的数据,而Activity的可见周期在onStart
与onStop
之间,那么就可以再onStart
时启动服务,在onStop
时停止服务。为了节约系统资源,除了提高用户体验以外,开发人员应尽可能的优化程序。 - 与Cursor结合
使用managedQuery让Activity
帮你管理Cursor
的生命周期,不用自己去close。 - 释放资源
可以在onDestory
中释放一些资源。比如可以在onDestory
时调用MediaPlayer的release。
AlarmManager定时启动Service
private static AlarmManager am;
private static PendingIntent pendingIntent;
/**
* 使用 AlarmManager 来 定时启动服务
*/
public static void startPendingIntent(Context context) {
am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyService.class);//启动示例Service
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
long interval = DateUtils.MINUTE_IN_MILLIS * 30;// 30分钟一次
long firstWake = System.currentTimeMillis() + interval;
am.setRepeating(AlarmManager.RTC, firstWake, interval, pendingIntent);
}
public static void stopPendingIntent() {
if (pendingIntent != null) {
if ( am == null) {
am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
}
am.cancel(pendingIntent);
pendingIntent.cancel();
}
};
参考:android 使用AlarmManager定时启动service
Timer与AlarmManager的区别
通过调用Thread类的start()
方法来启动一个线程,这时此线程处于就绪(可运行)状态,但此时并没有运行,它需要CPU时间片
。一旦得到CPU时间片,就会执行run()
方法。run()
的方法体称为线程体
,它包含了要执行的这个线程的内容,run()
方法运行结束,此线程也随即终止。
Android 平台上常用的定时器主要有两个:
- Java的Timer
- Android的AlarmManager
Timer
Java的Timer
类可以用来计划需要循环执行的任务。
简单的说,一个Timer
内部封装装了“一个Thread
”和“一个TimerTask
队列”,这个队列按照一定的方式将任务排队处理。封装的Thread
在Timer
的构造方法调用时被启动,这个Thread
的run
方法按照条件去循环这个TimerTask
队列,然后调用TimerTask
的run
方法。
但是,如果CPU进入了休眠状态,那么这个thread将会因为失去CPU时间片而阻塞,从而造成我们需要的定时任务失效。上述定时任务失效的场景分析:假设定时任务的条件是到了时间xx:yy才能执行,但由于cpu休眠造成线程阻塞的关系,当前系统时间超过了这个时间,即便CPU从终端中恢复了,那么由于条件不满足,定时任务在这一次自然就失效了。
解决方案:它需要用WakeLock
让CPU保持唤醒状态。但这样会大量消耗手机电量,大大减短手机待机时间。这种方式不能满足我们的需求。
注:TimerTask
实现Runnable
接口,但它的run
方法只是简单的在Timer
中封装的Thread
中被调用,并未将其放在其它线程中执行。也就是说timer
是单线程执行的,那么问题来了,为何要这么费劲的封装一个Runnable接口又不进行多线程调用?
AlarmManager
AlarmManager
是Android 系统封装的用于管理RTC
的模块,RTC(Real Time Clock)
是一个独立的硬件时钟,可以在 CPU休眠时正常运行,在预设的时间到达时,通过中断唤醒CPU。这意味着,如果我们用 AlarmManager
来定时执行任务,CPU 可以正常的休眠,只有在需要运行任务时醒来一段很短的时间。
Switch开关
Switch mSwitch = (Switch)view.findViewById(R.id.setting_switch_notification_word);
mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if (isChecked) {
// switch on,开启通知栏单词
intentNotiService = new Intent(mContext, ServiceNotification.class);
pendingIntentNotiService = PendingIntent.getService(mContext, 0, intentNotiService, 0);
minute = 1;
startPendingIntent(pendingIntentNotiService);
} else {
//switch off,关闭通知栏单词
stopPendingIntent(pendingIntentNotiService);
}
}
});
2015-8-21
解决定时启动通知服务时pop up无效
在Service的onStartCommand()
里更新每次Notification需要更新的内容,如单词信息。无需改动的信息在onCreate()
里初始化。
-
原代码:
@Override public int onStartCommand(Intent intent, int flags, int startId) { mBuilder .setContentText(WordsDB.wordClass.toString()) //测试用的单词信息 .setWhen(System.currentTimeMillis()) //更新的时间 .setTicker(WordsDB.wordClass.toString()); //在通知栏动画向上弹出 startForeground(notifyID, mBuilder.build()); //display in "ongoing" Log.d("通知栏单词", WordsDB.wordClass.toString()); return super.onStartCommand(intent, flags, startId); }
按钮点击时会更新notification,也会在通知栏弹出提示。
但使用AlarmManager
定时启动该service时,会更新内容,但不会在通知栏弹出提示,只能自己打开通知栏才能查看到更新。 -
改动:使用
NotificationManager.notify()