在本文中,我们将通过一个真实的示例来说明如何在Android中使用片段 。 在上一篇文章中,我们讨论了Android中的Fragment以及如何使用它来支持多屏幕。 我们描述了他们的生命周期以及如何使用它。 在本文中,我们想更深入地创建一个示例,以帮助我们更好地理解如何使用片段。 例如,我们要创建一个由两个片段构建的简单应用程序:
- 显示链接列表的一个
- 另一个在WebView中显示网页的页面。
我们可以假设我们有两种不同的布局,一种用于纵向模式,一种用于
景观 。 在横向模式下,我们需要类似下图的内容:
同时,在纵向模式下,我们需要以下内容:
创建布局
我们要做的第一步是创建布局。 正如我们所说,我们需要两种不同的布局,一种用于肖像,另一种用于风景。 因此,我们必须在res / layout下创建两个xml文件(用于纵向模式),在res / layout-land下创建一个xml文件(用于横向模式)。 当然,我们可以自定义更多布局,包括其他规格,但现在已经足够。 这两个文件称为activity_layout.xml 。
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<fragment android:id="@+id/listFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="com.survivingwithandroid.fragment.LinkListFragment"/>
</RelativeLayout>
这是一个纵向模式,我们注意到只有一个片段仅包含链接列表。 稍后我们将看到如何创建此片段。 此外,如上图所示,我们需要另一种布局,其中包含WebView 。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<WebView android:id="@+id/webPage"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
</LinearLayout>
对于横向模式,我们有一些非常相似的东西,并且在同一布局中还有FrameLayout组件。
<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:orientation="horizontal" >
<fragment android:id="@+id/listFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
class="com.survivingwithandroid.fragment.LinkListFragment"
android:layout_weight="2"/>
<FrameLayout android:id="@+id/fragPage"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4"
/>
</LinearLayout>
创建链接列表片段
您已经注意到,这两种布局都有一个称为LinkListFragment的公共片段。 我们必须创建它。 如果您还没有阅读解释生命周期的文章,那么现在该看看一下。 在这种情况下,我们不必重写片段生命周期中的所有方法,而是那些对控制其行为很重要的方法。 在我们的情况下,我们需要重写:
- onCreateView
- onAttach
在此片段中,我们使用一个简单的ListView来显示链接,并使用一个简单的适配器来定制项目的显示方式。 我们不想花太多时间在如何创建自定义适配器上,因为它不在本主题范围内,您可以在此处参考以获得更多信息。 只需记住onCreateView片段方法中,我们只需为布局充气并初始化自定义适配器即可。 随着XML布局在片段中的膨胀,我们有:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView android:id="@+id/urls"
android:layout_height="match_parent"
android:layout_width="wrap_content"/>
</LinearLayout>
而该方法看起来像:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("SwA", "LV onCreateView");
View v = inflater.inflate(R.layout.linklist_layout, container, false);
ListView lv = (ListView) v.findViewById(R.id.urls);
la = new LinkAdapter(linkDataList, getActivity());
lv.setAdapter(la);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parentAdapter, View view, int position,
long id) {
LinkData data = ( (LinkAdapter) la ).getItem(position);
( (ChangeLinkListener) getActivity()).onLinkChange(data.getLink());
}
});
return v;
}
在这种方法中,我们只需设置我们的自定义适配器,并在用户单击某个项目时设置侦听器。 稍后我们将进行介绍(如果您感到好奇,请参阅片段间通信)。
在onAttach方法中,我们验证保存该片段的活动是否实现了特定的接口。
@Override
public void onAttach(Activity activity) {
// We verify that our activity implements the listener
if (! (activity instanceof ChangeLinkListener) )
throw new ClassCastException();
super.onAttach(activity);
}
我们稍后将阐明为什么需要此控件。
片段通讯
基本上在我们的示例中,我们有两个片段,它们需要交换信息,以便当用户在片段1(LinkListFragment)中选择一个项目时,另一个(WebViewFragment)显示与该链接相对应的网页。 因此,我们需要找到一种方法来让这些片段交换数据。
另一方面,我们知道片段是一段代码,可以在其他活动中重复使用,因此我们不想将片段绑定到特定活动以免使我们的工作无效。 在Java中,如果我们想分离两个类,可以使用一个接口。 因此,此接口解决方案非常适合。 另一方面,我们不希望片段直接交换信息,因为每个片段只能依靠保存它的活动。 因此,最简单的解决方案是该活动实现一个接口。
因此,在我们的案例中,我们定义了一个名为ChangeLinkListener的接口,该接口只有一种方法:
public interface ChangeLinkListener {
public void onLinkChange(String link);
}
此外,我们还需要验证我们的活动是否实现了此接口,以确保可以调用它。 验证它的最佳位置是onAttach方法(请参见上文),最后,当用户在ListView中选择一个项目时,我们需要调用此方法:
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parentAdapter, View view, int position,
long id) {
LinkData data = ( (LinkAdapter) la ).getItem(position);
(ChangeLinkListener) getActivity()).onLinkChange(data.getLink());
}
});
编程主要活动:查找片段
到目前为止,我们仅讨论了片段,但是我们知道片段存在于控制它们的“父亲”活动中。 因此,我们必须创建此活动,但要做的更多。
如前所述,此活动必须实现一个自定义接口,以便它可以从LinkListFragment接收数据。 在此方法(onLinkChange)中,我们可以通过某种方式控制我们是处于横向模式还是纵向模式,因为在第一种情况下,我们需要更新WebViewFragment,而在第二种情况下,我们必须启动另一个活动。 我们该怎么做? 布局的区别在于FrameLayout的存在。 如果存在,则表示我们处于风景模式,否则处于肖像模式。 因此,onLinkChange方法中的代码是:
@Override
public void onLinkChange(String link) {
System.out.println("Listener");
// Here we detect if there's dual fragment
if (findViewById(R.id.fragPage) != null) { WebViewFragment wvf = (WebViewFragment) getFragmentManager().findFragmentById(R.id.fragPage);
if (wvf == null) {
System.out.println("Dual fragment - 1");
wvf = new WebViewFragment();
wvf.init(link);
// We are in dual fragment (Tablet and so on)
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//wvf.updateUrl(link);
ft.replace(R.id.fragPage, wvf);
ft.commit();
}
else {
Log.d("SwA", "Dual Fragment update");
wvf.updateUrl(link);
}
}
else {
System.out.println("Start Activity");
Intent i = new Intent(this, WebViewActivity.class);
i.putExtra("link", link);
startActivity(i);
}
}
让我们分析一下这种方法。 第一部分(第5行)验证是否存在FrameLayout。 如果存在,则使用FragmentManager查找相对于WebViewFragment的片段。 如果该片段为null(因此这是我们第一次使用它),我们只需创建它,然后将此片段放在FrameLayout的“内部”(第7-20行)。 如果该片段已经存在,我们只需更新url(第23行)。 如果不处于横向模式,则可以启动一个新活动,将数据作为Intent传递(第28-30行)。
WEBVIEW片段和WEBVIEWACTIVITY:该网页
最后,我们分析WebViewFragment。 这真的很简单,它只是重写某些Fragment方法以自定义其行为:
public class WebViewFragment extends Fragment {
private String currentURL;
public void init(String url) {
currentURL = url;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("SwA", "WVF onCreateView");
View v = inflater.inflate(R.layout.web_layout, container, false);
if (currentURL != null) {
Log.d("SwA", "Current URL 1["+currentURL+"]");
WebView wv = (WebView) v.findViewById(R.id.webPage);
wv.getSettings().setJavaScriptEnabled(true);
wv.setWebViewClient(new SwAWebClient());
wv.loadUrl(currentURL);
}
return v;
}
public void updateUrl(String url) {
Log.d("SwA", "Update URL ["+url+"] - View ["+getView()+"]");
currentURL = url;
WebView wv = (WebView) getView().findViewById(R.id.webPage);
wv.getSettings().setJavaScriptEnabled(true);
wv.loadUrl(url);
}
private class SwAWebClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
}
}
在onCreateView方法中,我们只是在片段内增加布局,并验证要显示的URL不为空。 如果是这样,我们只需显示页面(第15-30行)。 在updateUrl中,我们只需找到WebView组件并更新其url。
在纵向模式下,我们说我们需要启动另一个活动来显示网页,因此我们需要一个活动(WebViewActivity)。 这真的很简单,我只显示代码而没有其他注释:
public class WebViewActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebViewFragment wvf = new WebViewFragment();
Intent i = this.getIntent();
String link = i.getExtras().getString("link");
Log.d("SwA", "URL ["+link+"]");
wvf.init(link);
getFragmentManager().beginTransaction().add(android.R.id.content, wvf).commit();
}
}
源代码@ github 。
翻译自: https://www.javacodegeeks.com/2013/06/fragment-in-android-tutorial-with-example-using-webview.html