1.使用碎片为不同的屏幕设计布局,
main布局加了一个layout-sw600dp,里面定义了一个Fragment和一个FragmentLayout,分别对应左边的详细列表和右边的单个信息。
这样在Layout的布局中,定义一个Fragment,这个id必须要layout-sw600dp的一样,
在detail中定义的一个FragmentLayout这个id必须要layout-sw600dp中的FragmentLayout一样,这样无论是什么屏幕,怎么显示,
内容都是到同一个布局上了,
layout-sw600dp
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:baselineAligned="false" android:divider="?android:attr/dividerHorizontal" tools:context="com.loveqiqi.sy.mysunshine.MainActivity" > <fragment android:id="@+id/fragment_forecast" android:name="com.loveqiqi.sy.mysunshine.ForecastFragment" android:layout_width="0dp" android:layout_weight="2" android:layout_height="match_parent" tools:layout="@android:layout/list_content"/> <FrameLayout android:id="@+id/weather_detail_container" android:layout_width="0dp" android:layout_weight="4" android:layout_height="match_parent"/> </LinearLayout>
layout的
<?xml version="1.0" encoding="utf-8"?> <fragment xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/fragment_forecast" android:name="com.loveqiqi.sy.mysunshine.ForecastFragment" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:layout_marginRight="@dimen/activity_horizontal_margin" tools:context="com.loveqiqi.sy.mysunshine.ForecastFragment" tools:layout="@android:layout/list_content" > </fragment>
detail的
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/weather_detail_container" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.android.sunshine.app.DetailActivity" tools:ignore="MergeRootFrame" />
2.在DetailFragment中的判断
为什么要先判断intent的不是空的?如果是空的,就表示没有传入ContentUri,也就是没有点击哪一个,这样就返回null,导致Detail的view不会被更新,
显示的就是初始化的这些破玩意,如果不为空就是点击的,我们一开始进入main,肯定是空啊。如果是空还查询干嘛,直接返回null就ok。
这个intent是forecastFragment通过intent启动DetaiActivity,DetailActivity中包含这个DetailFragment,这样通过getActivity().getIntent()活动intent,是这个逻辑,
有点明白了
@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { Intent intent = getActivity().getIntent(); if (intent == null || intent.getData() == null) {//这里为什么要判断intent是否为null return null; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. return new CursorLoader( getActivity(), intent.getData(), DETAIL_COLUMNS, null, null, null ); }
3.两个屏幕的问题就是一开始进入就应该显示第一条的,但是现在右边显示的是硬编码的数据,肯定不对,这就需要碎片和活动通信了,
这是重点,要好好学习哈
下面通信遇到的问题:
为了把点击的那一项的Uri传过去,自己为DetailFragment定义了接收Uri的构造函数,这样就可以直接使用这个Uri了。
但是有一个问题,就是一旋转屏幕就死了,然后DetailFragment貌似必须要有一个无参构造函数,官方文档上写了:
Default constructor. Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle)
and later retrieved by the Fragment with getArguments()
.
Applications should generally not implement a constructor. The first place application code can run where the fragment is ready to be used is in onAttach(Activity)
, the point where the fragment is actually associated with its activity. Some applications may also want to implement onInflate(Activity, AttributeSet, Bundle)
to retrieve attributes from a layout resource, though should take care here because this happens for the fragment is attached to its activity.
@Override public void onItemSelected(Uri itemUir) { if(mTwoPane){ Bundle args = new Bundle(); args.putParcelable(DetailFragment.DETAIL_URI, itemUir); //替换DetaiFragment DetailFragment detailFragment = new DetailFragment(); detailFragment.setArguments(args); getSupportFragmentManager().beginTransaction().replace(R.id.weather_detail_container, detailFragment, DETAILFRAGMENT_TAG).commit(); }else{ Intent intent = new Intent(getApplicationContext(), DetailActivity.class) .setData(itemUir); startActivity(intent); } }
双屏的:
在DetailFragment中的OnCreateView接收,初始化他的mUri成员变量:
Bundle arguments = getArguments(); if(arguments != null){ mUri = arguments.getParcelable(DETAIL_URI); }
然后对于启动另一个活动DetailActivity的情况,直接在OnCreate方法中接收这个参数,继续写入,传递:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_detail); if (savedInstanceState == null) { Bundle arguments = new Bundle(); arguments.putParcelable(DetailFragment.DETAIL_URI, getIntent().getData()); DetailFragment fragment = new DetailFragment(); fragment.setArguments(arguments); getSupportFragmentManager().beginTransaction() .add(R.id.weather_detail_container, fragment) .commit(); } }
这样在Fragment中OnCreateView也可以获得这个Bundle。
总之就是在DetailFragment启动或者替换的地方,要在使用setArguments把需要的参数设置进去,然后在DetailFragment的getArguments中取出来
4.恢复旋转造成的问题
旋转之后,之前旋转的不知道哪去了,我们在ForecastFragment的onSaveInstancState保存位置,等到回来的时候,
在OnCreateView中判断应该是哪个位置,最后在onLoaderFininsh加载完数据,使用ListView的滚动到相应位置。
@Override public void onSaveInstanceState(Bundle outState) { // When tablets rotate, the currently selected list item needs to be saved. // When no item is selected, mPosition will be set to Listview.INVALID_POSITION, // so check for that before storing. if (mPosition != ListView.INVALID_POSITION) { outState.putInt(SELECTED_KEY, mPosition); } super.onSaveInstanceState(outState); }
@Override public void onLoadFinished(android.support.v4.content.Loader<Cursor> loader, Cursor data) { mForecastAdapter.swapCursor(data); if (mPosition != ListView.INVALID_POSITION) { // If we don't need to restart the loader, and there's a desired position to restore // to, do so now. mListView.smoothScrollToPosition(mPosition); } }
有一个问题就是,我竖屏点击一个item,然后看完详情,在返回,返回不到我刚才看的那一天,而是从头开始,这也是没有记住,需要
在哪里记住我这个选择?
一个方案,把记录位置的改成static,这样就会回滚,但是这样明显看到一个滚动,用户体验不好,应该还有其他方法
public static int mPosition = ListView.INVALID_POSITION;