Android 官网Train阅读记录——2

来源:互联网 发布:汇编语言编程事例讲解 编辑:程序博客网 时间:2024/05/21 06:33

LZ阅读的是中文翻译版本:http://hukai.me/android-training-course-in-chinese/basics/activity-lifecycle/recreating.html,试验机系统版本7.1。

1.5 使用Fragment建立动态的UI

1.5.1 创建Fragment

创建Fragment要继承自Fragment,一般,为了兼容低版本,要继承v4包中的Fragment。

有两种方式向Activity的视图中添加Fragment:

1、直接在Activity的布局xml文件中通过标签(fragment)的方式,通过android:name属性指明引用的是哪个Fragment,其值是要引用的Fragment的全类名。

2、在Activity的代码中通过FragmentManager添加。


1.5.2 建立灵活动态的UI

在小屏幕的设备上,如手机,通常需要替换Fragment来达到较好的user experience,而大屏幕,如平板,或者是横屏的设备上,则可以一次横放两个,甚至更能多的Fragment。

所以,如果一个App要适配不同尺寸的设备,就要通过 Android 官网Train阅读记录——1提到的各种适配方法来适配了。

如果一个新闻app想同时运行在手机和平板上,并保持很好的用户体验,达到在手机上单屏单窗,在平板上单屏双窗的效果,就需要为同一个Activity建立两个xml布局文件,例如:

其中在手机上的布局文件名为res/layout/news.xml,而在平板上的布局文件名为res/layout-large/news.xml。

在前一个布局文件中可以只有一个FrameLayout作为动态替换Fragment的容器,而在后面的文件中,可以固定的添加两个<Fragment>标签。

在Activity的代码中可以动态的通过查询FrameLayout容器是否存在,判断该设备是小屏幕还是大屏幕,这样在不同的布局中做不同的操作,就可以达到在手机上单屏单窗,在平板上单屏双窗的效果。


如果要让Fragment想Activity一样存入backstack,必须手动调用addToBackStack方法,此方法由FragmentTransaction所有,并且需要在FragmentTransaction调用commit之前调用才行。


1.5.3 Fragments之间以及Fragment和Activity之间的交互

Fragments之间不提倡直接进行交互,这样耦合性严重,可复用性差。所以,Fragments之间的交互通常通过Fragment与Activity之间的交互来完成。

Fragment和Activity之间的交互:

Activity可以通过Fragment的setArguments方法向Fragment传递消息。

Fragment可以通过自定义回调接口,让包含它的Activity实现,来完成向Activity传递消息。Fragment通过onAttach方法或者getActivity方法获取Activity的引用然后转换成相应的接口对象,这样在Fragment中调用接口的方法,通过向该方法传递参数,以达到向Activity传递消息的目的。


Fragment可以向ActionBar中添加Menu Item,实现方式为:复写onCreateOptionsMenu方法,并在onCreate方法中调用setHasOptionsMenu(true),来达到目的,然后通过复写onOptionsItemSelected方法监听按钮的点击事件。



1.6 数据保存

1.6.2 保存到文件

数据可以保存到internal storage或者external storage,保存到external storage需要WRITE_EXTERNAL_STORAGE权限。

保存到internal可以

File file = new File(context.getFilesDir(), filename);
或者

String filename = "myfile";String string = "Hello world!";FileOutputStream outputStream;try {  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);  outputStream.write(string.getBytes());  outputStream.close();} catch (Exception e) {  e.printStackTrace();}
保存到internal的文件在app卸载的时候,会被系统随之移除。


保存到external之前需要验证external storage是否可用

 /* Checks if external storage is available for read and write */public boolean isExternalStorageWritable() {    String state = Environment.getExternalStorageState();    if (Environment.MEDIA_MOUNTED.equals(state)) {        return true;    }    return false;}/* Checks if external storage is available to at least read */public boolean isExternalStorageReadable() {    String state = Environment.getExternalStorageState();    if (Environment.MEDIA_MOUNTED.equals(state) ||        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {        return true;    }    return false;}

保存到external分两种:

不想让文件随app卸载而被系统移除可以

File file = new File(Environment.getExternalStoragePublicDirectory(            Environment.DIRECTORY_PICTURES), albumName);

其中DIRECTORY_PICTURES表示该目录存放的是照片,其他还有音乐,铃声等等类型,具体可以自行查阅。

要让文件随app卸载而被系统移除可以

 File file = new File(context.getExternalFilesDir(            Environment.DIRECTORY_PICTURES), albumName);


若文件是保存在internal storage的,可以用下面的方式删除文件

myContext.deleteFile(fileName);


1.6.3 保存到数据库

通过实现BaseColumns接口,创建包含表名和列名的类,其他的关于表的创建、增删改查操作使用该类即可

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


1.7 与其他应用的交互

1.7.1 Intent的发送

拨号

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

Uri webpage = Uri.parse("http://www.android.com");Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
创建日历事件

Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());calendarIntent.putExtra(Events.TITLE, "Ninja class");calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");


验证是否有app接受Intent

PackageManager packageManager = getPackageManager();List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);boolean isIntentSafe = activities.size() > 0;
如果接受Intent的app不止一个,强制用户选择

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().getText(R.string.chooser_title);// Create and start the chooserIntent chooser = Intent.createChooser(intent, title);startActivity(chooser);

1.7.3 Intent过滤

如果要让其他app能够调用我们自己写的app,我们需要为被调用的Activity添加Intent-filter。若Activity中的intent-filter满足以下Intent对象的标注,系统就能够把特定的Intent发送给Activity:

Action:一个想要执行的动作的名称。通常是系统已经定义好的值,如ACTION_SEND或ACTION_VIEW。在Intent-filter中通过<action>标签指定它的值,值的类型必须为字符串。

Data:Intent附带数据的描述。在Intent-filter中通过<data>指定它的值,可以使用一个或者多个属性,我们可以只定义MIME type或者是只指定URI prefix,也可以只定义一个URI scheme,或者是它们综合使用。

Category:提供一个附加的方法来标识这个Activity能够handle的Intent。通常与用户的手势或者是启动位置有关。系统有支持几种不同的Category,但是大多数都很少用到。而且,所有的implicit intents都默认是CATEGORY_DEFAULT类型的。在Intent-filter中用<category>指定它的值。

例子

<activity android:name="ShareActivity">    <intent-filter>        <action android:name="android.intent.action.SEND"/>        <category android:name="android.intent.category.DEFAULT"/>        <data android:mimeType="text/plain"/>        <data android:mimeType="image/*"/>    </intent-filter></activity>
一个Intent-filter可以有多个action、category或者data。一个Activity可以有多个Intent-filter。

在Activity中Handle发送过来的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 ...    }}
返回Result

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


0 0