前言
从Flickr下载的图片都有其关联网页。本章,我们继续升级PhotoGallery应用,让用户点击图片就能看到它的Flickr网页。我们会以两种不同的方式整合网页内容,左边是使用浏览器应用,右边是使用WebView在应用中显示网页内容。
一 最后一段 Flickr 数据
在GalleryItem中添加代码清单30-1所示代码,创建图片URL。
代码清单30-1 添加创建图片URL的代码(GalleryItem.java)
public class GalleryItem {
private String mCaption;
private String mId;
private String mUrl;
private String mOwner;
...
public void setUrl(String url) {
mUrl = url;
}
public String getOwner() {
return mOwner;
}
public void setOwner(String owner) {
mOwner = owner;
}
public Uri getPhotoPageUri() {
return Uri.parse("http://www.flickr.com/photos/")
.buildUpon()
.appendPath(mOwner)
.appendPath(mId)
.build();
}
@Override
public String toString() {
return mCaption;
}
}
以上代码新建了一个mOwner属性,以及一个产生图片URL的getPhotoPageUri()方法。现在,修改parseItems(…)方法,从JSON数据中获取owner属性,如代码清单30-2所示。
代码清单30-2 从JSON数据中获取owner属性(FlickrFetchr.java)
public class FlickrFetchr {
...
private void parseItems(List<GalleryItem> items, JSONObject jsonBody)
throws IOException, JSONException {
JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
for (int i = 0; i < photoJsonArray.length(); i++) {
JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
GalleryItem item = new GalleryItem();
item.setId(photoJsonObject.getString("id"));
item.setCaption(photoJsonObject.getString("title"));
if (!photoJsonObject.has("url_s")) {
continue;
}
item.setUrl(photoJsonObject.getString("url_s"));
item.setOwner(photoJsonObject.getString("owner"));
items.add(item);
}
}
}
获取图片网页URL的任务就完成了。
二 简单方式:隐式 intent
首先,监听 RecyclerView 显示项的点击事件。更新 PhotoGalleryFragment 类 的
PhotoHolder,实现一个可以发送隐式intent的事件监听方法,如代码清单30-3所示。
代码清单30-3 通过隐式intent实现网页浏览(PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends VisibleFragment {
...
private class PhotoHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
private ImageView mItemImageView;
private GalleryItem mGalleryItem;
public PhotoHolder(View itemView) {
super(itemView);
mItemImageView = (ImageView) itemView.findViewById(R.id.item_image_view);
itemView.setOnClickListener(this);
}
public void bindDrawable(Drawable drawable) {
mItemImageView.setImageDrawable(drawable);
}
public void bindGalleryItem(GalleryItem galleryItem) {
mGalleryItem = galleryItem;
}
@Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_VIEW, mGalleryItem.getPhotoPageUri());
startActivity(i);
}
}
...
}
然后,在PhotoAdapter.onBindViewHolder(…)方法中绑定PhotoHolder给GalleryItem,
如代码清单30-4所示。
代码清单30-4 绑定GalleryItem(PhotoGalleryFragment.java)
private class PhotoAdapter extends RecyclerView.Adapter<PhotoHolder> {
...
@Override
public void onBindViewHolder(PhotoHolder photoHolder, int position) {
GalleryItem galleryItem = mGalleryItems.get(position);
photoHolder.bindGalleryItem(galleryItem);
Drawable placeholder = getResources().getDrawable(R.drawable.bill_up_close);
photoHolder.bindDrawable(placeholder);
mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
}
...
}
启动PhotoGallery应用并点击任意图片。浏览器应用应该会弹出并加载显示对应的图片网页。
三 较难方式:使用 WebView
创建fragment。新建PhotoPageFragment类,继承上一章的VisibleFragment类。然后,在这个新类中,实例化布局文件,引用WebView,并转发从intent数据中获取的URL,如代码清单30-5所示。
代码清单30-5 创建网页浏览fragment(PhotoPageFragment.java)
public class PhotoPageFragment extends VisibleFragment {
private static final String ARG_URI = "photo_page_url";
private Uri mUri;
private WebView mWebView;
public static PhotoPageFragment newInstance(Uri uri) {
Bundle args = new Bundle();
args.putParcelable(ARG_URI, uri);
PhotoPageFragment fragment = new PhotoPageFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUri = getArguments().getParcelable(ARG_URI);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, container, false);
mWebView = (WebView) v.findViewById(R.id.web_view);
return v;
}
}
当前,PhotoPageFragment类还未完成,稍后再来完成它。接下来,新建PhotoPageActivity托管类,继承SingleFragmentActivity类,如代码清单30-6所示。
代码清单30-6 创建显示网页的activity(PhotoPageActivity.java)
public class PhotoPageActivity extends SingleFragmentActivity {
public static Intent newIntent(Context context, Uri photoPageUri) {
Intent i = new Intent(context, PhotoPageActivity.class);
i.setData(photoPageUri);
return i;
}
@Override
protected Fragment createFragment() {
return PhotoPageFragment.newInstance(getIntent().getData());
}
}
回到PhotoGalleryFragment类中,弃用隐式intent,启动新建的activity,如代码清单30-7所示。
代码清单30-7 启动新建的activity(PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends VisibleFragment {
...
private class PhotoHolder extends RecyclerView.ViewHolder
implements View.OnClickListener{
...
@Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_VIEW, mGalleryItem.getPhotoPageUri());
Intent i = PhotoPageActivity
.newIntent(getActivity(), mGalleryItem.getPhotoPageUri());
startActivity(i);
}
}
...
}
最后,在配置文件中声明新建的activity,如代码清单30-8所示。
代码清单30-8 在配置文件中声明activity(AndroidManifest.xml)
<manifest ... >
...
<application
...>
<activity
android:name=".PhotoGalleryActivity"
android:label="@string/app_name" >
...
</activity>
<activity
android:name=".PhotoPageActivity" />
<service android:name=".PollService" />
...
</application>
</manifest>
运行PhotoGallery应用,点击任意图片,可看到一个新的空activity弹出。
首先是告诉WebView要打开的URL。
其次是启用JavaScript。JavaScript默认是禁用的。虽然并不总是需要启用它,但Flickr网站需要。(启用JavaScript后,Android Lint会提示警告信息(担心跨网站的脚本攻击),可以使用@SuppressLint(“SetJavaScriptEnabled”)注解onCreateView(…)方法以禁止Lint的警告。)
最后,需要实现一个WebViewClient类(用来响应WebView上的渲染事件)。添加代码清单30-9所示代码。然后,我们来详细解读PhotoPageFragment类。
代码清单30-9 加载URL(PhotoPageFragment.java)
public class PhotoPageFragment extends VisibleFragment {
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, container, false);
mWebView = (WebView) v.findViewById(R.id.web_view);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
mWebView.loadUrl(mUri.toString());
return v;
}
}
为使用ProgressBar,还需使用WebView:WebChromeClient的第二个回调方法。如果说WebViewClient是响应渲染事件的接口,那么WebChromeClient就是一个事件接口,用来响应那些改变浏览器中装饰元素的事件。这包括JavaScript警告信息、网页图标、状态条加载,以及当前网页标题的刷新。在onCreateView(…)方法中,编码实现使用WebChromeClient,如代码清单30-10所示。
代码清单30-10 使用WebChromeClient(PhotoPageFragment.java)
public class PhotoPageFragment extends VisibleFragment {
...
private WebView mWebView;
private ProgressBar mProgressBar;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, container, false);
mProgressBar = (ProgressBar)v.findViewById(R.id.progress_bar);
mProgressBar.setMax(100); // WebChromeClient reports in range 0-100
mWebView = (WebView) v.findViewById(R.id.web_view);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView webView, int newProgress) {
if (newProgress == 100) {
mProgressBar.setVisibility(View.GONE);
} else {
mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.setProgress(newProgress);
}
}
public void onReceivedTitle(WebView webView, String title) {
AppCompatActivity activity = (AppCompatActivity) getActivity();
activity.getSupportActionBar().setSubtitle(title);
}
});
mWebView.setWebViewClient(new WebViewClient());
mWebView.loadUrl(mUri.toString());
return v;
}
}
点击任意图片,PhotoPageActivity会出现。网页加载时,会出现进度条,工具栏会出现来自onReceivedTitle(…)方法的子标题。页面加载完毕,进度条随即消失。
四 处理 WebView 的设备旋转问题
为了让PhotoPageActivity自己处理设备配置调整,可在AndroidManifest.xml配置文件中做
如下调整,如代码清单30-11所示。
代码清单30-11 自己处理设备配置更改(AndroidManifest.xml)
<manifest ... >
...
<activity
android:name=".PhotoPageActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
...
</manifest>
android:configChanges属性表明,如果因键盘开或关、屏幕方向改变、屏幕大小改变(也包括Android 3.2之后的屏幕方向变化)而发生设备配置更改,那么activity应自己处理配置更改。