【发布时间】:2014-06-06 11:45:58
【问题描述】:
编辑:当我刚开始进行 Android 应用程序开发时,这个问题是作为我的第一个 Android 项目之一创建的。出于历史原因,我保留它,但您应该考虑改用 EventBus 或 RxJava。这是一个巨大的混乱。
请不要考虑使用它。谢谢。
事实上,如果你想要一些很酷的东西来解决使用具有多个“片段”的单个活动的问题,那么使用带有自定义视图组的flowless。
我已经实现了一种方法来启动片段的创建,从片段使用广播意图通过 LocalBroadcastManager 告诉 Activity 要实例化什么片段。
我知道这是一个非常长的代码量,但我不是要求调试,它按我的预期完美运行 - 接收到数据,创建可以通过 Bundle 参数化,并且 Fragment 不直接实例化其他片段。
public abstract class FragmentCreator implements Parcelable
{
public static String fragmentCreatorKey = "fragmentCreator";
public static String fragmentCreationBroadcastMessage = "fragment-creation";
public static String fragmentDialogCreationBroadcastMessage = "fragment-dialog-creation";
protected Bundle arguments;
protected Boolean hasBundle;
public FragmentCreator(Bundle arguments, boolean hasBundle)
{
this.arguments = arguments;
this.hasBundle = hasBundle;
}
protected FragmentCreator(Parcel in)
{
hasBundle = (Boolean) in.readSerializable();
if (hasBundle == true && arguments == null)
{
arguments = in.readBundle();
}
}
public Fragment createFragment()
{
Fragment fragment = instantiateFragment();
if (arguments != null)
{
fragment.setArguments(arguments);
}
return fragment;
}
protected abstract Fragment instantiateFragment();
@Override
public int describeContents()
{
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeSerializable(hasBundle);
if (arguments != null)
{
arguments.writeToParcel(dest, 0);
}
}
public void sendFragmentCreationMessage(Context context)
{
Intent intent = new Intent(FragmentCreator.fragmentCreationBroadcastMessage);
intent.putExtra(FragmentCreator.fragmentCreatorKey, this);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
public void sendDialogFragmentCreationMessage(Context context)
{
Intent intent = new Intent(FragmentCreator.fragmentDialogCreationBroadcastMessage);
intent.putExtra(FragmentCreator.fragmentCreatorKey, this);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
}
这样,创建的 Fragment 如下所示:
public class TemplateFragment extends Fragment implements GetActionBarTitle, View.OnClickListener
{
private int titleId;
public TemplateFragment()
{
titleId = R.string.app_name;
}
@Override
public int getActionBarTitleId()
{
return titleId;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_template, container, false);
return rootView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onClick(View v)
{
}
public static class Creator extends FragmentCreator
{
public Creator()
{
super(null, false);
}
public Creator(Bundle bundle)
{
super(bundle, true);
}
protected Creator(Parcel in)
{
super(in);
}
@Override
protected Fragment instantiateFragment()
{
return new TemplateFragment();
}
@SuppressWarnings("unused")
public static final Parcelable.Creator<TemplateFragment.Creator> CREATOR = new Parcelable.Creator<TemplateFragment.Creator>()
{
@Override
public TemplateFragment.Creator createFromParcel(Parcel in)
{
return new TemplateFragment.Creator(in);
}
@Override
public TemplateFragment.Creator[] newArray(int size)
{
return new TemplateFragment.Creator[size];
}
};
}
}
可以处理消息的初始容器活动如下所示:
Intent intent = new Intent();
intent.setClass(this.getActivity(), ContainerActivity.class);
intent.putExtra(FragmentCreator.fragmentCreatorKey,
new TemplateFragment.Creator());
startActivity(intent);
片段像这样“实例化其他片段”:
Bundle bundle = new Bundle();
bundle.putParcelable("argument", data);
TemplateFragment.Creator creator = new TemplateFragment.Creator(bundle);
creator.sendFragmentCreationMessage(getActivity());
并且容器Activity接收到实例化请求:
public class ContainerActivity extends ActionBarActivity implements SetFragment, ShowDialog
{
private BroadcastReceiver mFragmentCreationMessageReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
setFragment((FragmentCreator) intent.getParcelableExtra(FragmentCreator.fragmentCreatorKey));
}
};
private BroadcastReceiver mFragmentDialogCreationMessageReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
showDialog((FragmentCreator) intent.getParcelableExtra(FragmentCreator.fragmentCreatorKey));
}
};
@Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
getActionBar().setDisplayHomeAsUpEnabled(true);
if (saveInstanceState == null)
{
Fragment fragment = ((FragmentCreator) getIntent().getParcelableExtra(
FragmentCreator.fragmentCreatorKey)).createFragment();
if (fragment != null)
{
replaceFragment(fragment);
}
}
else
{
this.getActionBar()
.setTitle(
((GetActionBarTitle) (this.getSupportFragmentManager()
.findFragmentById(R.id.activity_container_container)))
.getActionBarTitleId());
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
@Override
protected void onResume()
{
LocalBroadcastManager.getInstance(this).registerReceiver(mFragmentCreationMessageReceiver,
new IntentFilter(FragmentCreator.fragmentCreationBroadcastMessage));
LocalBroadcastManager.getInstance(this).registerReceiver(mFragmentDialogCreationMessageReceiver,
new IntentFilter(FragmentCreator.fragmentDialogCreationBroadcastMessage));
super.onResume();
}
@Override
protected void onPause()
{
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mFragmentCreationMessageReceiver);
LocalBroadcastManager.getInstance(this).unregisterReceiver(
mFragmentDialogCreationMessageReceiver);
}
@Override
public void setFragment(FragmentCreator fragmentCreator)
{
Fragment fragment = fragmentCreator.createFragment();
replaceFragment(fragment);
}
public void replaceFragment(Fragment fragment)
{
if (fragment != null)
{
this.setTitle(((GetActionBarTitle) fragment).getActionBarTitleId());
getSupportFragmentManager().beginTransaction()
.replace(R.id.activity_container_container, fragment).addToBackStack(null).commit();
}
}
@Override
public void showDialog(FragmentCreator fragmentCreator)
{
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fragmentCreator.createFragment();
if (fragment instanceof DialogFragment)
{
DialogFragment df = (DialogFragment) fragment;
df.show(fm, "dialog");
}
else
{
Log.e(this.getClass().getSimpleName(), "showDialog() called with non-dialog parameter!");
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
this.onBackPressed();
}
return super.onOptionsItemSelected(item);
}
}
我的问题是,这实际上是一个好主意,还是这是一个“过度工程”的可怕案例(为每个 Fragment 创建一个工厂并以本地广播的形式将其发送到 Activity,而不仅仅是投射最可能的持有者活动接口的活动并像那样调用函数)?
我的目标是这样,我可以使用相同的 Activity 来保存“分支”片段,这样我就不需要为每个菜单点创建一个。而不是仅仅重复使用相同的活动,并将所有逻辑划分为片段。 (目前它不支持基于方向的布局组织,我看到了它的缺点——而且这样每个 Fragment 都需要拥有一个静态创建者,这是额外的“样板代码”)。
如果您知道为什么我不应该为此使用本地广播管理器,我会很高兴听到回复。我认为它非常简洁,但有可能只是使简单的事情过于复杂。
【问题讨论】:
-
你可以使用一个简单的回调方法,它更干净,更短的方法
-
你能提供一个示例代码或链接吗?目前我开始使用类似 '((ShowDialog) this.getActivity()).showDialog(new EditNameDialog.Creator());'但即使这样也使用了创造者。我的目标是能够在许多片段中重用相同的“容器”活动,这样我就不需要为每个子菜单创建一个活动。
-
Activity 和 Fragments 之间的通信,你也可以使用 EventBus (github.com/greenrobot/EventBus),它大大简化了代码。
-
我想我以后也会尝试一下,也许如果我将它与 Dagger 集成它会看起来非常漂亮。
标签: android android-fragments android-broadcast