Jesse's Blog

不管怎样,明天又是全新的一天。

0%

5 Content Provider(3)

在databasetest中新建一个文件DatabaseProvider.java/创建内容提供器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
public class DatabaseProvider extends ContentProvider {

public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR =2;
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.example.databasetest.provider";
public static UriMatcher uriMatcher;
private MyDatabaseHelper dbHelper;
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//注册需要的Uri
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);//参数3是要返回的匹配码
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}//静态代码块,对UriMatcher初始化
//将期望匹配的3种URI格式添加进去
//--常量 UriMatcher.NO_MATCH 表示不匹配任何路径的返回码
//--# 号为通配符
//--* 号为任意字符
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
//创建或升级数据库
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
return true;
}
// public DatabaseProvider() {
// }
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
//创建SQLiteDatabase实例
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch(uriMatcher.match(uri)){
case BOOK_DIR:
cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
//将URI的AUTHORITY之后的部分用 / 符号分割,放到一个列表中
//不包含元字符串最左端的部分
String bookId = uri.getPathSegments().get(1);
cursor = db.query("Book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("Category", projection, "id = ?", new String[]{ categoryId }, null, null, sortOrder);
break;
default:
break;
}
return cursor; //返回cursor
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri uriReturn = null;
switch(uriMatcher.match(uri)){
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);//返回新增的行号
uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+ "/category/"+newCategoryId);
break;
default:
break;
}
return uriReturn;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updatedRows =0;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
updatedRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("Book", values, "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("Category", values, "id = ?", new String[]{categoryId});
break;
default:
break;
}
return updatedRows;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deletedRows = 0;
switch(uriMatcher.match(uri)){
case BOOK_DIR:
deletedRows = db.delete("Book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("Book", "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
deletedRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("Category", "id = ?", new String[]{categoryId});
break;
default:
break;
}
return deletedRows;
}
@Override
public String getType(Uri uri) { //用于获取Uri对象MIME类型(字符串)
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
switch (uriMatcher.match(uri)){
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.example.databasetest.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item./vnd.example.databasetest.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.example.databasetest.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.example.databasetest.provider.category";
}
return null;
}
}
阅读全文 »

5 Content Provider(2)

ContentResolver的用法/访问其他应用程序的数据/跨程序共享数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class MainActivity extends AppCompatActivity {

ArrayAdapter<String> adapter;
List<String> contactsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView contactsView = (ListView)findViewById(R.id.contacts_list);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList); //设置适配器
contactsView.setAdapter(adapter);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
}else{
readContacts();
}
}
private void readContacts() {
Cursor cursor = null;
try {
// CONTENT_URI常量是ContactsContract.CommonDataKinds.Phone类做好的封装
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != null){
while(cursor.moveToNext()){
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(displayName+"\n"+number); // 将数据添加到ListView
}
adapter.notifyDataSetChanged(); //通知刷新一下ListView
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor != null){
cursor.close();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch(requestCode){
case 1:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
readContacts();
}else{
Toast.makeText(this, "权限获取失败", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}

5 Content Provider(1)

运行时权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void onClick(View v) {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {//判断是否已经授权
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CALL_PHONE}, 1);//获取权限
}else{
call();
}
}

private void call() {//拨打电话的逻辑操作
try{
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086")); //Uri.parse()解析成Uri对象
startActivity(intent);
} catch (SecurityException e) {
e.printStackTrace();
}
}
<!-- more -->

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){ //判断获取权限结果
case 1:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
call();
}else {
Toast.makeText(this, "获取权限失败", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}

4 Data(4)

LitePal 3.0

添加依赖、配置

  • build.gradle
    implementation 'org.litepal.android:java:3.0.0'

  • AndroidManifest.xml: application添加
    android:name="org.litepal.LitePalApplication"

  • 在app/src/main新建assets/litepal.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" encoding="utf-8" ?>
    <litepal>
    <!-- 数据库名称-->
    <dbname value="LitePalDemo"/>
    <!-- 版本-->
    <version value="1"/>
    <list>
    <!-- 注册NewsBean表-->
    <mapping class="com.example.litepaltest.NewsBean"/>
    </list>
    </litepal>
    阅读全文 »

4 Data(1)

文件存储读取

  • 存储

    • save()
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      private void save(String inputText) throws IOException {
      FileOutputStream out = null;
      BufferedWriter writer = null;
      try {
      out = openFileOutput("data", Context.MODE_PRIVATE);
      writer = new BufferedWriter(new OutputStreamWriter(out));
      writer.write(inputText);
      }catch (IOException e){
      e.printStackTrace();
      }finally{
      try {
      if(writer != null){
      writer.close();
      }
      }catch (IOException e){
      e.printStackTrace();
      }
      }
      }
    • 在onDestroy()中保存
      1
      2
      3
      4
      5
      6
      7
      8
      9
      protected void onDestroy() { //将输入内容存入文件中
      super.onDestroy();
      String inputText = edit.getText().toString();
      try {
      save(inputText);
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      阅读全文 »

4 Data(2)

SharedPreferences

  • 保存数据(onCreate方法内)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Button saveData = findViewById(R.id.save_data);
    saveData.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
    editor.putString("name", "Tom");
    editor.putInt("age", 28);
    editor.putBoolean("married", false);
    editor.apply();
    }
    });
  • 读取数据(onCreate方法内)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Button restoreData = findViewById(R.id.restore_data);
    restoreData.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
    String name = pref.getString("name", ""); // 通过键读取值
    int age = pref.getInt("age", 0);
    boolean married = pref.getBoolean("married", true);
    Log.i("---", "name "+name);
    Log.i("---", "age "+age);
    Log.i("---", "married "+married);
    }
    });

4 Data(3)

SQLite

创建SQLiteOpenHelper继承类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyDatabaseHelper extends SQLiteOpenHelper {

public static final String CREATE_BOOK = "create table Book ("
+"id integer primary key autoincrement, " //autoincrement: 自增长,自动生成id列的值
+"author text, "
+"price real, "
+"name text)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version){
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
Toast.makeText(mContext, "create succeeded", Toast.LENGTH_SHORT).show();
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}
}
阅读全文 »

3 Broadcast

动态注册监听网络变化

  • 例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class MainActivity extends AppCompatActivity {

    private IntentFilter intentFilter;
    private NetWorkChangeReceiver netWorkChangeReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // intentFilter
    intentFilter = new IntentFilter();
    intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
    netWorkChangeReceiver = new NetWorkChangeReceiver();
    // registerReceiver
    registerReceiver(netWorkChangeReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
    super.onDestroy();
    // unregisterReceiver
    unregisterReceiver(netWorkChangeReceiver);
    }
    // 广播接收器
    class NetWorkChangeReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
    // ConnectivityManager
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    if(networkInfo != null && networkInfo.isAvailable()){
    Toast.makeText(context, "Network Available", Toast.LENGTH_SHORT).show();
    }else{
    Toast.makeText(context, "Network unavailable", Toast.LENGTH_SHORT).show();
    }
    }
    }
    }
    阅读全文 »

2 Fragment

一种嵌入到活动中的UI片段

碎片

  • java: news, NewsContentFragment, NewsTitleFragment, activity_main,
    xml: news_content_flag, news_content, news_title_flag, news_item

    关联

    1
    2
    3
    left_fragment.xml-->LeftFragment.java
    right_fragment.xml-->RightFragment.java
    another_right_fragment.xml-->AnotherRightFragment.java
  • 以LeftFragment为例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Button" />
    </LinearLayout>
    <!-- more -->

    1
    2
    3
    4
    5
    6
    7
    8
    public class LeftFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.left_fragment, container, false);
    return view;
    }
    }
    LeftFragment继承自Fragment,将left_fragment加载进来。
  • activity_main.xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
    android:id="@+id/left_fragment"
    android:name="com.example.fragmenttest.LeftFragment"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="match_parent"
    />

    <fragment
    android:id="@+id/right_fragment"
    android:name="com.example.fragmenttest.RightFragment"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1" />
    </LinearLayout>
    android:name属性显式指明碎片名。

    动态添加碎片

    创建another_right_fragment和AnotherRightFragment,按下左侧按钮将右侧碎片替换成AnotherRightFragment。
  • replaceFragment()(在MainActivity中)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void replaceFragment(Fragment fragment){
    // 获取FragmentManager
    FragmentManager fragmentManager = getSupportFragmentManager();
    // 开启一个事务
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    // 向容器内添加或替换碎片
    transaction.replace(R.id.right_layout, fragment );
    // 按下Back键返回上个碎片
    transaction.addToBackStack(null);
    // 提交事务
    transaction.commit();
    }

    碎片与活动之间的通信

  • 活动中获取碎片
    1
    RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
  • 碎片中获取活动
    1
    MainActivity activity = (MainActivity) getActivity();

    动态加载布局技巧

  • 使用限定符
    layout-large/activity_main, layout/activity_main
  • 使用最小宽度限定符
    res/layout-sw600dp/activity_main

    碎片的生命周期

    碎片的回调方法

  • onAttach()
  • onCreateView() 为碎片
  • onActivityCreated()
  • onDestroyView()
  • onDetach()

1 RecyclerView

RecyclerView

  • 调用

    1
    2
    3
    4
    5
    RecyclerView recyclerView = findViewById(R.id.recycler_view);
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);
    FruitAdapter adapter = new FruitAdapter(fruitList);
    recyclerView.setAdapter(adapter);
    • 控件
      • id: recycler_view
        1
        2
        3
        4
        <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
      • id: fruit_name, fruit_image
        阅读全文 »