第一章 Getting Started

来源:互联网 发布:国信证券行情分析软件 编辑:程序博客网 时间:2024/04/29 06:18

android

一、概述

本节是 Google Developer 的第一章,它主要包括以下几部分的内容:

  1. Building Your First App
  2. Supporting Different Devices
  3. Building Dynamic UI with Fragment
  4. Saving Data
  5. Interacting with Other Apps
  6. Working with System Permissions

Building Your First App

讲述了如何创建一个应用程序

Supporting Different Devices

从三个方面(语言、屏幕尺寸、Android平台版本)讲述了如何做好应用程序的适配

Building Dynamic UI with Fragment

如何通过 Fragment 创建更灵活的 UI

Saving Data

从三个方面(共享参数、文件、数据库)讲述了如何存储应用程序的数据

Interacting with Other Apps

如何通过 Intent 完成应用程序之间的交互

Working with System Permissions

讲述了 Android 权限系统的使用方法

二、详述

1.Building Your First App

本节共包含四部分:

  1. Creating an Android Project
  2. Runing Your Application
  3. Building a Simple User Interface
  4. Starting Another Activity

主要讲解了如何在 Android Studio 中构建自己的应用程序,以及如何通过 Intent 启动其他应用程序组件,并完成数据的传递。

2.Supporting Different Devices

本节共包含三部分:

  1. Supporting Different Languages
  2. Supporting Different Screens
  3. Supporting Different Platform Versions

第一部分讲述了如何在 Android 工程的 res 文件夹下创建包含相同名字(string.xml)、不同内容的 value-* 文件夹:

MyProject/    res/       values/           strings.xml       values-es/           strings.xml       values-fr/           strings.xml

第二部分讲述了如何在 Android 工程的 res 文件夹下创建包含相同布局文件名称(main.xml)、不同内容的 layout-* 文件夹:

MyProject/    res/        layout/            main.xml        layout-large/            main.xml

同样的方法也适用于 drawable

MyProject/    res/        drawable-xhdpi/            awesomeimage.png        drawable-hdpi/            awesomeimage.png        drawable-mdpi/            awesomeimage.png        drawable-ldpi/            awesomeimage.png

第三部分讲述了如何在清单文件中设置当前应用支持的最小版本和如何在代码中手动检测当前应用程序的版本:

清单文件设置版本

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" />    ...</manifest>

手动检测当前应用程序版本

private void setUpActionBar() {    // Make sure we're running on Honeycomb or higher to use ActionBar APIs    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {        ActionBar actionBar = getActionBar();        actionBar.setDisplayHomeAsUpEnabled(true);    }}

3.Building Dynamic UI with Fragment

本节共包含三部分:

  1. Creating a Fragment
  2. Building a Flxible UI
  3. Communicating with Other Fragments

第一部分讲解了如果创建一个 Fragment、Fragment 的作用以及为大屏幕尺寸的设备创建布局的方法:

创建 Fragment

import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.ViewGroup;public class ArticleFragment extends Fragment {    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,        Bundle savedInstanceState) {        // Inflate the layout for this fragment        return inflater.inflate(R.layout.article_view, container, false);    }}

为大尺寸设备创建布局文件——res/layout-large/news_articles.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:layout_width="fill_parent"    android:layout_height="fill_parent">    <fragment android:name="com.example.android.fragments.HeadlinesFragment"              android:id="@+id/headlines_fragment"              android:layout_weight="1"              android:layout_width="0dp"              android:layout_height="match_parent" />    <fragment android:name="com.example.android.fragments.ArticleFragment"              android:id="@+id/article_fragment"              android:layout_weight="2"              android:layout_width="0dp"              android:layout_height="match_parent" /></LinearLayout>

第二部分讲解创建灵活布局的方法和步骤:

  1. 为小屏幕尺寸的设备创建布局文件——res/layout/news_articles.xml
  2. 将创建好的布局动态添加到 Activity 布局文件中
  3. 在 Activity 中替换 Fragment

小屏幕尺寸的设备创建布局文件——res/layout/news_articles.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/fragment_container"    android:layout_width="match_parent"    android:layout_height="match_parent" />

将创建好的布局动态添加到 Activity 布局文件中

import android.os.Bundle;import android.support.v4.app.FragmentActivity;public class MainActivity extends FragmentActivity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.news_articles);        // Check that the activity is using the layout version with        // the fragment_container FrameLayout        if (findViewById(R.id.fragment_container) != null) {            // However, if we're being restored from a previous state,            // then we don't need to do anything and should return or else            // we could end up with overlapping fragments.            if (savedInstanceState != null) {                return;            }            // Create a new Fragment to be placed in the activity layout            HeadlinesFragment firstFragment = new HeadlinesFragment();            // In case this activity was started with special instructions from an            // Intent, pass the Intent's extras to the fragment as arguments            firstFragment.setArguments(getIntent().getExtras());            // Add the fragment to the 'fragment_container' FrameLayout            getSupportFragmentManager().beginTransaction()                    .add(R.id.fragment_container, firstFragment).commit();        }    }}

在 Activity 中替换 Fragment

// Create fragment and give it an argument specifying the article it should showArticleFragment newFragment = new ArticleFragment();Bundle args = new Bundle();args.putInt(ArticleFragment.ARG_POSITION, position);newFragment.setArguments(args);FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();// Replace whatever is in the fragment_container view with this fragment,// and add the transaction to the back stack so the user can navigate backtransaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);// Commit the transactiontransaction.commit();

第三部分讲解了 Fragment 之间数据交互实现的方法——接口回调,主要步骤:

  1. 在标题 Fragment 中定义接口
  2. 宿主 Activity 实现上述接口
  3. 在宿主 Activity 中实现具体事务处理逻辑

在标题 Fragment 中定义接口

public class HeadlinesFragment extends ListFragment {    OnHeadlineSelectedListener mCallback;    // Container Activity must implement this interface    public interface OnHeadlineSelectedListener {        public void onArticleSelected(int position);    }    @Override    public void onAttach(Activity activity) {        super.onAttach(activity);        // This makes sure that the container activity has implemented        // the callback interface. If not, it throws an exception        try {            mCallback = (OnHeadlineSelectedListener) activity;        } catch (ClassCastException e) {            throw new ClassCastException(activity.toString()                    + " must implement OnHeadlineSelectedListener");        }    }    ...}

宿主 Activity 实现上述接口

public static class MainActivity extends Activity        implements HeadlinesFragment.OnHeadlineSelectedListener{    ...    public void onArticleSelected(int position) {        // The user selected the headline of an article from the HeadlinesFragment        // Do something here to display that article    }}

在宿主 Activity 中实现具体事务处理逻辑

public static class MainActivity extends Activity        implements HeadlinesFragment.OnHeadlineSelectedListener{    ...    public void onArticleSelected(int position) {        // The user selected the headline of an article from the HeadlinesFragment        // Do something here to display that article        ArticleFragment articleFrag = (ArticleFragment)                getSupportFragmentManager().findFragmentById(R.id.article_fragment);        if (articleFrag != null) {            // If article frag is available, we're in two-pane layout...            // Call a method in the ArticleFragment to update its content            articleFrag.updateArticleView(position);        } else {            // Otherwise, we're in the one-pane layout and must swap frags...            // Create fragment and give it an argument for the selected article            ArticleFragment newFragment = new ArticleFragment();            Bundle args = new Bundle();            args.putInt(ArticleFragment.ARG_POSITION, position);            newFragment.setArguments(args);            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();            // Replace whatever is in the fragment_container view with this fragment,            // and add the transaction to the back stack so the user can navigate back            transaction.replace(R.id.fragment_container, newFragment);            transaction.addToBackStack(null);            // Commit the transaction            transaction.commit();        }    }}

注意: FragmentManager 可以使用同一个,但是 FragmentTransaction 不可以,因为它的没完成一个动作( add 或者 replace )都需要 Commit 动作。如果不同的事务使用同一个只有最后一个起作用,而如果你使用已经提交的 FragmentTransaction 就会出现异常。

4.Saving Data

本节共包含三部分:

  1. Saving Key-Values Sets
  2. Saving Files
  3. Saving Data in SQL Databases

第一部分讲述了共享参数存储数据的方法:

  1. 获取共享参数对象
  2. 保存数据到共享参数
  3. 从共享参数读取保存的数据

获取共享参数对象

SharedPreferences sharedPref = context.getSharedPreferences(        getString(R.string.preference_file_key), Context.MODE_PRIVATE);

保存数据到共享参数

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);SharedPreferences.Editor editor = sharedPref.edit();editor.putInt(getString(R.string.saved_high_score), newHighScore);editor.commit();

从共享参数读取保存的数据

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);int defaultValue = getResources().getInteger(R.string.saved_high_score_default);long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);

第二部分讲述了文件存储数据的方法,其中包含内部存储和扩展存储。内部存储对应的就是 Android 系统分配给每个应用程序的存储空间,当应用程序卸载的时候,相应的数据也就被移除了;扩展存储一般指的是 SD 卡,在它上面存储的数据即使应用程序被卸载了,数据依然存在。

Internal storage:
- It’s always available.
- Files saved here are accessible by only your app.
- When the user uninstalls your app, the system removes all your app’s files from internal storage.

External storage:
- It’s not always available, because the user can mount the external storage as USB storage and in some cases remove it from the device.
- It’s world-readable, so files saved here may be read outside of your control.
- When the user uninstalls your app, the system removes your app’s files from here only if you save them in the directory from getExternalFilesDir().

具体的流程操作流程如下:

  1. 声明应用程序所需权限(External storage)
  2. 获取目标文件
  3. 存储数据到目标文件
  4. 从目标文件读取数据

第三部分讲述了数据库存储数据的方法,大致流程如下:

  1. 创建数据库帮助类——FeedReaderContract
  2. 利用 SQL Helper 创建数据库
  3. 数据的增、删、改、查

创建数据库帮助类

public final class FeedReaderContract {    // To prevent someone from accidentally instantiating the contract class,    // make the constructor private.    private FeedReaderContract() {}    /* Inner class that defines the table contents */    public static class FeedEntry implements BaseColumns {        public static final String TABLE_NAME = "entry";        public static final String COLUMN_NAME_TITLE = "title";        public static final String COLUMN_NAME_SUBTITLE = "subtitle";    }}

利用 SQL Helper 创建数据库

private static final String SQL_CREATE_ENTRIES =    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +    FeedEntry._ID + " INTEGER PRIMARY KEY," +    FeedEntry.COLUMN_NAME_TITLE + " TEXT," +    FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";private static final String SQL_DELETE_ENTRIES =    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;public class FeedReaderDbHelper extends SQLiteOpenHelper {    // If you change the database schema, you must increment the database version.    public static final int DATABASE_VERSION = 1;    public static final String DATABASE_NAME = "FeedReader.db";    public FeedReaderDbHelper(Context context) {        super(context, DATABASE_NAME, null, DATABASE_VERSION);    }    public void onCreate(SQLiteDatabase db) {        db.execSQL(SQL_CREATE_ENTRIES);    }    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        // This database is only a cache for online data, so its upgrade policy is        // to simply to discard the data and start over        db.execSQL(SQL_DELETE_ENTRIES);        onCreate(db);    }    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {        onUpgrade(db, oldVersion, newVersion);    }}

数据的增、删、改、查

------------增------------// Gets the data repository in write modeSQLiteDatabase db = mDbHelper.getWritableDatabase();// Create a new map of values, where column names are the keysContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_TITLE, title);values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);// Insert the new row, returning the primary key value of the new rowlong newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);------------查------------SQLiteDatabase db = mDbHelper.getReadableDatabase();// Define a projection that specifies which columns from the database// you will actually use after this query.String[] projection = {    FeedEntry._ID,    FeedEntry.COLUMN_NAME_TITLE,    FeedEntry.COLUMN_NAME_SUBTITLE    };// Filter results WHERE "title" = 'My Title'String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";String[] selectionArgs = { "My Title" };// How you want the results sorted in the resulting CursorString sortOrder =    FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";Cursor cursor = db.query(    FeedEntry.TABLE_NAME,                     // The table to query    projection,                               // The columns to return    selection,                                // The columns for the WHERE clause    selectionArgs,                            // The values for the WHERE clause    null,                                     // don't group the rows    null,                                     // don't filter by row groups    sortOrder                                 // The sort order    );List itemIds = new ArrayList<>();while(cursor.moveToNext()) {  long itemId = cursor.getLong(      cursor.getColumnIndexOrThrow(FeedEntry._ID));  itemIds.add(itemId);}cursor.close();------------改------------SQLiteDatabase db = mDbHelper.getReadableDatabase();// New value for one columnContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_TITLE, title);// Which row to update, based on the titleString selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";String[] selectionArgs = { "MyTitle" };int count = db.update(    FeedReaderDbHelper.FeedEntry.TABLE_NAME,    values,    selection,    selectionArgs);------------删------------// Define 'where' part of query.String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";// Specify arguments in placeholder order.String[] selectionArgs = { "MyTitle" };// Issue SQL statement.db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);

5.Interacting with Other Apps

本节共包含三部分:

  1. Sending the User to Another App
  2. Getting a Result From the Activity
  3. Allowing Other Apps to Start Your Activity

第一部分讲述了:

  1. 通过隐式意图启动其他 Activity
  2. 检测是否有应用程序可以响应当前意图
  3. 展示一个应用程序选择器

通过隐式意图启动其他 Activity

Uri number = Uri.parse("tel:5551234");Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

检测是否有应用程序可以响应当前意图

PackageManager packageManager = getPackageManager();List activities = packageManager.queryIntentActivities(intent,        PackageManager.MATCH_DEFAULT_ONLY);boolean isIntentSafe = activities.size() > 0;

展示一个应用程序选择器

Intent intent = new Intent(Intent.ACTION_SEND);...// Always use string resources for UI text.// This says something like "Share this photo with"String title = getResources().getString(R.string.chooser_title);// Create intent to show chooserIntent chooser = Intent.createChooser(intent, title);// Verify the intent will resolve to at least one activityif (intent.resolveActivity(getPackageManager()) != null) {    startActivity(chooser);}

第二部分讲述了启动目标 Activity 之后如何从目标 Activity 获取数据:

启动目标 Activity

static final int PICK_CONTACT_REQUEST = 1;  // The request code...private void pickContact() {    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));    pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);}

获取数据

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    // Check which request we're responding to    if (requestCode == PICK_CONTACT_REQUEST) {        // Make sure the request was successful        if (resultCode == RESULT_OK) {            // The user picked a contact.            // The Intent's data Uri identifies which contact was selected.            // Do something with the contact here (bigger example below)        }    }}

第三部分讲述了:

  1. 为当前 Activity 添加意图过滤器
  2. 在当前 Activity 处理接收到的 Intent
  3. 从当前 Activity 返回结果

为当前 Activity 添加意图过滤器

<activity android:name="ShareActivity">    <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->    <intent-filter>        <action android:name="android.intent.action.SENDTO"/>        <category android:name="android.intent.category.DEFAULT"/>        <data android:scheme="sms" />        <data android:scheme="smsto" />    </intent-filter>    <!-- filter for sending text or images; accepts SEND action and text or image data -->    <intent-filter>        <action android:name="android.intent.action.SEND"/>        <category android:name="android.intent.category.DEFAULT"/>        <data android:mimeType="image/*"/>        <data android:mimeType="text/plain"/>    </intent-filter></activity>

处理接收到的 Intent

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    // Get the intent that started this activity    Intent intent = getIntent();    Uri data = intent.getData();    // Figure out what to do based on the intent type    if (intent.getType().indexOf("image/") != -1) {        // Handle intents with image data ...    } else if (intent.getType().equals("text/plain")) {        // Handle intents with text ...    }}

从当前 Activity 返回结果

// Create intent to deliver some kind of result dataIntent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"));setResult(Activity.RESULT_OK, result);finish();

6.Working with System Permissions

本节共包含三部分:

  1. Declaring Permissions
  2. Requesting Permissions at Run Time
  3. Usage Notes for Runtime Permissions

第一部分如何向清单文件中添加应用程序所需权限:

声明权限

<manifest xmlns:android="http://schemas.android.com/apk/res/android"        package="com.example.snazzyapp">    <uses-permission android:name="android.permission.SEND_SMS"/>    <application ...>        ...    </application></manifest>

第二部分系统权限在 Android 6.0 之后的改变,以及如何检查权限、请求权限、处理请求权限返回的结果:

检查权限

// Assume thisActivity is the current activityint permissionCheck = ContextCompat.checkSelfPermission(thisActivity,        Manifest.permission.WRITE_CALENDAR);

请求权限

// Here, thisActivity is the current activityif (ContextCompat.checkSelfPermission(thisActivity,                Manifest.permission.READ_CONTACTS)        != PackageManager.PERMISSION_GRANTED) {    // Should we show an explanation?    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,            Manifest.permission.READ_CONTACTS)) {        // Show an explanation to the user *asynchronously* -- don't block        // this thread waiting for the user's response! After the user        // sees the explanation, try again to request the permission.    } else {        // No explanation needed, we can request the permission.        ActivityCompat.requestPermissions(thisActivity,                new String[]{Manifest.permission.READ_CONTACTS},                MY_PERMISSIONS_REQUEST_READ_CONTACTS);        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an        // app-defined int constant. The callback method gets the        // result of the request.    }}

处理请求权限返回的结果

@Overridepublic void onRequestPermissionsResult(int requestCode,        String permissions[], int[] grantResults) {    switch (requestCode) {        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {            // If request is cancelled, the result arrays are empty.            if (grantResults.length > 0                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                // permission was granted, yay! Do the                // contacts-related task you need to do.            } else {                // permission denied, boo! Disable the                // functionality that depends on this permission.            }            return;        }        // other 'case' lines to check for other        // permissions this app might request    }}

第三部分讲述了:

  1. 考虑使用 Intent 代替直接在应用程序中开发响应模块
  2. 只申请应用程序必须的权限
  3. 在合适的时间申请权限而不是一拥而上
  4. 向用户解释所需权限的原因

三、参考文档

Building Your First App
Supporting Different Devices
Building a Dynamic UI with Fragments
Saving Data
Interacting with Other Apps
Working with System Permissions

0 0