20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结

目录

  • 学习《Java和Android开发学习指南(第二版)》第39、40、41、42章——
  • 教材学习中的问题和解决过程
  • 代码调试中的问题和解决过程
  • 上周错题总结
  • [代码托管]
  • statistics.sh脚本运行结果的截图
  • 学习进度条
  • 参考资料

学习《Java和Android开发学习指南(第二版)》第39、40、41、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文件




    
        
            
                

                
            
        
        
        
    

2.代码清单39.2 第一个活动的布局文件(activity_main.xml)


    
 

3.代码清单39.3 MainActivity类

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类

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文件



    
        

    

    
        
        
    

20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第1张图片

第40章操作空间
40.1 概览
Android设备提供了两种存储区域,内部的和外部的。
内部存储对于应用程序来说是私有的,用户和其他的应用程序不能访问它。
外部存储中所存储的文件将会和其他的应用程序分享,其他用户也能够访问外部存储。
40.1.1内部存储
1.Context类提供了各种方法,

通常使用这些方法来访问在内部存储中存储的文件,而且不应该将内部存储的位置直接编写到代码中。

40.1.2 外部存储
1.有两种类型的文件能够写入到外部存储中,私有文件和公有文件。私有文件是应用程序所私有的,当应用程序卸载的时候,将会删除这些文件。公有文件是要和其他的应用程序共享的,用户也可以访问它。
2.外部存储可以删除。试图读取或向其写入的时候,应该首先测试外部存储是否可用。
3.写入到外部存储需要用户的许可。
4.读取外部存储,应该在清单文件中声明users-permission元素。
40.2 创建一个Notes应用程序
1.代码清单40.1 AndroidManifest.xml文件




    

    
    
    

    
        
            
                

                
            
        
        
        
    

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 arrayAdapter =
                new ArrayAdapter(
                        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类

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();
    }
}

20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第2张图片

40.3访问公共存储
1.代码清单40.4 FileDemo2的活动的布局文件




    
        
            
                

                
            
        
    

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 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 arrayAdapter = new
                ArrayAdapter(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 arrayAdapter = new
                ArrayAdapter(this,
                android.R.layout.simple_list_item_activated_1,
                files);
        ListView listView2 = (ListView)
                findViewById(R.id.listView2);
        listView2.setAdapter(arrayAdapter);
    }
}

20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第3张图片

第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文件



    
    
        
            
                
                
            
        
        
        
        
        
    

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 getAllContacts() {
        List contacts = new ArrayList();
        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();
    }
}

20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第4张图片

第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 清单



    
    
    

    
        
            
                

                
            
        
    

2.代码清单42.2 菜单文件(menu_main.xml)



    
    

3.代码清单42.3 activity_main.xml文件



    

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[]{"[email protected]"});
        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..."));
    }
}

20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第5张图片

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)




    
    
    
    

    
        
            
                

                
            
        
    

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");
    }
}

20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第6张图片

教材学习中的问题和解决过程

  • 问题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://)。
    20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第7张图片

代码调试中的问题和解决过程

  • 问题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文件夹里运行。
20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结_第8张图片

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 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

参考资料

  • 《Java和Android开发学习指南(第二版)(Java for Android.2nd)》
  • Android Studio中文社区

你可能感兴趣的:(20189230杨 2018-2019-2 《移动平台开发实践》第10周学习总结)