版权声明:本文出自汪磊的博客,转载请务必注明出处。

本篇博客只是记录一下ContentProvider的使用(这部分工作中用的比较少总是忘记),没有太深入研究已经熟练掌握使用方式,想深入了解内部机制的同学可以绕过了。

一、ContentProvider概述

Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的。为了增强程序之间的数据共享能力,Android系统提供了像SharedPreferences这类简单的跨越程序边界的访问方法,但这些方法都存在一定的局限性,提供数据的能力有限,安卓系统提供了另一种跨进程提供数据的方式也就ContentProvider,ContentProvider翻译过来叫做:数据提供者,是应用程序之间共享数据的一种接口机制,其他应用程序则可以在不知道数据来源的情况下,对共享数据进行增删改查等操作。在Android系统中,许多系统内置的数据也是通过ContentProvider提供给用户使用,例如通讯录、音视频图像文件等。

二、ContentProvider调用

调用者不能直接调用ContentProvider的接口函数,需要通过ContentResolver对象,通过URI间接调用ContentProvider,Android系统根据URI确定处理这个查询的ContentProvider。

三、通用资源标识符URI

URI可以理解为一个个网站的访问地址,比如百度有百度的地址,阿里有阿里的地址,同样在安卓系统中每个ContentProvider也都有自己的访问地址,ContentProvider使用的URI语法结构如下:

  1.  content://<authority>/<data_path>/<id>
  • content:// 是通用前缀,表示该UIR用于ContentProvider定位资源。
  • < authority > 是授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此一般< authority >都由类的小写全称组成,以保证唯一性。
  • < data_path > 是数据路径,用来确定请求的是哪个数据集。如果ContentProvider只提供一个数据集,数据路径则可以省略;如果ContentProvider提供多个数据集,数据路径必须指明具体数据集。数据集的数据路径可以写成多段格式,例如people/delete和people/insert。
  • < id > 是数据编号,用来唯一确定数据集中的一条记录,匹配数据集中_ID字段的值。如果请求的数据不只一条,< id >可以省略。

、创建ContentProvider

创建一个类继承ContentProvider,重载6个函数,分别为onCreate(),getType(),insert()、delete()、update()、query()。

onCreate()
一般用来初始化底层数据集和建立数据连接等工作

getType()
用来返回指定URI的MIME数据类型,若URI是单条数据,则返回的MIME数据类型以vnd.android.cursor.item开头;若URI是多条数据,则返回的MIME数据类型以vnd.android.cursor.dir/开头。

insert()、delete()、update()、query()
用于对数据集的增删改查操作。

、UriMatcher类

UriMatcher类其实就是一个工具类,用于匹配用户传递进来的Uri。

示例:

  1. private static final int PRESON_INSERT_CODE = 0;
  2. private static final int PERSON_DELETE_CODE = 1;
  3. private static final int PERSON_UPDATE_CODE = 2;
  4. private static final int PERSON_QUERY_ALL_CODE = 3;
  5. private static final int PERSON_QUERY_ITEM_CODE = 4;
  6. //
  7. private static UriMatcher uriMatcher;
  8. private PersonSQLiteOpenHelper mOpenHelper;
  9.  
  10. static {
  11.  uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  12.  
  13. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_INSERT,
  14. PRESON_INSERT_CODE);
  15. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_DELETE,
  16. PERSON_DELETE_CODE);
  17. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_UPDATE,
  18. PERSON_UPDATE_CODE);
  19. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ALL,
  20. PERSON_QUERY_ALL_CODE);
  21. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ITEM,
  22. PERSON_QUERY_ITEM_CODE);
  23. }

UriMatcher的构造函数中,UriMatcher.NO_MATCH是URI无匹配时的返回代码,值为-1。 addURI() 方法用来添加新的匹配项,语法为:

  1. public void addURI(String authority, String path, int code)

其中authority表示匹配的授权者名称,path表示数据路径,code表示匹配成功时的返回代码。

使用示例:

  1. @Override
  2. public String getType(Uri uri) {
  3. switch (uriMatcher.match(uri)) {
  4. case PERSON_QUERY_ALL_CODE: // 返回多条的MIME-type
  5. return "vnd.android.cursor.dir/person";
  6. case PERSON_QUERY_ITEM_CODE: // 返回单条的MIME-TYPE
  7. return "vnd.android.cursor.item/person";
  8. default:
  9. break;
  10. }
  11. return null;
  12. }

、ContentObserver简要介绍

ContentObserver——内容观察者,观察)特定Uri引起的数据库的变化,继而做一些相应的处理,当ContentObserver所观察的Uri发生变化时,便会触发它回调onChange方法。

ContentObserver的编写:创建一个类继承自ContentObserver,重写onChange,监听的的url数据发生变化时就会回调此方法。

示例:

  1. public class PersonContentObserver extends ContentObserver {
  2.  
  3. //
  4. private static final String TAG = "TestCase";
  5. private Context mContext;
  6.  
  7. public PersonContentObserver(Handler handler,Context mContext) {
  8. super(handler);
  9. this.mContext = mContext;
  10. }
  11.  
  12. @Override
  13. public void onChange(boolean selfChange) {
  14. //
  15. 15 }
  16.  
  17. }

ContentObserver的注册:ContentObserver的注册是由ContentResolver来完成的。

示例:

  1. public class MainActivity extends Activity {
  2.  
  3. private PersonContentObserver mContentObserver;
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9.  
  10. mContentObserver = new PersonContentObserver(new Handler(),this);
  11. getContentResolver().registerContentObserver(Person.CONTENT_URI_DELETE,
  12. true, mContentObserver);
  13. }
  14.  
  15. @Override
  16. protected void onDestroy() {
  17. //
  18. super.onDestroy();
  19.  
  20. getContentResolver().unregisterContentObserver(mContentObserver);
  21. }
  22. }

void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendents, @NonNull ContentObserver observer)参数说明

uri:监测的uri地址

notifyForDescendents:为true 表示可以同时匹配其派生的Uri,false只精确匹配当前Uri.

observer:就是我们自己编写的ContentObserve了。

、Demo源码示例

1,编写ContentProvider工程,此工程演示ContentProvider的创建以及ContentObserver的使用

工程目录:

先来看看Person类:

  1. public class Person {
  2.  
  3. public static final String AUTHORITY = "com.wanglei.personcontentprovider";
  4. //
  5. public static final String PATH_INSERT = "person/insert";
  6. public static final String PATH_DELETE = "person/delete";
  7. public static final String PATH_UPDATE = "person/update";
  8. public static final String PATH_QUERY_ALL = "person/queryAll";
  9. public static final String PATH_QUERY_ITEM = "person/query/#";
  10. //
  11. public static final Uri CONTENT_URI_INSERT = Uri.parse("content://" + AUTHORITY + "/" + PATH_INSERT);
  12. public static final Uri CONTENT_URI_DELETE = Uri.parse("content://" + AUTHORITY + "/" + PATH_DELETE);
  13. public static final Uri CONTENT_URI_UPDATE = Uri.parse("content://" + AUTHORITY + "/" + PATH_UPDATE);
  14. public static final Uri CONTENT_URI_QUERY_ALL = Uri.parse("content://" + AUTHORITY + "/" + PATH_QUERY_ALL);
  15. public static final Uri CONTENT_URI_QUERY_ITEM = Uri.parse("content://" + AUTHORITY + "/" + PATH_QUERY_ITEM);
  16. //
  17. public static final String KEY_ID = "_id";
  18. public static final String KEY_NAME = "name";
  19. public static final String KEY_AGE = "age";
  20. }

此类只是定义了一些常量,由于只是演示,为了方便没有写成正规的javaBean.很简单,没有多余需要解释的。

接下来看下PersonSQLiteOpenHelper类,此类就是数据库的创建了,很简单,如下:

  1. /**
  2. * @author andong 数据库帮助类, 用于创建和管理数据库的.
  3. */
  4. public class PersonSQLiteOpenHelper extends SQLiteOpenHelper {
  5.  
  6. //数据库名称
  7. private static final String DB_NAME = "person.db";
  8. //表名称
  9. public static final String TABLE_NAME = "person";
  10.  
  11. /**
  12. * 数据库的构造函数
  13. *
  14. * @param context
  15. *
  16. * name 数据库名称 factory 游标工程 version 数据库的版本号 不可以小于1
  17. */
  18. public PersonSQLiteOpenHelper(Context context) {
  19. super(context, DB_NAME, null, 1);
  20. }
  21.  
  22. /**
  23. * 数据库第一次创建时回调此方法. 初始化一些表
  24. */
  25. @Override
  26. public void onCreate(SQLiteDatabase db) {
  27.  
  28. // 操作数据库
  29. String sql = "create table " + TABLE_NAME + "(" + Person.KEY_ID
  30. + " integer primary key autoincrement, " + Person.KEY_NAME
  31. + " varchar(100), "+Person.KEY_AGE+" integer);";
  32. db.execSQL(sql); // 创建person表
  33. }
  34.  
  35. /**
  36. * 数据库的版本号更新时回调此方法, 更新数据库的内容(删除表, 添加表, 修改表)
  37. */
  38. @Override
  39. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  40.  
  41. db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
  42. onCreate(db);
  43. }
  44. }

接下来继续看下PersonContentProvider类:

  1. public class PersonContentProvider extends ContentProvider {
  2.  
  3. private static final int PRESON_INSERT_CODE = 0;
  4. private static final int PERSON_DELETE_CODE = 1;
  5. private static final int PERSON_UPDATE_CODE = 2;
  6. private static final int PERSON_QUERY_ALL_CODE = 3;
  7. private static final int PERSON_QUERY_ITEM_CODE = 4;
  8. //
  9. private static UriMatcher uriMatcher;
  10. private PersonSQLiteOpenHelper mOpenHelper;
  11.  
  12. static {
  13. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  14.  
  15. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_INSERT,
  16. PRESON_INSERT_CODE);
  17. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_DELETE,
  18. PERSON_DELETE_CODE);
  19. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_UPDATE,
  20. PERSON_UPDATE_CODE);
  21. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ALL,
  22. PERSON_QUERY_ALL_CODE);
  23. uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ITEM,
  24. PERSON_QUERY_ITEM_CODE);
  25. }
  26.  
  27. @Override
  28. public boolean onCreate() {
  29. mOpenHelper = new PersonSQLiteOpenHelper(getContext());
  30. return true;
  31. }
  32.  
  33. @Override
  34. public Cursor query(Uri uri, String[] projection, String selection,
  35. String[] selectionArgs, String sortOrder) {
  36. SQLiteDatabase db = mOpenHelper.getReadableDatabase();
  37. switch (uriMatcher.match(uri)) {
  38. case PERSON_QUERY_ALL_CODE: // 查询所有人的uri
  39. if (db.isOpen()) {
  40. Cursor cursor = db.query(PersonSQLiteOpenHelper.TABLE_NAME,
  41. projection, selection, selectionArgs, null, null,
  42. sortOrder);
  43. cursor.setNotificationUri(getContext().getContentResolver(), uri);
  44. return cursor;
  45. // db.close(); 返回cursor结果集时, 不可以关闭数据库
  46. }
  47. break;
  48. case PERSON_QUERY_ITEM_CODE: // 查询的是单条数据, uri末尾出有一个id
  49. if (db.isOpen()) {
  50.  
  51. long id = ContentUris.parseId(uri);
  52.  
  53. Cursor cursor = db.query(PersonSQLiteOpenHelper.TABLE_NAME,
  54. projection, Person.KEY_ID + " = ?", new String[] { id
  55. + "" }, null, null, sortOrder);
  56. cursor.setNotificationUri(getContext().getContentResolver(), uri);
  57. return cursor;
  58. }
  59. break;
  60. default:
  61. throw new IllegalArgumentException("uri不匹配: " + uri);
  62. }
  63. return null;
  64. }
  65.  
  66. @Override
  67. public String getType(Uri uri) {
  68. switch (uriMatcher.match(uri)) {
  69. case PERSON_QUERY_ALL_CODE: // 返回多条的MIME-type
  70. return "vnd.android.cursor.dir/person";
  71. case PERSON_QUERY_ITEM_CODE: // 返回单条的MIME-TYPE
  72. return "vnd.android.cursor.item/person";
  73. default:
  74. break;
  75. }
  76. return null;
  77. }
  78.  
  79. @Override
  80. public Uri insert(Uri uri, ContentValues values) {
  81.  
  82. switch (uriMatcher.match(uri)) {
  83. case PRESON_INSERT_CODE: // 添加人到person表中
  84. SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  85.  
  86. if (db.isOpen()) {
  87.  
  88. long id = db.insert(PersonSQLiteOpenHelper.TABLE_NAME, null,
  89. values);
  90.  
  91. db.close();
  92. Uri newUri = ContentUris.withAppendedId(uri, id);
  93. //通知内容观察者数据发生变化
  94. getContext().getContentResolver().notifyChange(newUri, null);
  95. return newUri;
  96. }
  97. break;
  98. default:
  99. throw new IllegalArgumentException("uri不匹配: " + uri);
  100. }
  101. return null;
  102. }
  103.  
  104. @Override
  105. public int delete(Uri uri, String selection, String[] selectionArgs) {
  106. switch (uriMatcher.match(uri)) {
  107. case PERSON_DELETE_CODE: // 在person表中删除数据的操作
  108. SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  109. if (db.isOpen()) {
  110. int count = db.delete(PersonSQLiteOpenHelper.TABLE_NAME,
  111. selection, selectionArgs);
  112. db.close();
  113. //通知内容观察者数据发生变化
  114. getContext().getContentResolver().notifyChange(uri, null);
  115. return count;
  116. }
  117. break;
  118. default:
  119. throw new IllegalArgumentException("uri不匹配: " + uri);
  120. }
  121. return 0;
  122. }
  123.  
  124. @Override
  125. public int update(Uri uri, ContentValues values, String selection,
  126. String[] selectionArgs) {
  127. switch (uriMatcher.match(uri)) {
  128. case PERSON_UPDATE_CODE: // 更新person表的操作
  129. SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  130. if (db.isOpen()) {
  131. int count = db.update(PersonSQLiteOpenHelper.TABLE_NAME,
  132. values, selection, selectionArgs);
  133. db.close();
  134. //通知内容观察者数据发生变化
  135. getContext().getContentResolver().notifyChange(uri, null);
  136. return count;
  137. }
  138. break;
  139. default:
  140. throw new IllegalArgumentException("uri不匹配: " + uri);
  141. }
  142. return 0;
  143. }
  144.  
  145. }

可以看到此ContentProvider内部操作对象就是person.db中的person表,并且对数据库操作完调用 getContext().getContentResolver().notifyChange(uri, null)通知对应内容观察者数据发生了变化。

PersonContentObserver类:

  1. public class PersonContentObserver extends ContentObserver {
  2.  
  3. //
  4. private static final String TAG = "TestCase";
  5. private Context mContext;
  6.  
  7. public PersonContentObserver(Handler handler,Context mContext) {
  8. super(handler);
  9. this.mContext = mContext;
  10. }
  11.  
  12. @Override
  13. public void onChange(boolean selfChange) {
  14. //
  15. Log.i(TAG, "PersonContentObserver");
  16. ContentResolver resolver = mContext.getContentResolver();
  17.  
  18. Cursor cursor = resolver
  19. .query(Person.CONTENT_URI_QUERY_ALL, new String[] {
  20. Person.KEY_ID, Person.KEY_NAME, Person.KEY_AGE }, null,
  21. null, "_id desc");
  22.  
  23. if (cursor != null && cursor.getCount() > 0) {
  24.  
  25. int id;
  26. String name;
  27. int age;
  28. while (cursor.moveToNext()) {
  29. id = cursor.getInt(cursor.getColumnIndex(Person.KEY_ID));
  30. name = cursor.getString(cursor.getColumnIndex(Person.KEY_NAME));
  31. age = cursor.getInt(cursor.getColumnIndex(Person.KEY_AGE));
  32. Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
  33. }
  34. cursor.close();
  35. }
  36. }
  37. }

在我们接收到数据发生变化的时候进行的操作是重新查询person表中所有数据。

最后在MainActivity中注册PersonContentObserver:

  1. public class MainActivity extends Activity {
  2.  
  3. private PersonContentObserver mContentObserver;
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9.  
  10. mContentObserver = new PersonContentObserver(new Handler(),this);
  11. getContentResolver().registerContentObserver(Person.CONTENT_URI_DELETE,
  12. true, mContentObserver);
  13. }
  14.  
  15. @Override
  16. protected void onDestroy() {
  17. //
  18. super.onDestroy();
  19.  
  20. getContentResolver().unregisterContentObserver(mContentObserver);
  21. }
  22. }

别忘了在清单文件中注册内容提供者:

  1. <provider
  2. android:name="com.wanglei.provider.PersonContentProvider"
  3. android:authorities="com.wanglei.personcontentprovider"
  4. android:exported="true" >
  5. </provider>

接下来我们就要编写新项目测试我们的PersonContentProvider能不能正常使用以及PersonContentObserver能不能检测到数据发生发生变化了。

编写UseContentProvider项目,结构如下:

其中Person类和上面的Person类是一样的。

test.java类就是测试类了,测试增删改查:

  1. public class test extends AndroidTestCase {
  2.  
  3. private static final String TAG = "TestCase";
  4.  
  5. public void testInsert() {
  6. // 内容提供者访问对象
  7. ContentResolver resolver = getContext().getContentResolver();
  8.  
  9. for (int i = 0; i < 10; i++) {
  10. //
  11. ContentValues values = new ContentValues();
  12. values.put(Person.KEY_NAME, "wanglei"+i);
  13. values.put(Person.KEY_AGE, i);
  14. Uri uri = resolver.insert(Person.CONTENT_URI_INSERT, values);
  15. Log.i(TAG, "uri: " + uri);
  16. long id = ContentUris.parseId(uri);
  17. Log.i(TAG, "添加到: " + id);
  18. }
  19. }
  20.  
  21. public void testDelete() {
  22.  
  23. // 内容提供者访问对象
  24. ContentResolver resolver = getContext().getContentResolver();
  25. String where = Person.KEY_ID + " = ?";
  26. String[] selectionArgs = { "3" };
  27. int count = resolver.delete(Person.CONTENT_URI_DELETE, where,
  28. selectionArgs);
  29. Log.i(TAG, "删除行: " + count);
  30. }
  31.  
  32. public void testUpdate() {
  33.  
  34. // 内容提供者访问对象
  35. ContentResolver resolver = getContext().getContentResolver();
  36.  
  37. ContentValues values = new ContentValues();
  38. values.put(Person.KEY_NAME, "lisi");
  39.  
  40. int count = resolver.update(Person.CONTENT_URI_UPDATE, values,
  41. Person.KEY_ID + " = ?", new String[] { "1" });
  42. Log.i(TAG, "更新行: " + count);
  43. }
  44.  
  45. public void testQueryAll() {
  46.  
  47. // 内容提供者访问对象
  48. ContentResolver resolver = getContext().getContentResolver();
  49.  
  50. Cursor cursor = resolver
  51. .query(Person.CONTENT_URI_QUERY_ALL, new String[] {
  52. Person.KEY_ID, Person.KEY_NAME, Person.KEY_AGE }, null,
  53. null, "_id desc");
  54.  
  55. if (cursor != null && cursor.getCount() > 0) {
  56.  
  57. int id;
  58. String name;
  59. int age;
  60. while (cursor.moveToNext()) {
  61. id = cursor.getInt(cursor.getColumnIndex(Person.KEY_ID));
  62. name = cursor.getString(cursor.getColumnIndex(Person.KEY_NAME));
  63. age = cursor.getInt(cursor.getColumnIndex(Person.KEY_AGE));
  64. Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
  65. }
  66. cursor.close();
  67. }
  68. }
  69.  
  70. public void testQuerySingleItem() {
  71.  
  72. // 在uri的末尾添加一个id
  73. Uri uri = ContentUris.withAppendedId(Person.CONTENT_URI_QUERY_ITEM, 1);
  74.  
  75. // 内容提供者访问对象
  76. ContentResolver resolver = getContext().getContentResolver();
  77.  
  78. Cursor cursor = resolver.query(uri, new String[] { Person.KEY_ID,
  79. Person.KEY_NAME, Person.KEY_AGE }, null, null, null);
  80.  
  81. if (cursor != null && cursor.moveToFirst()) {
  82. int id = cursor.getInt(0);
  83. String name = cursor.getString(1);
  84. int age = cursor.getInt(2);
  85. cursor.close();
  86. Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
  87. }
  88. }
  89. }

好了,本片只是个人记录一些经常忘记的知识点方便以后忘记了可以翻翻,没有特别仔细分析。

ContentResolver,ContentProvider,ContentObserver使用记录的更多相关文章

  1. Android ContentProvider、ContentResolver和ContentObserver的使用

    1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之中的一个,可见它在Android中的作用非 ...

  2. ContentProvider、ContentResolver、ContentObserver之间的关系

    ContentProvider.ContentResolver.ContentObserver之间的关系 ContentPRrovider: * 四大组件的内容提供者,主要用于对外提供数据 * 实现各 ...

  3. ContentProvider 、 ContentResolver 、 ContentObserver

    说说ContentProvider . ContentResolver . ContentObserver 之间的关系**a. ContentProvider 内容提供者,用于对外提供数据 b. Co ...

  4. AndroidContentProvider ContentResolver和ContentObserver的使用

    1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之一,可见它在Android中 的作用非同小 ...

  5. ContentProvider,ContentResolver和ContentObserver 使用

    1 ContentProvider内容提供者 四大组件之一,实现不同程序实现数据的共享.联系人应用就使用了ContentProvider,比如你在自己的应用可以读取和修改联系人的数据(获得相应权限). ...

  6. 内容观察者 ContentObserver 监听短信、通话记录数据库 挂断来电

    Activity public class MainActivity extends ListActivity {     private TextView tv_info;     private  ...

  7. Android应用开发学习笔记之ContentProvider

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz ContentProvider用于为其它应用程序提供共享数据,它为不同应用程序间共享数据提供了统一的操作接口. 一. ...

  8. android ContentProvider 笔记

    学习android的contentprovider.笔记记录于此. contentprovider作用是将数据共享给其他的应用. 参考链接 https://www.tutorialspoint.com ...

  9. ContentProvider要点复习

    ContentProvider要点复习 ContentProvider作为四大组件之一,发挥着举足轻重的作用.与之相关联的另外两个类分别是ContentResolver和ContentObserver ...

随机推荐

  1. Postgres中表和元组的组织方式

    PG version 9.5.3 PG中四种堆文件: 普通堆 临时堆 序列堆 TOAST表 PageHeaderData长度为24(截图为8.4版本,20字节)个字节包含的内容如下: 空闲空间的起始和 ...

  2. NGUI_Atlas

    二.NGUI的图集制作: 1.概述: 将导入的图片资源全部制成一张图集,可以节约资源,当制成图集后,就可以将导入的图片资源进行删除, 再后续的操作直接使用图集中的图片即可,NGUI自带的Atlas M ...

  3. POJ 2367 topological_sort

    Genealogical tree Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 2920 Accepted: 1962 Spe ...

  4. 第一次面试&amp;第一次霸面

    哈哈哈哈,第一次面试和第一次都献给了CVTE! CVTE的招聘流程有点特别:网測-- 一面--笔试--二面--offer 想起网測那天就心酸.那先在做第三部分的专业測试.计时器突然出错........ ...

  5. orale 查询每年、每月、每日统计量的sql语句

    每年 select to_char(createtime, 'YYYY') 年, count(*) from table  group by to_char(createtime, 'YYYY'); ...

  6. 几条jQuery代码片段助力Web开发效率提升

    平滑滚动至页面顶部 以下是jQuery最为常见的一种实现效果:点击一条链接以平滑滚动至页面顶部.虽然没什么新鲜感可言,但每位开发者几乎都用得上. $("a[href='#top']" ...

  7. 跨平台应用集成(在ASP.NET Core MVC 应用程序中集成 Microsoft Graph)

    作者:陈希章 发表于 2017年6月25日 谈一谈.NET 的跨平台 终于要写到这一篇了.跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标.我在前面的文章已经提到过了,Micro ...

  8. VirtualBox 安装 Ubuntu 14.04 无法调节分辨率问题

    基础环境 宿主系统:Windows 10 虚拟机系统:Ubuntu 14.04-32bit.Ubuntu 14.04-64bit VirtualBox:5.2.0 r118431 (Qt5.6.2) ...

  9. 获取AJAX加载的内容

    1.有些网页内容使用AJAX加载,AJAX一般返回的是JSON,直接对AJAX地址进行post或get,就返回JSON数据了. 2.用抓包工具分析https://movie.douban.com/j/ ...

  10. Intellijidea建javaWeb以及Servlet简单实现

    一.创建并设置javaweb工程1.创建javaweb工程File --> New --> Project... 点击Project后出现如下界面,选择Java Enterprise,选中 ...