第十章 使用fragment argument
有关fragment和activity中相似方法的比较
新建Activity
Activity.startActivity()
Fragment.startActivity()
用法没什么不同。都是通过利用有关Intent来创建新Activity。在构造intent时,都是创建了一个新的方法,将构造intent和附加Extra信息绑定到一个方法里。
获取Extra信息
fragment是直接使用activity的getIntent()来获取intent的,需要用getActivity来获取其所在的activity。然后对intent拆包的方法也相同。
getActivity().getIntent()
fragmen argument与bundle
每一个fragment都和activity一样可以附带一个bundle对象以保存一些数据,fragment.setArguments()本身传入的就是一个bundle类型的文件,这与activity的onSaveInstanceState()方法作用相似。bundle绑定键值对的方法也都一样。
//Activity
protected void onSaveInstanceState(Bundle IS){
...
IS.putXXX();
...
}
//Fragment
...
Bundle args=new Bundle();
args.putXXX();
fragment.setArguments(args);
...
在获取bundle时也略有不同。Activity是直接对其bundles进行读取,二Fragment是要先获取argument,通过argument来调用bundles的方法。
//Activity
protected void onSaveInstanceState(Bundle IS){
...
IS.putXXX();
...
}
protected void onCreate(Bundle IS){
...
XXX=IS.getXXX();
...
}
//Fragment
...
Bundle args=new Bundle();
args.putXXX();
fragment.setArguments(args);
...
...
XXX=getArguments().getXXX();
...
获取返回结果
Activity.startActivityForResult()
Fragment.startActivityForResult()
Activity.OnActivityResult()
Fragment.OnActivityResult()
activity和fragment都有各自的方法(如上),只不过用法实在是相似,调用时只需要覆盖对应的方法即可。
但是fragment没有serResult()方法,因为“fragment自身无法持有返回结果”,所以需要让托管它的activity来返回结果值:
getActivity().setResult()
有关挑战练习 实现高效的RecyclerView刷新
我的想法是给每一个CrimeFragment一个在列表中的位置,当我从托管这个CrimeFragment返回时就可以将其位置返回给CrimeListFragment,然后再有这个位置来刷新RecyclerView。
CrimeListFragment将位置信息传递给CrimeActivity:把位置信息绑定给对应的ViewHolder,ViewHolder启动CrimeActivity的同时将位置信息绑定给CrimeActivity。
private abstract class Holder extends RecyclerView.ViewHolder
{
...
public void bind(Crime crime,int position){}
}
private class CrimeHolder extends Holder implements View.OnClickListener
{
...
private int fragmentPosition;
...
public void bind(Crime crime,int position)
{
...
fragmentPosition=position;
}
@Override
public void onClick(View v) {
Intent intent=new Intent(CrimeActivity.newIntent(getActivity(),mCrime.getId(),fragmentPosition));
startActivityForResult(intent,REQUEST_CRIME);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode!= Activity.RESULT_OK)
{
return;
}
if (requestCode==REQUEST_CRIME)
{
if (data==null)
{
return;
}
CrimePosition=CrimeFragment.getPosition(data);
}
}
private class CrimeAdapter extends RecyclerView.Adapter<Holder>
{
...
@Override
public void onBindViewHolder(Holder holder, int position) {
Crime crime=mCrimes.get(position);
//!!!
holder.bind(crime,position);
}
...
}
CrimeActivity将位置信息交给CrimeFragment:将获得的位置信息拆包,附加给创建的CrimeFragment作为Arguments。
private static final String EXTRA_CRIME_ID="com.example.a73421.challenge.crime_id";
//!!!
private static final String EXTRA_CRIME_POSITION="com.example.a73421.challenge.crime_position";
public static Intent newIntent(Context context, UUID crimeID,int position)
{
Intent intent=new Intent(context,CrimeActivity.class);
intent.putExtra(EXTRA_CRIME_ID,crimeID);
//!!!
intent.putExtra(EXTRA_CRIME_POSITION,position);
return intent;
}
@Override
protected Fragment createFragment() {
UUID crimeID=(UUID)getIntent().getSerializableExtra(EXTRA_CRIME_ID);
//!!!
int crimePosition=(int)getIntent().getIntExtra(EXTRA_CRIME_POSITION,0);
return CrimeFragment.newInstance(crimeID,crimePosition);
}
CrimeFragment获取位置信息并且在修改文本之后返回位置信息:
private static final String ARG_CRIME_ID="crime_id";
//!!!
private static final String CRIME_POSITION="crime_position";
private static final String FRAGMENT_POSITION="fragment_position";
private Crime mCrime;
//!!!
private int crimePosition;
...
//!!!
private void returnResult()
{
Intent intent=new Intent();
intent.putExtra(FRAGMENT_POSITION,crimePosition);
getActivity().setResult(Activity.RESULT_OK,intent);
}
//!!!
public static int getPosition(Intent intent)
{
return intent.getIntExtra(FRAGMENT_POSITION,0);
}
@Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
UUID crimeID=(UUID)getArguments().getSerializable(ARG_CRIME_ID);
//!!!
crimePosition=(int)getArguments().getInt(CRIME_POSITION);
mCrime=CrimeLab.get(getActivity()).getCrime(crimeID);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState)
{
...
mTitleField.addTextChangedListener(new TextWatcher() {
...
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mCrime.setTitle(s.toString());
//!!!
returnResult();
}
...});
...
}
这个位置信息的传递过程是CrimeListFragment.Adapter->CrimeListFragment.ViewHolder->CrimeActivity->CrimeFragment->(返回后)CrimeListFragment。看上去就很不合理:因为这个位置信息转了一圈又回到起点了。
观察CZT的做法,他是用了一个函数getLayoutPosition()来直接获取此时的ViewHolder的position,所有代码的改动只有两行。草。
有关挑战练习 优化ClimbLab的表现
原来是遍历找crime的,复杂度为O(n),我觉得吧这个也不是不能接受,非要优化就要用到Map,想要优化效果好还要用unordered_map,但是Java只有hash_map,反正都差不多。
public class CrimeLab
{
private static CrimeLab sCrimeLab;
private List<Crime>mCrimes;
//!!!
private HashMap<UUID,Integer>findCrime=new HashMap<>();
public static CrimeLab get(Context context)
{
if (sCrimeLab==null)
{
sCrimeLab=new CrimeLab(context);
}
return sCrimeLab;
}
private CrimeLab(Context context)
{
mCrimes=new ArrayList<>();
for (int i=0;i<100;i++)
{
Crime crime=new Crime();
crime.setTitle("Crime #"+i);
//!!!
findCrime.put(crime.getId(),i);
crime.setSolved(i%2==0);
crime.setRequiresPolice(i%3==0);
mCrimes.add(crime);
}
}
public List<Crime>getCrimes()
{
return mCrimes;
}
//!!!
public Crime getCrime(UUID id)
{
if (findCrime.get(id)==null)
{
return null;
}
else
{
return mCrimes.get(findCrime.get(id));
}
}
}