Android程式設計權威指南 - 第10章 使用fragment argument

weixin_34124651發表於2016-12-08

目標

本章的目標效果是,在列表中點選一項,彈出明細fragment介面。
此介面可以修改後,並儲存返回列表fragment介面。
類的呼叫關係如下:

1923143-1159c9b509b5afe1.png
類呼叫關係

啟動明細介面CrimeFragment

CrimeListFragment類中啟動呼叫

  • 啟動是在點選列表的某一項之後,所以在onListItemClick函式中啟動
  • 和Activity啟動另外一個Activity函式一樣,通過顯示的宣告一個Intent物件,物件指定了呼叫類和被呼叫類。需要說明的是,仍舊呼叫的是包含目標fragment的Activity,而不是直接呼叫fragment。
  • 仍舊通過intent的putExtra來傳遞資訊。由於明細是要顯示對應的物件內容的,所以我們傳遞了具體的Crime物件給被呼叫者。並且設定了其對應的key為CrimeFragment.EXTRA_CRIME_ID常量。
  • 通過startActivity()來啟動

具體程式碼如下

@Override
public void onListItemClick(ListView a, View v, int position, long id) {    
      Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);    
      Log.d(TAG, c.getTitle() + " was clicked.");    

      // Start CrimeActivity    
      Intent i = new Intent(getActivity(), CrimeActivity.class);    
      i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());    
      startActivity(i);
}

CrimeFragment類的設計

獲取Intent內容

和Activity一樣,通過activity的getSerializableExtra來獲得Intent傳遞的物件

UUID crimeId = (UUID)this.getIntent().getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);

獲取Intent並處理的設計

在哪獲取Intent傳遞的訊息資訊?
我們可以直接在CrimeFragment類中使用上面的程式碼獲取Intent的內容,並生成具體的Crime物件,然後重新整理明細介面。
但是這樣做的問題在於,犧牲了CrimeFragment的封裝性,使得其無法再被其他的activity進行呼叫。
所以,我們應該在Activity就處理掉Intent資訊,並將獲取的資訊作為引數傳遞給Fragment。傳遞此引數的物件就是Bundle。

用於傳遞引數的Bundle物件

  • 每一個Fragment都有一個對應的Bundle物件,我們可以將要使用的引數塞進Bundle物件中,需要的時候再取出來使用。
  • Bundle的引數是key-value形式儲存的。

最終的設計

  • CrimeFragment類中建立一個newInstance的靜態方法用於生成CrimeFragment例項。最重要的是,這個方法裡包含了Bundle物件的生成和引數的設定。
public static CrimeFragment newInstance(UUID crimeId) {    
      Bundle args = new Bundle();    
      args.putSerializable(EXTRA_CRIME_ID, crimeId);    
      
      CrimeFragment fragment = new CrimeFragment();    
      fragment.setArguments(args);    

      return fragment;
}
  • 這時,我們可以在包含fragment的CrimeActivity類中,獲取Intent中的引數。然後呼叫newInstance生成CrimeFragment例項,並且將Intent中的引數設定進Bundle中。
public class CrimeActivity extends SingleFragmentActivity {    
      @Override    
      protected Fragment createFragment() {        
            UUID crimeId = (UUID)this.getIntent().getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);        
            
            return CrimeFragment.newInstance(crimeId);    
      }
}
  • 我們再在CrimeFragment類的onCreate函式中,獲取Bundle中的引數,並生成重新整理介面需要的Crime物件。
@Override
public void onCreate(Bundle savedInstanceState) {    
      super.onCreate(savedInstanceState);    
      
      UUID crimeId = (UUID)getArguments().getSerializable(EXTRA_CRIME_ID);    
      mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
}
  • 在CrimeFragment類的onCreateView函式中設定要顯示的介面
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
      View v = inflater.inflate(R.layout.fragment_crime, parent, false);

      mTitleField = (EditText)v.findViewById(R.id.crime_title);
      mTitleField.setText(mCrime.getTitle());

      ..........................................................

      mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
      mSolvedCheckBox.setChecked(mCrime.isSolved());

      ...........................................................

      return v;
}

最終效果

選中列表的Cime 4點選,出現如下明細介面

1923143-0829c033c0c5df99.png
明細介面

列表介面顯示明細的修改

  • 我們在明細介面可以修改Title這樣的引數,點選返回按鈕,預設是不會顯示修改資訊的。如果需要顯示,則需要做相關的處理。
  • 我們只需要一行程式碼就可以實現這樣的功能了。
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();

這確實挺神奇的,畢竟在明細介面完全沒有配置相關程式碼,Adapter是需要好好研究的類。

  • 實現功能語句放在哪?
    當我們跳轉到CrimeFragment(CrimeActiviy)時,CrimeListActivity和CrimeListFragment是處於暫停的狀態。所以當返回時,系統會呼叫onResume來進行恢復。
    也就是說實現顯示變化的語句應該放在CrimeListFragment類的onResume函式中。
@Override
public void onResume() {    
      super.onResume();        
      ((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
}

通過fragment獲取返回結果

  • 在CrimeListFragment類中,啟動時用的是Fragment的startActivityForResult方法,而不是Activity的此方法;獲取返回值並處理時,覆寫的是Fragment的onActivityResult方法,而不是Activity的此方法。
public class CrimeListFragment extends ListFragment {
      private ArrayList<Crime> mCrimes;
      private static final String TAG = "CrimeListFragment";
      private static final int REQUEST_CRIME = 1;

      @Override
      public void onListItemClick(ListView a, View v, int position, long id) {    
            Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);    
            Log.d(TAG, c.getTitle() + " was clicked.");    

            // Start CrimeActivity    
            Intent i = new Intent(getActivity(), CrimeActivity.class);    
            i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());    
            startActivityForResult(i, REQUEST_CRIME);
      }

      @Override
      public void onActivityResult(int requestCode, int resultCode, Intent data) {    
            if (requestCode == REQUEST_CRIME) {       
                  // Handle result    
            }
      }
}
  • Fragment可以獲取返回結果,但是不能產生返回結果。只能通知託管其的Activity來設定返回結果。
public class CrimeFragment extends Fragment {

      public void returnResult() {    
            getActivity().setResult(Activity.RESULT_OK, null);
      }
}

具體的應用在20章才會詳細介紹*

更新後的程式結構圖

1923143-31795e3d0dc67a68.png
更新後的程式結構圖

相關文章