20189230楊 2018-2019-2 《移動平臺開發實踐》第10周學習總結

學習《Java和Android開發學習指南(第二版)》第3九、40、4一、42章——

第39章偏好
Android帶有一個SharedPreferences接口,它能夠用來管理鍵/值對這樣的應用程序設置。SharedPreferences還負責向一個文件寫入數據。此外,Android還提供了Preference API,它帶有鏈接到默認的SharedPreferences實例的用戶接口類,以即可以很容易地建立一個UI來修改應用程序設置。
39.1SharedPreference
1.android.content.SharedPreferences接口提供了用於排序和讀取應用程序設置的方法。
2.getXXX方法返回了和指定的鍵相關聯的值(若是鍵值對存在的話)。
3.可使用contains方法檢查一個SharePreferences是否包含一個鍵值對。
4.可使用getAll方法將全部的鍵值對獲取爲一個Map。
5.一個SharedPreferences中存儲的值會自動持久化,而且將會在用戶會話中存在。
39.2Preference API
1.要在一個SharedPreferences中存儲一個鍵值對,一般使用Android Preference API來建立一個用戶界面,使得用戶可以編輯設置。
2.如今的Android版本中,一般使用PreferenceFragment來代替偏好xml文件。
39.3 使用Preference
1.代碼清單39.1 AndroidManifest.xml文件java

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.preferencedemo1" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.example.preferencedemo1.SettingsActivity"
            android:parentActivityName=".MainActivity"
            android:label="">
        </activity>
    </application>

</manifest>

2.代碼清單39.2 第一個活動的佈局文件(activity_main.xml)android

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">
    <TextView
        android:id="@+id/info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"/>
</RelativeLayout>

3.代碼清單39.3 MainActivity類git

package com.example.preferencedemo1;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    @Override
    public void onResume() {
        super.onResume();
        SharedPreferences sharedPref = PreferenceManager.
                getDefaultSharedPreferences(this);
        boolean allowMultipleUsers = sharedPref.getBoolean(
                SettingsActivity.ALLOW_MULTIPLE_USERS, false);
        String envId = sharedPref.getString(
                SettingsActivity.ENVIRONMENT_ID, "");
        String account = sharedPref.getString(
                SettingsActivity.ACCOUNT, "");
        TextView textView = (TextView) findViewById(R.id.info);
        textView.setText("Allow multiple users: " +
                allowMultipleUsers + "\nEnvironment Id: " + envId
                + "\nAccount: " + account);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:
                startActivity(new Intent(this,
                        SettingsActivity.class));
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

4.代碼清單39.4 SettingsActivity類sql

package com.example.preferencedemo1;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;

public class SettingsActivity extends Activity {

    public static final String ALLOW_MULTIPLE_USERS =
            "allowMultipleUsers";
    public static final String ENVIRONMENT_ID = "envId";
    public static final String ACCOUNT = "account";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getActionBar().setDisplayHomeAsUpEnabled(true);
        getFragmentManager()
                .beginTransaction()
                .replace(android.R.id.content,
                        new SettingsFragment()).commit();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_settings, menu);
        return true;
    }
}

5.代碼清單39.5 SettingsFragment類數據庫

package com.example.preferencedemo1;
import android.os.Bundle;
import android.preference.PreferenceFragment;

public class SettingsFragment extends PreferenceFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource 
        addPreferencesFromResource(R.xml.preferences);
    }
}

6.代碼清單39.6 res/xml/preferences.xml文件api

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory
        android:title="Category 1">
        <CheckBoxPreference
            android:key="allowMultipleUsers"
            android:title="Allow multiple users"
            android:summary="Allow multiple users" />

    </PreferenceCategory>

    <PreferenceCategory
        android:title="Category 2">
        <EditTextPreference
            android:key="envId"
            android:title="Environment Id"
            android:dialogTitle="Environment Id"/>
        <EditTextPreference
            android:key="account"
            android:title="Account"/>
    </PreferenceCategory>
</PreferenceScreen>

第40章操做空間
40.1 概覽
Android設備提供了兩種存儲區域,內部的和外部的。
內部存儲對於應用程序來講是私有的,用戶和其餘的應用程序不能訪問它。
外部存儲中所存儲的文件將會和其餘的應用程序分享,其餘用戶也可以訪問外部存儲。
40.1.1內部存儲
1.Context類提供了各類方法,android-studio

一般使用這些方法來訪問在內部存儲中存儲的文件,並且不該該將內部存儲的位置直接編寫到代碼中。

40.1.2 外部存儲
1.有兩種類型的文件可以寫入到外部存儲中,私有文件和公有文件。私有文件是應用程序所私有的,當應用程序卸載的時候,將會刪除這些文件。公有文件是要和其餘的應用程序共享的,用戶也能夠訪問它。
2.外部存儲能夠刪除。試圖讀取或向其寫入的時候,應該首先測試外部存儲是否可用。
3.寫入到外部存儲須要用戶的許可。
4.讀取外部存儲,應該在清單文件中聲明users-permission元素。
40.2 建立一個Notes應用程序
1.代碼清單40.1 AndroidManifest.xml文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.filedemo1"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="19" />

    <android:uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="18" />
    <android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <android:uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.filedemo1.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.example.filedemo1.AddNoteActivity"
            android:label="@string/title_activity_add_note" >
        </activity>
    </application>

</manifest>

2.MainActivity類服務器

package com.example.filedemo1;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
    private String selectedItem;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView listView = (ListView) findViewById(
                R.id.listView1);
        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        listView.setOnItemClickListener(
                new OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> adapterView,
                                            View view, int position, long id) {
                        readNote(position);
                    }
                });
    }

    @Override
    public void onResume() {
        super.onResume();
        refreshList();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle presses on the action bar items 
        switch (item.getItemId()) {
            case R.id.action_add:
                startActivity(new Intent(this,
                        AddNoteActivity.class));
                return true;
            case R.id.action_delete:
                deleteNote();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void refreshList() {
        ListView listView = (ListView) findViewById(
                R.id.listView1);
        String[] titles = fileList();
        ArrayAdapter<String> arrayAdapter =
                new ArrayAdapter<String>(
                        this,
                        android.R.layout.simple_list_item_activated_1,
                        titles);
        listView.setAdapter(arrayAdapter);
    }

    private void readNote(int position) {
        String[] titles = fileList();
        if (titles.length > position) {
            selectedItem = titles[position];
            File dir = getFilesDir();
            File file = new File(dir, selectedItem);
            FileReader fileReader = null;
            BufferedReader bufferedReader = null;
            try {
                fileReader = new FileReader(file);
                bufferedReader = new BufferedReader(fileReader);
                StringBuilder sb = new StringBuilder();
                String line = bufferedReader.readLine();
                while (line != null) {
                    sb.append(line);
                    line = bufferedReader.readLine();
                }
                ((TextView) findViewById(R.id.textView1)).
                        setText(sb.toString());
            } catch (IOException e) {

            } finally {
                if (bufferedReader != null) {
                    try {
                        bufferedReader.close();
                    } catch (IOException e) {
                    }
                }
                if (fileReader != null) {
                    try {
                        fileReader.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
    }

    private void deleteNote() {
        if (selectedItem != null) {
            deleteFile(selectedItem);
            selectedItem = null;
            ((TextView) findViewById(R.id.textView1)).setText("");
            refreshList();
        }
    }
}

3.代碼清單40.3 AddNoteActivity類app

package com.example.filedemo1;
import java.io.File;
import java.io.PrintWriter;
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class AddNoteActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_note);
    }

    public void cancel(View view) {
        finish();
    }

    public void addNote(View view) {
        String fileName = ((EditText)
                findViewById(R.id.noteTitle))
                .getText().toString();
        String body = ((EditText) findViewById(R.id.noteBody))
                .getText().toString();
        File parent = getFilesDir();
        File file = new File(parent, fileName);
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(file);
            writer.write(body);
            finish();
        } catch (Exception e) {
            showAlertDialog("Error adding note", e.getMessage());
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (Exception e) {

                }
            }
        }
    }

    private void showAlertDialog(String title, String message) {
        AlertDialog alertDialog = new
                AlertDialog.Builder(this).create();
        alertDialog.setTitle(title);
        alertDialog.setMessage(message);
        alertDialog.show();
    }
}

40.3訪問公共存儲
1.代碼清單40.4 FileDemo2的活動的佈局文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.filedemo2" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.filedemo2.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

2.代碼清單40.5 MainActivity類

package com.example.filedemo2;
import java.io.File;
import java.util.Arrays;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
    class KeyValue {
        public String key;
        public String value;
        public KeyValue(String key, String value) {
            this.key = key;
            this.value = value;
        }
        @Override
        public String toString() {
            return key;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final List<KeyValue> keyValues = Arrays.asList(
                new KeyValue("Alarms", Environment.DIRECTORY_ALARMS),
                new KeyValue("DCIM", Environment.DIRECTORY_DCIM),
                new KeyValue("Downloads",
                        Environment.DIRECTORY_DOWNLOADS),
                new KeyValue("Movies", Environment.DIRECTORY_MOVIES),
                new KeyValue("Music", Environment.DIRECTORY_MUSIC),
                new KeyValue("Notifications",
                        Environment.DIRECTORY_NOTIFICATIONS),
                new KeyValue("Pictures",
                        Environment.DIRECTORY_PICTURES),
                new KeyValue("Podcasts",
                        Environment.DIRECTORY_PODCASTS),
                new KeyValue("Ringtones",
                        Environment.DIRECTORY_RINGTONES)
        );
        ArrayAdapter<KeyValue> arrayAdapter = new
                ArrayAdapter<KeyValue>(this,
                android.R.layout.simple_list_item_activated_1,
                keyValues);
        ListView listView1 = (ListView)
                findViewById(R.id.listView1);
        listView1.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        listView1.setAdapter(arrayAdapter);
        listView1.setOnItemClickListener(new
             OnItemClickListener() {
                 @Override
                 public void onItemClick(AdapterView<?> adapterView,
                                         View view, int position, long id) {
                     KeyValue keyValue = keyValues.get(position);
                     listDir(keyValue.value);
                 }
             });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    private void listDir(String dir) {
        File parent = Environment
                .getExternalStoragePublicDirectory(dir);
        String[] files = null;
        if (parent == null || parent.list() == null) {
            files = new String[0];
        } else {
            files = parent.list();
        }
        ArrayAdapter<String> arrayAdapter = new
                ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_activated_1,
                files);
        ListView listView2 = (ListView)
                findViewById(R.id.listView2);
        listView2.setAdapter(arrayAdapter);
    }
}

第41章操做數據庫
Android擁有本身的技術來操做數據庫,它和Java數據庫鏈接(Java Database Connectivity,JDBC)無關,JDBC是Java開發者用來訪問關係數據庫中的數據的一種技術。Android帶有SQLite,這是一個開源的數據庫。
41.1 概覽
1.Android帶有本身的Database API。
2.SQLite中的一項功能是,當插入一行的時候,有一個整數的主鍵自動增長,不須要爲該字段傳入一個值。
41.2 Database API
41.2.1 SQLiteOpenHelper類
1.提供一個構造方法,它調用本身的超類,傳入Context和數據庫名稱以及其餘內容。
2.覆蓋onCreate方法和onUpgrade方法。
3.SQLiteOpenHelper自動管理到底層數據庫的鏈接。
41.2.2 SQLiteDatabase類
1.調用insert或execSQL方法來操做數據庫中的數據。
2.要獲取記錄,使用query方法之一。
41.2.3 Cursor接口
1.在SQLiteDatabase上調用query方法將返回一個Cursor。
41.3 示例
1.代碼清單41.1 AndroidManifest.xml文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.databasedemo1"
    android:versionCode="1"
    android:versionName="1.0" >
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category
                    android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".AddContactActivity"
            android:parentActivityName=".MainActivity"
            android:label="@string/title_activity_add_contact">
        </activity>
        <activity
            android:name=".ShowContactActivity"
            android:parentActivityName=".MainActivity"
            android:label="@string/title_activity_show_contact" >
        </activity>
    </application>
</manifest>

2.代碼清單41.2 Contact類

package com.example.databasedemo1;
public class Contact {
    private long id;
    private String firstName;
    private String lastName;
    private String phone;
    private String email;

    public Contact() {
    }

    public Contact(String firstName, String lastName,
                   String phone, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.phone = phone;
        this.email = email;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

}

3.代碼清單41.3 DatabaseManager類

package com.example.databasedemo1;
import java.util.ArrayList;
import java.util.List;
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 DatabaseManager extends SQLiteOpenHelper {
    public static final String TABLE_NAME = "contacts";
    public static final String ID_FIELD = "_id";
    public static final String FIRST_NAME_FIELD = "first_name";
    public static final String LAST_NAME_FIELD = "last_name";
    public static final String PHONE_FIELD = "phone";
    public static final String EMAIL_FIELD = "email";
    public DatabaseManager(Context context) {
        super(context, 
                /*db name=*/ "contacts_db2", 
                /*cursorFactory=*/ null, 
                /*db version=*/1);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.d("db", "onCreate");
        String sql = "CREATE TABLE " + TABLE_NAME
                + " (" + ID_FIELD + " INTEGER, "
                + FIRST_NAME_FIELD + " TEXT,"
                + LAST_NAME_FIELD + " TEXT,"
                + PHONE_FIELD + " TEXT,"
                + EMAIL_FIELD + " TEXT,"
                + " PRIMARY KEY (" + ID_FIELD + "));";
        db.execSQL(sql);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
        Log.d("db", "onUpdate");
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        // re-create the table 
        onCreate(db);
    }

    public Contact addContact(Contact contact) {
        Log.d("db", "addContact");
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(FIRST_NAME_FIELD, contact.getFirstName());
        values.put(LAST_NAME_FIELD, contact.getLastName());
        values.put(PHONE_FIELD, contact.getPhone());
        values.put(EMAIL_FIELD, contact.getEmail());
        long id = db.insert(TABLE_NAME, null, values);
        contact.setId(id);
        db.close();
        return contact;
    }

    // Getting single contact 
    Contact getContact(long id) {
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.query(TABLE_NAME, new String[] {
                        ID_FIELD, FIRST_NAME_FIELD, LAST_NAME_FIELD,
                        PHONE_FIELD, EMAIL_FIELD }, ID_FIELD + "=?",
                new String[] { String.valueOf(id) }, null,
                null, null, null);
        if (cursor != null) {
            cursor.moveToFirst();
            Contact contact = new Contact(
                    cursor.getString(1),
                    cursor.getString(2),
                    cursor.getString(3),
                    cursor.getString(4));
            contact.setId(cursor.getLong(0));
            return contact;
        }
        return null;
    }

    // Getting All Contacts 
    public List<Contact> getAllContacts() {
        List<Contact> contacts = new ArrayList<Contact>();
        String selectQuery = "SELECT  * FROM " + TABLE_NAME;

        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cursor = db.rawQuery(selectQuery, null);

        while (cursor.moveToNext()) {
            Contact contact = new Contact();
            contact.setId(Integer.parseInt(cursor.getString(0)));
            contact.setFirstName(cursor.getString(1));
            contact.setLastName(cursor.getString(2));
            contact.setPhone(cursor.getString(3));
            contact.setEmail(cursor.getString(4));
            contacts.add(contact);
        }
        return contacts;
    }

    public Cursor getContactsCursor() {
        String selectQuery = "SELECT  * FROM " + TABLE_NAME;
        SQLiteDatabase db = this.getWritableDatabase();
        return db.rawQuery(selectQuery, null);
    }

    public int updateContact(Contact contact) {
        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(FIRST_NAME_FIELD, contact.getFirstName());
        values.put(LAST_NAME_FIELD, contact.getLastName());
        values.put(PHONE_FIELD, contact.getPhone());
        values.put(EMAIL_FIELD, contact.getEmail());

        return db.update(TABLE_NAME, values, ID_FIELD + " = ?",
                new String[] { String.valueOf(contact.getId()) });
    }

    public void deleteContact(long id) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.delete(TABLE_NAME, ID_FIELD + " = ?",
                new String[] { String.valueOf(id) });
        db.close();
    }
}

4.代碼清單41.4 MainActivity類

package com.example.databasedemo1;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.CursorAdapter;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

public class MainActivity extends Activity {
    DatabaseManager dbMgr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView listView = (ListView) findViewById(
                R.id.listView);
        dbMgr = new DatabaseManager(this);

        Cursor cursor = dbMgr.getContactsCursor();
        startManagingCursor(cursor);

        ListAdapter adapter = new SimpleCursorAdapter(
                this,
                android.R.layout.two_line_list_item,
                cursor,
                new String[] {DatabaseManager.FIRST_NAME_FIELD,
                        DatabaseManager.LAST_NAME_FIELD},
                new int[] {android.R.id.text1, android.R.id.text2},
                CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

        listView.setAdapter(adapter);
        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        listView.setOnItemClickListener(
                new OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> adapterView,
                                            View view, int position, long id) {
                        Intent intent = new Intent(
                                getApplicationContext(),
                                ShowContactActivity.class);
                        intent.putExtra("id", id);
                        startActivity(intent);
                    }
                });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_add:
                startActivity(new Intent(this,
                        AddContactActivity.class));
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

5.代碼清單41.5 AddContactActivity類

package com.example.databasedemo1;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;

public class AddContactActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_contact);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.add_contact, menu);
        return true;
    }

    public void cancel(View view) {
        finish();
    }
    public void addContact(View view) {
        DatabaseManager dbMgr = new DatabaseManager(this);
        String firstName = ((TextView) findViewById(
                R.id.firstName)).getText().toString();
        String lastName = ((TextView) findViewById(
                R.id.lastName)).getText().toString();
        String phone = ((TextView) findViewById(
                R.id.phone)).getText().toString();
        String email = ((TextView) findViewById(
                R.id.email)).getText().toString();
        Contact contact = new Contact(firstName, lastName,
                phone, email);
        dbMgr.addContact(contact);
        finish();
    }
}

6.代碼清單41.6 ShowContactActivity類

package com.example.databasedemo1;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class ShowContactActivity extends Activity {
    long contactId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_contact);
        getActionBar().setDisplayHomeAsUpEnabled(true);
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            contactId = extras.getLong("id");
            DatabaseManager dbMgr = new DatabaseManager(this);
            Contact contact = dbMgr.getContact(contactId);
            if (contact != null) {
                ((TextView) findViewById(R.id.firstName))
                        .setText(contact.getFirstName());
                ((TextView) findViewById(R.id.lastName))
                        .setText(contact.getLastName());
                ((TextView) findViewById(R.id.phone))
                        .setText(contact.getPhone());
                ((TextView) findViewById(R.id.email))
                        .setText(contact.getEmail());
            } else {
                Log.d("db", "contact null");
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.show_contact, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_delete:
                deleteContact();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void deleteContact() {
        new AlertDialog.Builder(this)
                .setTitle("Please confirm")
                .setMessage(
                        "Are you sure you want to delete " +
                                "this contact?")
                .setPositiveButton("Yes",
                        new DialogInterface.OnClickListener() {
                            public void onClick(
                                    DialogInterface dialog,
                                    int whichButton) {
                                DatabaseManager dbMgr =
                                        new DatabaseManager(
                                                getApplicationContext());
                                dbMgr.deleteContact(contactId);
                                dialog.dismiss();
                                finish();
                            }
                        })
                .setNegativeButton("No",
                        new DialogInterface.OnClickListener() {
                            public void onClick(
                                    DialogInterface dialog,
                                    int which) {
                                dialog.dismiss();
                            }
                        })
                .create()
                .show();
    }
}

第42章獲取圖片
Android爲獲取靜態圖像的應用程序提供了兩個選項,使用一個內建的意圖來啓動Camera或者使用Camera API。第一個選項很容易使用,可是缺少Camera API所提供的功能。
42.1 概覽
1.要告訴Camera把獲取的圖片存儲在哪裏,能夠給Intent傳入一個Uri實例。
2.系統經過傳入3個參數來調用onActivityResult方法。第一個參數是requestCode,是調用startActivityForResult方法的時候傳入的請求代碼。第二個參數是結果代碼。第三個參數包含了來自被調用的活動的數據。
42.2 使用相機
CameraDemo應用程序展現瞭如何使用內建的意圖來激活Camera應用程序來使用它來拍照。
1.代碼清單42.1 清單

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.camerademo" >
    <uses-feature android:name="android.hardware.camera"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.camerademo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

2.代碼清單42.2 菜單文件(menu_main.xml)

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/action_camera"
        android:orderInCategory="100"
        android:showAsAction="ifRoom"
        android:title="@string/action_show_camera"/>
    <item
        android:id="@+id/action_email"
        android:orderInCategory="200"
        android:showAsAction="ifRoom"
        android:title="@string/action_email"/>
</menu>

3.代碼清單42.3 activity_main.xml文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</RelativeLayout>

4.代碼清單42.4 MainActivity類

package com.example.camerademo;
import java.io.File;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
    File pictureDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "CameraDemo");
    private static final String FILE_NAME = "image01.jpg";

    private Uri fileUri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (!pictureDir.exists()) {
            pictureDir.mkdirs();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_camera:
                showCamera();
                return true;
            case R.id.action_email:
                emailPicture();
                return true;
            default:
                return super.onContextItemSelected(item);
        }
    }

    private void showCamera() {
        Intent intent = new Intent(
                MediaStore.ACTION_IMAGE_CAPTURE);
        File image = new File(pictureDir, FILE_NAME);
        fileUri = Uri.fromFile(image);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
        // check if the device has a camera: 
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent,
                    CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
        }
    }

    @Override
    protected void onActivityResult(int requestCode,
                                    int resultCode, Intent data) {
        if (requestCode ==
                CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                ImageView imageView = (ImageView)
                        findViewById(R.id.imageView);
                File image = new File(pictureDir, FILE_NAME);
                fileUri = Uri.fromFile(image);
                imageView.setImageURI(fileUri);
            } else if (resultCode == RESULT_CANCELED) {
                Toast.makeText(this,  "Action cancelled",
                        Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this,  "Error",
                        Toast.LENGTH_LONG).show();
            }
        }
    }

    private void emailPicture() {
        Intent emailIntent = new Intent(
                android.content.Intent.ACTION_SEND);
        emailIntent.setType("application/image");
        emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
                new String[]{"me@example.com"});
        emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                "New photo");
        emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
                "From My App");
        emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
        startActivity(Intent.createChooser(emailIntent,
                "Send mail..."));
    }
}

42.3 Camera API
42.3.1 管理相機
使用takePicture方法能夠肯定從相機獲得的最終的原始圖像和JPEG圖像作些什麼。takePicture方法的四個參數介紹以下:
1.shutter。圖像捕獲瞬間的回調。
2.raw。解壓縮圖像數據的回調。
3.postview。預覽圖像數據的回調。
4.jpeg。JPEG圖像數據的回調。
42.4 使用Camera API
1.代碼清單42.5 佈局文件(activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.cameraapidemo" >

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.cameraapidemo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

2.代碼清單42.6 MainActivity類

package com.example.cameraapidemo;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.media.AudioManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity
        implements SurfaceHolder.Callback {

    private Camera camera;
    SoundPool soundPool;
    int beepId;
    File pictureDir = new File(Environment
            .getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_PICTURES),
            "CameraAPIDemo");
    private static final String TAG = "camera";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pictureDir.mkdirs();

        soundPool = new SoundPool(1,
                AudioManager.STREAM_NOTIFICATION, 0);
        Uri uri = Settings.System.DEFAULT_RINGTONE_URI;
        beepId = soundPool.load(uri.getPath(), 1);
        SurfaceView surfaceView = (SurfaceView)
                findViewById(R.id.surfaceview);
        surfaceView.getHolder().addCallback(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public void onResume() {
        super.onResume();
        try {
            if (Build.VERSION.SDK_INT >=
                    Build.VERSION_CODES.GINGERBREAD) {
                camera = Camera.open(0);
            } else {
                camera = Camera.open();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (camera != null) {
            try {
                camera.release();
                camera = null;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void enableButton(boolean enabled) {
        Button button = (Button) findViewById(R.id.button1);
        button.setEnabled(enabled);
    }

    public void takePicture(View view) {
        enableButton(false);
        camera.takePicture(shutterCallback, null,
                pictureCallback);
    }

    private ShutterCallback shutterCallback =
            new ShutterCallback() {
                @Override
                public void onShutter() {
                    // play sound
                    soundPool.play(beepId, 1.0f, 1.0f, 0, 0, 1.0f);
                }
            };

    private PictureCallback pictureCallback =
            new PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data,
                                           final Camera camera) {
                    Toast.makeText(MainActivity.this, "Saving image",
                            Toast.LENGTH_LONG)
                            .show();
                    File pictureFile = new File(pictureDir,
                            System.currentTimeMillis() + ".jpg");

                    try {
                        FileOutputStream fos = new FileOutputStream(
                                pictureFile);
                        fos.write(data);
                        fos.close();
                    } catch (FileNotFoundException e) {
                        Log.d(TAG, e.getMessage());
                    } catch (IOException e) {
                        Log.d(TAG, e.getMessage());
                    }

                    Handler handler = new Handler();
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                enableButton(true);
                                camera.startPreview();
                            } catch (Exception e) {
                                Log.d("camera",
                                        "Error starting camera preview: "
                                                + e.getMessage());
                            }
                        }
                    }, 2000);
                }
            };

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (Exception e){
            Log.d("camera", e.getMessage());
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder,
                               int format, int w, int h3) {
        if (holder.getSurface() == null){
            Log.d(TAG, "surface does not exist, return");
            return;
        }

        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (Exception e){
            Log.d("camera", e.getMessage());
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(TAG, "surfaceDestroyed");
    }
}

教材學習中的問題和解決過程

  • 問題1:Cursor如何實現ListView的自動刷新?
  • 問題解決方案1:將Cursor做爲一個ListAdapter的數據源,而ListAdapter反過來能夠用於填充一個ListView。針對ListView使用一個Cursor的優勢在於,Cursor能夠管理你的數據。換句話說,若是該數據更新了,Cursor能夠自行刷新ListView。
  • 問題2:uri和url的區別
  • 問題解決方案2:
    URL:(Uniform/Universal Resource Locator 的縮寫,統一資源定位符)。
    URI:(Uniform Resource Identifier 的縮寫,統一資源標識符)(表明一種標準)。
    關係:
    URI 屬於 URL 更高層次的抽象,一種字符串文本標準。
    就是說,URI 屬於父類,而 URL 屬於 URI 的子類。URL 是 URI 的一個子集。
    兩者的區別在於,URI 表示請求服務器的路徑,定義這麼一個資源。而 URL 同時說明要如何訪問這個資源(http://)。

代碼調試中的問題和解決過程

  • 問題1:在調試第42章的CameraDemo和CameraAPIDemo時,遇到Gradle裏No cached version available for offline mode的問題。
  • 問題解決方案2:在setting中gradle選項裏把offline work前面的勾給去掉就行了。
  • 問題2:Android遊戲項目的設計運行無錯後,在模擬器上運行老是閃退。
  • 問題解決方案2:添加本身的Android設備繼續調試。

上週錯題總結

1.字符串「dxxxdxxd」,表達式 (d)(\w+)(d)對應的應該是dxxxdxxd,而不是xxxdxx。
錯誤緣由:\w+將匹配第一個d和最後一個d之間的全部字符,(d)是兩端的d,不能缺。

[代碼託管]

https://gitee.com/EvelynYang/tenth_weeks

statistics.sh腳本運行結果的截圖

在新建的AndroidProjects文件夾中運行腳本,第六週及以前都是在IdeaProjects文件夾裏運行。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 200/200 2/2 20/20
第二週 300/500 1/3 18/38
第三週 500/1000 1/4 38/76
第四周 1000/2000 1/5 20/96
第五週 1000/3000 1/6 25/121
第六週 1000/4000 1/7 25/146
第七週 1000/5000 1/8 25/171
第八週 1000/6000 1/9 15/186
第九周 1000/7000 1/10 20/206
第十週 1000/8000 1/11 20/226

參考資料

相關文章
相關標籤/搜索