FrameLayout(框架布局)
此布局是五种布局中最简单的布局,Android中并没有对child view的摆布进行控制,这个布局中所有的控件都会默认出现在视图的左上角,我们可以使用android:layout_margin,android:layout_gravity等属性去控制子控件相对布局的位置。
LinearLayout(线性布局)
一行(或一列)只控制一个控件的线性布局,所以当有很多控件需要在一个界面中列出时,可以用LinearLayout布局。 此布局有一个需要格外注意的属性:android:orientation=“horizontal|vertical。
android:orientation="horizontal
时,说明你希望将水平方向的布局交给LinearLayout ,其子元素的android:layout_gravity="right|left"
等控制水平方向的gravity值都是被忽略的,此时LinearLayout中的子元素都是默认的按照水平从左向右来排,我们可以用android:layout_gravity="top|bottom"
等gravity值来控制垂直展示。android:orientation="vertical
时,LinearLayout对其子元素展示上的的处理方式。AbsoluteLayout(绝对布局)
可以放置多个控件,并且可以自己定义控件的x,y位置
RelativeLayout(相对布局)
这个布局也是相对自由的布局,Android 对该布局的child view的 水平layout& 垂直layout做了解析,由此我们可以FrameLayout的基础上使用标签或者Java代码对垂直方向 以及 水平方向 布局中的views进行任意的控制.
android:layout_centerInParent="true|false"
android:layout_centerHorizontal="true|false"
android:layout_alignParentRight="true|false"
TableLayout(表格布局)
将子元素的位置分配到行或列中,一个TableLayout由许多的TableRow组成
使用android:launchMode="standard|singleInstance|singleTask|singleTop"
来控制Acivity任务栈。
任务栈是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会一个一个的出栈,并且调用其onDestory()方法。如果栈内没有Activity,那么系统就会回收这个栈,每个APP默认只有一个栈,以APP的包名来命名.
Activity的堆栈管理以ActivityRecord为单位,所有的ActivityRecord都放在一个List里面.可以认为一个ActivityRecord就是一个Activity栈
有a、b两个Activity,当从a进入b之后一段时间,可能系统会把a回收,这时候按back,执行的不是a的onRestart而是onCreate方法,a被重新创建一次,这是a中的临时数据和状态可能就丢失了。
可以用Activity中的onSaveInstanceState()回调方法保存临时数据和状态,这个方法一定会在活动被回收之前调用。方法中有一个Bundle参数,putString()、putInt()等方法需要传入两个参数,一个键一个值。数据保存之后会在onCreate中恢复,onCreate也有一个Bundle类型的参数。
示例代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里,当Acivity第一次被创建的时候为空
//所以我们需要判断一下
if( savedInstanceState != null ){
savedInstanceState.getString("anAnt");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("anAnt","Android");
}
一、onSaveInstanceState (Bundle outState)
当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。
注意上面的双引号,何为“容易”?言下之意就是该activity还没有被销毁,而仅仅是一种可能性。这种可能性有哪些?通过重写一个activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我们可以清楚地知道当某个activity(假定为activity A)显示在当前task的最上层时,其onSaveInstanceState方法会在什么时候被执行,有这么几种情况:
1、当用户按下HOME键时。
这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
2、长按HOME键,选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从activity A中启动一个新的activity时。
5、屏幕方向切换时,例如从竖屏切换到横屏时。(如果不指定configchange属性) 在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。另外,需要注意的几点:
1.==布局中的每一个View默认实现了onSaveInstanceState()方法,这样的话,这个UI的任何改变都会自动地存储和在activity重新创建的时候自动地恢复。但是这种情况只有在你为这个UI提供了唯一的ID之后才起作用,如果没有提供ID,app将不会存储它的状态==。
2.由于默认的onSaveInstanceState()方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的状态信息,你应该在执行任何代码之前都调用父类的onSaveInstanceState()方法(super.onSaveInstanceState())。 既然有现成的可用,那么我们到底还要不要自己实现onSaveInstanceState()?这得看情况了,如果你自己的派生类中有变量影响到UI,或你程序的行为,当然就要把这个变量也保存了,那么就需要自己实现,否则就不需要。
3.由于onSaveInstanceState()方法调用的不确定性,你应该只使用这个方法去记录activity的瞬间状态(UI的状态)。==不应该用这个方法去存储持久化数据==。当用户离开这个activity的时候应该在onPause()方法中存储持久化数据(例如应该被存储到数据库中的数据)。
4.==onSaveInstanceState()如果被调用,这个方法会在onStop()前被触发,但系统并不保证是否在onPause()之前或者之后触发==。
二、onRestoreInstanceState (Bundle outState)
至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,(本人注:我昨晚调试时就发 现原来不一定成对被调用的!)
onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被 调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行
另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。 还有onRestoreInstanceState在onstart之后执行。 至于这两个函数的使用,给出示范代码(留意自定义代码在调用super的前或后):
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
通过查询Intent/Bundle的API文档,我们可以获知,Intent/Bundle支持传递基本类型的数据和基本类型的数组数据,以及String/CharSequence类型的数据和String/CharSequence类型的数组数据。而对于其它类型的数据貌似无能为力,其实不然,我们可以在Intent/Bundle的API中看到Intent/Bundle还可以传递Parcelable(包裹化,邮包)和Serializable(序列化)类型的数据,以及它们的数组/列表数据。
所以要让非基本类型和非String/CharSequence类型的数据通过Intent/Bundle来进行传输,我们就需要在数据类型中实现Parcelable接口或是Serializable接口。
1.在Context中通过public boolean bindService(Intent service,ServiceConnection conn,int flags) 方法来进行Service与Context的关联并启动,并且Service的生命周期依附于Context(不求同时同分同秒生!但求同时同分同秒屎!!)。
2.通过public ComponentName startService(Intent service)方法去启动一个Service,此时Service的生命周期与启动它的Context无关。
3.要注意的是,whatever,都需要在xml里注册你的Service,就像这样:
<service
android:name=".packnameName.youServiceName"
android:enabled="true" />
ContentProvider是用于将数据共享给其他应用。例如在同一部手机里面,有两个APP,第一个APP要访问第二个APP的数据。此时,第二个APP就需要设置ContentProvider。这样,第一个APP就能通过Uri访问第二个APP的数据。
第二部手机的设置,首先需要一个类来继承ContentProvider这个类,继承后需要实现onCreate,query,getType,insert,delete,update这个几个方法,如其名,其作用是给第一个APP调用的,至于第一个APP是如何调用的,下面会提到。
给出一个继承ContentProvider的例子:
1.先是提供的数据类型等数据的类。
package org.juetion.cp;
import android.net.Uri;
import android.provider.BaseColumns;
/**
* 提供的数据类型等数据。
* Created by juetionke on 13-12-21.
*/
public class MyProviderMetaData {
public static final String AUTHORIY = "org.juetion.cp.MyContentProvider";
/**
* 数据库名称
*/
public static final String DATABASE_NAME = "MyProvider.db";
/**
* 数据库版本
*/
public static final int DATABASE_VERSION = 1;
/**
* 表名
*/
public static final String USERS_TABLE_NAME = "users";
/**
* 继承了BaseColumns,所以已经有了_ID
*/
public static final class UserTableMetaData implements BaseColumns {
/**
* 表名
*/
public static final String TABLE_NAME = "users";
/**
* 访问该ContentProvider的URI
*/
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORIY + "/users");
/**
* 该ContentProvider所返回的数据类型定义
*/
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/org.juetion.user";
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/org.juetion.user";
/**
* 列名
*/
public static final String USER_NAME = "name";
public static final String USER_AGE = "age";
/**
* 默认的排序方法
*/
public static final String DEFAULT_SORT_ORDER = "_id desc";
}
}
2,继承ContentProvider的类:
package org.juetion.cp;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import org.juetion.sqlite3.DatabaseHelper;
import java.util.HashMap;
/**
* Created by juetionke on 13-12-21.
*/
public class MyContentProvider extends ContentProvider {
/**
* 定义规则
*/
public static final UriMatcher uriMatcher;
public static final int USERS_COLLECTION = 1;//用于标记
public static final int USERS_SINGLE = 2;//用于标记
private DatabaseHelper databaseHelper;//这里的数据共享是共享Sqlite里的数据,当然,可以试用其他,如文本数据共享。
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);//试用一个没有规则的Uri。然后下面自己匹配。
uriMatcher.addURI(MyProviderMetaData.AUTHORIY,"/users",USERS_COLLECTION);//自己定义的规则,有点像路由器,是uri匹配的方案。
uriMatcher.addURI(MyProviderMetaData.AUTHORIY,"/users/#",USERS_SINGLE);//同上。
}
/**
* 为列定义别名
*/
public static HashMap usersMap;
static {
usersMap = new HashMap();
usersMap.put(MyProviderMetaData.UserTableMetaData._ID, MyProviderMetaData.UserTableMetaData._ID);
usersMap.put(MyProviderMetaData.UserTableMetaData.USER_NAME, MyProviderMetaData.UserTableMetaData.USER_NAME);
usersMap.put(MyProviderMetaData.UserTableMetaData.USER_AGE, MyProviderMetaData.UserTableMetaData.USER_AGE);
}
@Override
public boolean onCreate() {
Log.i("juetion","onCreate");
databaseHelper = new DatabaseHelper(getContext(), MyProviderMetaData.DATABASE_NAME);//这里的实现,常见前篇关于Sqlite的文章。
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.i("juetion","query");
SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();//写入查询条件,有点像Hibernate。
switch (uriMatcher.match(uri)) {//判断查询的是单个数据还是多个数据。
case USERS_SINGLE:
sqLiteQueryBuilder.setTables(MyProviderMetaData.UserTableMetaData.TABLE_NAME);//需要查询的表
sqLiteQueryBuilder.setProjectionMap(usersMap);//列的别名定义
sqLiteQueryBuilder.appendWhere(MyProviderMetaData.UserTableMetaData._ID + "=" + uri.getPathSegments().get(1));
//查询条件,uri.getPathSegments().get(1),getPathSegments是将内容根据/划分成list。
break;
case USERS_COLLECTION:
sqLiteQueryBuilder.setTables(MyProviderMetaData.UserTableMetaData.TABLE_NAME);
sqLiteQueryBuilder.setProjectionMap(usersMap);
break;
}
String orderBy;//判断sortOrder是否为空,加入默认。
if (TextUtils.isEmpty(sortOrder)) {
orderBy = MyProviderMetaData.UserTableMetaData.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
Cursor cursor = sqLiteQueryBuilder.query(sqLiteDatabase, projection, selection, selectionArgs, null, null, sortOrder);//可以使用下面的方法,不过此时sqLiteDatabase将会没有用。
//Cursor cursor = sqLiteDatabase.query(MyProviderMetaData.UserTableMetaData.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy);
cursor.setNotificationUri(getContext().getContentResolver(),uri);
return cursor;
}
/**
* 根据传入的URI,返回URI说表示的数据类型
* @param uri
* @return
*/
@Override
public String getType(Uri uri) {
Log.i("juetion","getType");
switch (uriMatcher.match(uri)) {//匹配uri的规则
case USERS_COLLECTION:
return MyProviderMetaData.UserTableMetaData.CONTENT_TYPE;
case USERS_SINGLE:
return MyProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
default:
throw new IllegalArgumentException("Unknown URI" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i("juetion","insert");
SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
long rowId = sqLiteDatabase.insert(MyProviderMetaData.UserTableMetaData.TABLE_NAME, null, values);
if (rowId > 0) {
Uri insertUserUri = ContentUris.withAppendedId(MyProviderMetaData.UserTableMetaData.CONTENT_URI, rowId);//简单来说就是字符串拼凑一下。只不过是uri专用的。
//通知监听器
getContext().getContentResolver().notifyChange(insertUserUri,null);
return insertUserUri;
}else
throw new IllegalArgumentException("Failed to insert row into" + uri);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i("juetion","delete");
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.i("juetion","update");
return 0;
}
}
这两个类里面都有的对应的注释,都能看懂吧?
还有重要的一点,再第二个APP的AndroidManifest.xml里面需要添加
<provider
android:authorities="org.juetion.cp.MyContentProvider"
android:name="org.juetion.cp.MyContentProvider"/>
以上代码都是在第二个APP里面的。
--------我是可爱的分界线------------
以下代码是在第一个APP里面的。
关于使用。只需要将uri的string提供给第一个APP。
例如在第一个APP的Activity调用数据插入:
ContentValues contentValues = new ContentValues();
contentValues.put("name","zhangsan");
contentValues.put("age",19);
Uri uri = getContentResolver().insert(Uri.parse("content://org.juetion.cp.MyContentProvider/users"),contentValues);
Log.i("juetion", "insert uri-->" + uri.toString());
例如在第一个APP的Activity调用数据的查询:
Cursor cursor = getContentResolver().query(Uri.parse("content://org.juetion.cp.MyContentProvider/users"),
new String[]{"name", "age"},
null, null, null);
while (cursor.moveToNext()) {
Log.i("juetion", cursor.getString(cursor.getColumnIndex("name")));
}
正如上面的例子,使用的时候是在Activity里面调用getContentResolver()的。
tween 补间动画。通过指定View的初末状态和变化时间、方式,对View的内容完成一系列的图形变换来实现动画效果。 Alpha Scale Translate Rotate。
frame 帧动画 AnimationDrawable 控制 animation-list xml布局
PropertyAnimation 属性动画
SQLite:SQLite是一个轻量级的数据库,支持基本的SQL语法,是常被采用的一种数据存储方式。 Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的api
SharedPreference: 除SQLite数据库外,另一种常用的数据存储方式,其本质就是一个xml文件,常用于存储较简单的参数设置。
File: 即常说的文件(I/O)存储方法,常用语存储大数量的数据,但是缺点是更新数据将是一件困难的事情。
ContentProvider: Android系统中能实现所有应用程序共享的一种数据存储方式,由于数据通常在各应用间的是互相私密的,所以此存储方式较少使用,但是其又是必不可少的一种存储方式。例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储。每个Content Provider都会对外提供一个公共的URI(包装成Uri对象),如果应用程序有数据需要共享时,就需要使用Content Provider为这些数据定义一个URI,然后其他的应用程序就通过Content Provider传入这个URI来对数据进行操作。
一、概述
SQLite是Android系统的核心数据存储服务之一,它是一个==轻型的嵌入式数据库==,占用非常少的资源却能提供很好很快的数据存取服务,许多大型的需要数据存储的android项目都有用到SQLite(也可以用于桌面应用程序)。
下面介绍一下SQLite的创建数据库、表的操作,以及基本的增删改查操作。
二、基本操作API简介
在Android中,SQLiteDatabase类提供了SQLite的底层API,但在使用SQLite数据库时,我们往往不会直接操作SQLiteDatabase这个类,而是自己创建一个继承自SQLitOpenHelper的子类来实现数据库操作。这样做的目的一是为了以后如果数据库升级不至于要改动太多代码,已实现封装;二则是为了我们使用更方便。
1、创建数据库和表
SQLiteOpenHelper是一个抽象类,在这个类里有两个抽象方法,OnCreate和OnUpgrade,前者用于第一次创建数据库,后者用于数据库升级,创建类DBServices如下:
public class DBServices extends SQLiteOpenHelper{
final static int version = 1;
final static String dbName = "plan";
public DBServices(Context context){
super(context,dbName,null,version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
//创建今日计划表
String create_today_plan_sql = "CREATE TABLE [_today_plan] ("
+ "[_Date] varchar(10) not null,"
+ "[Item] varchar(200),"
+ "[Check] varchar(5) )";
db.execSQL(create_today_plan_sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
示例里定义两个变量,一个是数据库的版本号,一个是数据库名。当Android应用运行时,SQLiteOpenHelper会先检查是否已经存在数据库,如果不存在,就创建数据库,然后打开数据库,最后调用OnCreate方法,所以我们需要再OnCreate中创建表(视图等);如果数据库已存在,而版本号比上次创建的数据库版本号高,就调用OnUpgrade,用于升级。
2、数据的增——insert
在创建了数据库和表之后,我们就可以给数据库和表添加数据了。
添加数据的操作和其他数据库一样,也是使用insert,只是SQLite的insert是函数,而且使用起来非常方便,下面是方法(属于上面的DBServices类,附录里有完整代码):
public void insert(String table, String nullColumnHack, ContentValues values){
SQLiteDatabase db = this.getWritableDatabase();
db.insert(table, nullColumnHack, values);
}
参数说明:
table:表名,直接使用字符串指定;
nullColumnHack:指定null值的列,SQLite里不允许空行,使用这个参数可以指定一个列的值为null,当存入行为空时,这个列的值就被指定为null;
values:使用类似map键值对映射的数据结构ContentValues来指定插入的数据
添加数据示例:
String[] args = {
today,
content,
Boolean.toString(checked)
};
String[] column = {
"[_Date]",
"[Item]",
"[Check]"
};
//数据库中添加数据
ContentValues c = new ContentValues();
for(int i=0;i"_today_plan", null, c);
3、数据的删——delete
删除和添加一样,也是通过传入参数调用方法来实现,方法:
public void delete(String table , String whereClause , String[] whereArgs){
SQLiteDatabase db = this.getWritableDatabase();
db.delete(table, whereClause, whereArgs);
Log.d("Delete",whereClause);
}
参数说明:
table:表名;
whereClause:可选,指定删除条件,相当于SQL语句WHERE语句之后的类容,可通过?来指定参数;
whereArgs:当whereClause指定了?参数,这个字符串数组里就是?所代表的参数,个数应与?数一致;
删除数据示例:
String args[] ={
today,
content,
Boolean.toString(checked)
};
dbServices.delete("_today_plan", "[_Date]=? and [Item]=? and [Check]=?"
,args);
4、数据的修改——update
修改与添加、删除相差不多,下面是update方法:
public void update(String table, ContentValues values,
String whereClause, String[] whereArgs){
SQLiteDatabase db = this.getWritableDatabase();
db.update(table, values, whereClause, whereArgs);
}
参数说明:
table:表名;
values:同上,是需要修改的列和值的映射集合;
whereClause:修改的行所需符合的条件;
whereArgs:指定条件里的参数;
修改数据示例:
String args[] ={
today,
content,
Boolean.toString(!m)
};
ContentValues c = new ContentValues();
c.put("[Check]", Boolean.toString(m));
dbServices.update("_today_plan", c,"[_Date]=? and [StartTime]=? and [Item]=? and [Check]=?"
,args);
5、数据的查询——read
在这里就和前面有所不同了,读取数据所用的方法是直接执行查询语句,获取游标,然后通过游标来遍历数据库,方法如下:
public Cursor read(String sql ,String[] args){
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
Log.d("Database",cursor.getColumnName(0));
return cursor;
}
方法说明:
请注意:在这里db获取的是只读的数据库(getReadableDatabase),而在上述三种操作里都是使用的可写数据库(getWritableDatabase);至于游标的用法此处就不再赘述,只需看一下API的名字就能掌握基本用法,但最后一定要记得将游标关闭(close)!
三、附录
附录了DBServices类,仅作参考
package com.plan;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBServices extends SQLiteOpenHelper{
final static int version = 1;
final static String dbName = "plan";
public DBServices(Context context){
super(context,dbName,null,version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
//创建今日计划表
String create_today_plan_sql = "CREATE TABLE [_today_plan] ("
+ "[_Date] varchar(10) not null,"
+ "[Item] varchar(200),"
+ "[Check] varchar(5) )";
db.execSQL(create_today_plan_sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
public Cursor read(String sql ,String[] args){
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
Log.d("Database",cursor.getColumnName(0));
return cursor;
}
public void insert(String table, String nullColumnHack, ContentValues values){
SQLiteDatabase db = this.getWritableDatabase();
db.insert(table, nullColumnHack, values);
}
public void delete(String table , String whereClause , String[] whereArgs){
SQLiteDatabase db = this.getWritableDatabase();
db.delete(table, whereClause, whereArgs);
Log.d("Delete",whereClause);
}
public void update(String table, ContentValues values,
String whereClause, String[] whereArgs){
SQLiteDatabase db = this.getWritableDatabase();
db.update(table, values, whereClause, whereArgs);
}
}
有关SQLite的高级特性,如索引、视图以及触发器等,大家可以去看看SQlite的官方文档。
上述类在手机重启时,数据库会重新创建,原因是数据库没有关闭(但因为要提供游标,所以控制起来会比较困难),所以这里提出一个解决办法,就是数据库的操作(建表、增、删、改)均使用事务方式,示例如下:
db.beginTransaction(); //事务开始
//建表、增、删、改、查
db.setTransactionSuccessful(); //事务成功
db.endTransaction(); //提交事务
在Application中定义一个static常量,赋值为-1,在欢迎界面改为0,如果被强杀,application重新初始化,在父类Activity判断该常量的值。
如果在每一个Activity的onCreate里判断是否被强杀,冗余了,封装到Activity的父类中,如果被强杀,跳转回主界面,如果没有被强杀,执行Activity的初始化操作,给主界面传递intent参数,主界面会调用onNewIntent方法,在onNewIntent跳转到欢迎页面,重新来一遍流程。
res 目录下面有很多文件,例如 drawable,mipmap,raw 等。res 下面除了 raw 文件不会被压缩外,其余文件都会被压缩。同时 res目录下的文件可以通过R 文件访问。Asset 也是用来存储资源,但是 asset 文件内容只能通过路径或者 AssetManager 读取。
分两种情况,启动应用 和 普通Activity 启动应用 :Application 的构造方法,onCreate 方法中不要进行耗时操作,数据预读取(例如 init 数据) 放在异步中操作 启动普通的Activity:A 启动B 时不要在 A 的 onPause 中执行耗时操作。因为 B 的 onResume 方法必须等待 A 的 onPause 执行完成后才能运行
如何自定义控件:
自定义属性的声明和获取
布局onLayout(ViewGroup)
绘制onDraw
onTouchEvent
onInterceptTouchEvent(ViewGroup)
状态的恢复与保存
Retrofit
EventBus
Android5.0新特性:
Android6.0新特性
Android7.0新特性
IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题
优点:
查看每个应用程序最高可用内存:
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
Log.d("TAG", "Max memory is " + maxMemory + "KB");
构建工具、Groovy语法、Java
Jar包里面只有代码,aar里面不光有代码还包括代码还包括资源文件,比如 drawable 文件,xml 资源文件。对于一些不常变动的 Android Library,我们可以直接引用 aar,加快编译速度
首先是看书和看视频敲代码,然后看大牛的博客,做一些项目,向github提交代码,觉得自己API掌握的不错之后,开始看进阶的书,以及看源码,看完源码学习到一些思想,开始自己造轮子,开始想代码的提升,比如设计模式,架构,重构等。