Getting Started

2017-03-16  本文已影响29人  nancymi

Building Your First App

Supporting Different Devices

Building a Dynamic UI with Fragments

为了适配不同的屏幕尺寸(大屏幕可以比小屏幕多显示几个 Fragment),这一节主要说明如何通过 Fragments 创造动态化的用户体验,使你的 App 在不同的屏幕尺寸上都可获得最优的用户体验,设备最低支持到 Android 1.6.

Creating a Fragment

可以认为Fragment是Activity组合的一部分,有自己独立的生命周期,自己的输入事件,当其所依附的Activity还在运行时,可以自由添加或删除Fragment。

在创建Fragment之前,需要让App使用 Support Library

Create a Fragment Class

  1. extend Fragment
  2. override key lifecycle methods

必须使用 onCreateView() 的callback来定义layout组件

Add a Fragment to an Activity using XML

FragmentActivity 是用来支持 API level 11 以下的版本,如果版本在 11 及以上,则可以使用普通的Activity.

可以通过在 xml 文件中指定Fragmentname属性,从而指定特定的Fragment class.

Building a Flexible UI

FragmentManager 类提供添加、移除、替换fragment的方法,给用户动态的适配体验。

Add a Fragment to an Activity at Runtime

使用FragmentManager 创建一个 FragmentTransaction

在Activity运行时添加Fragment有一点需要注意:Activity必须包含一个你可以插入 fragment 的 View.

Replace One Fragment with Another

使用 replace() 代替 add().

Best Practice:在进行Fragment替换时,最好允许用户返回或者取消操作:addToBackStack()(在 FragmentTransaction.commit() 之前,Fragment 将不会被销毁,只会被remove掉).

Communicating with Other Fragments

所有Fragment之间的信息交换都是通过与其相关的Activity来完成,任何Fragment不应该直接交流。

Define an Interface

在 Fragment 中定义一个 interface,在 Activity 中实现这个 interface。Fragment 将会在 onAttach() 中通过得到 Activity 对象捕获这个实现,从而通过 Activity 来进行信息交流。

Implement the Interface

Activity 需要实现 Fragment 中声明的 interface.

Deliver a Message to a Fragment

Activity 可以通过 findFragmentById() 获取到 Fragment 实例,直接调用 Fragment 的方法。

场景:AFragment 有一堆文章列表,点击某个文章,进入 BFragment,阅读这篇文章。

  1. AFragment: callback.click(title)
  2. Activity: click(title) {title -> content -> replaceToBFragment(content) -> BFragment.updateArticleView(content)}
  3. BFragment: updateArticleView(content)

Saving Data

在 Android 中,有三种数据存储方式:

Saving Key-Value Sets

一个 SharedPreferences 对象指向一个包含key-value的文件,提供简单的方法进行读写。

Get a Handle to a SharedPreferences

创建或获取一个 shared preference 文件:

Write to Shared Preferences

  1. 创建 SharedPreferences.Editor:调用 SharedPreferences 对象的 edit() 方法
  2. 写入键值对:putInt(), putString, .etc
  3. 保存更改:commit()

Read from Shared Preferences

getInt(), getString, .etc.

Saving Files

使用 File API 来操作 Android 中的文件系统。

一个File对象适合读写大数据文件,从头到尾没有中断的顺序读取。

Choose Internal or External Storage

所有的Android设备拥有两个文件存储域:“internal” 和 “external”:

App 会被默认载入到internal中,在代码中如何设置下载位置?

在AndroidManifest中:更改android:installLocation

Obtain Permissions for External Storage

在external写文件:需要权限 android.permission.WRITE_EXTERNAL_STORAGE

在external写文件(in future):需要权限:android.permission.READ_EXTERNAL_STORAGE

Save a File on Internal Storage

Save a File on External Storage

由于external文件有很多不在场的不确定因素,所以在访问文件前最好验证其可用性:
getExternalStorageState() 获取external storage状态:如果返回MEDIA_MOUNTED,则可用。

Query Free Space

Delete a File

Saving Data in SQL Databases

Define a Schema and Contract

在 Contract 类中通过实现BaseColumns内部类,可以获得内部key_ID

Create a Database Using a SQL Helper

SQLiteOpenHelper 用来提供仅在的需要时候可长时间运行的操作(添加/更新数据库),避免在项目运行时就实例化数据库操作类。

只可在非UI线程调用以上两种方法,例如AsyncTaskIntentService.

继承SQLiteOpenHelper,需要重写onCreate(), onUpgrade(), onOpen(), (可选)onDowngrade().

Put Information into a Database

Insert: Database -> ContentValues -> db.insert(table_name, action_if_content_values_empty, content_values)

Read Information from a Database

Read: Database -> db.query(table_name, columns_to_return, column_where, column_where_value, group, filter, sort_order)

Return: Cursor -> cursor starts at position -1.

Delete Information from Database

Delete: Database -> db.delete(table_name, selection, selection_args)

Update a Database

Update: combine insert() & delete() -> db.update(table_name, content_values, selection, selection_args)

Persisting Database Connection

一般在Activity被摧毁时关闭DBHelper -> dbHelper.close()

Interacting with Other Apps

Sending the User to Another App

在与其它的App进行交互时,只能使用implicit intent。

Build an Implicit Intent

定义Action去具体化启动事件。

Verify There is an App to Receive the Intent

如果intent声明的唤起事件并不存在,app将会crash。

Start an Activity with the Intent

startActivity(intent)

Show an App Chooser

如果有多个唤起事件存在,需要用户自行选择具体的唤起事件,调用createChooser()来调起具体的被选事件。

Intent intent = new Intent(Intent.ACTION_SEND);
String title = getResources().getString(R.string.choose_title);
Intent chooser = Intent.createChooser(intent, title);
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

Getting a Result from an Activity

使用startActivityForResult()来启动一个activity并接收返回数据。
使用onActivityResult()来处理返回的数据

Start the Activity

static final int PICK_CONTACT_REQUEST = 1;
private void pickContact() {
    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
    pickContactIntent.setType(Phone.CONTENT_TYPE);
    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}

Receive the Result

通过 resultCode 来判断返回类型:

Allowing Other Apps to Start Your Activity

通过定义支持ACTION_SEND的intent:<intent-filter>.

Add an Intent Filter

intent-filter中定义以下几种criteria:

必须定义 CATEGORY_DEFAULT 否则implicit intent 无法处理跳转事件.

Handle the Intent in Your Activity

调用getIntent().
在activity的生命周期的任何时间段都可以调用,但是最好在onCreate() / onStart()中处理。

Return a Result

Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"));
setResult(Activity.RESULT_OK, result);
finish();

Working with System Permissions

为了保证App的数据安全,Android 在每一个有权限控制的沙箱中运行App。

Declaring Permissions

Determine What Permissions Your App Needs

Android 5.1 以下,用户会在安装App的时候赋予权限,在Android 6.0 以上,用户会在App运行时动态赋予权限。

Add Permissions to the Manifest

manifest属性下,申请permission使用uses-permission标签。

Requesting Permissions at Run Time

系统权限分为两种:normal 和 dangerous:

在Android 6.0以上,由于权限是动态授予的,所以需要保证在某些权限不可用时,App依然可以正常运行。

Check for Permissions

ContextCompat.checkSelfPermissions()

int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);

Request Permissions

最佳实践:在用户已经关闭权限时,App运行到需要使用权限才能正常运行的功能时,可以为用户提供权限解释。

Explain why the app needs permissions

shouldShowRequestPermissionRationale(): 如果App曾经请求过permission,用户拒绝了请求,该方法会返回true;
如果App曾经请求过permission,用户拒绝了请求,且选择Don't ask again,该方法会返回false;
如果设备安全等级拒绝授予该permission请求,该方法会返回false.

Request the permissions you need

requestPermissions() 用来请求权限。

if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) {
        //Show an explanation    
    } else {
        ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS);
    }    
}

Handle the permissions request response

onRequestPermissionsResult() override 该方法用来查询permission是否成功申请。

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTE) {
                //Do what you want    
            } else {
                //Do what when permission was denied    
            }
            return;
        }    
    }    
}

Permissions Usage Notes

权限控制准则

使用 adb 工具管理权限:

上一篇 下一篇

猜你喜欢

热点阅读