Android开源中国客户端学习 查看新闻界面学习 <6>

OSC客户端新闻详情页解析
本文解析了OSC客户端新闻详情页的功能实现,包括新闻内容显示、图片点击放大、超链接处理及评论显示等功能,并介绍了未读评论提示、收藏操作及界面切换等细节。

首先声明一点,这里写的东西只是自己记录笔记使用的东西而已,我希望的是学习人家的架构和编程经历,不会对osc客户端进行改造什么的,当然我会有自己的看法,但是经验要有限,烦请指正.

OSC的主界面和大部分的新闻客户端的主界面是相似的,也使用了webview,同样也是使用了loadDataWithBaseURL这个函数来load String.

在这里还会介绍一下其他小知识:

   未读评论提示,

   超链接的处理,点击webview中的图片可以查看详细图片

  

1.整体功能介绍

先是获取数据,这个功能在initData函数中,请求的流程和第一篇的大体一致,这里就不再做介绍

这里着重介绍的是获取数据后对界面显示的处理 是在handler的handleMessage成员函数中


mHandler = new Handler() {
			public void handleMessage(Message msg) {
				if (msg.what == 1) {
					headButtonSwitch(DATA_LOAD_COMPLETE);

					mTitle.setText(newsDetail.getTitle());
					mAuthor.setText(newsDetail.getAuthor());
					mPubDate.setText(StringUtils.friendly_time(newsDetail
							.getPubDate()));
					mCommentCount.setText(String.valueOf(newsDetail
							.getCommentCount()));

					// 是否收藏
					if (newsDetail.getFavorite() == 1)
						mFavorite
								.setImageResource(R.drawable.widget_bar_favorite2);
					else
						mFavorite
								.setImageResource(R.drawable.widget_bar_favorite);

					// 显示评论数
					if (newsDetail.getCommentCount() > 0) {
						bv_comment.setText(newsDetail.getCommentCount() + "");
						bv_comment.show();
					} else {
						bv_comment.setText("");
						bv_comment.hide();
					}

					String body = UIHelper.WEB_STYLE + newsDetail.getBody();
					// 读取用户设置:是否加载文章图片--默认有wifi下始终加载图片
					boolean isLoadImage;
					AppContext ac = (AppContext) getApplication();
					if (AppContext.NETTYPE_WIFI == ac.getNetworkType()) {
						isLoadImage = true;
					} else {
						isLoadImage = ac.isLoadImage();
					}
					if (isLoadImage) {
						// 过滤掉 img标签的width,height属性
						body = body.replaceAll(
								"(<img[^>]*?)\\s+width\\s*=\\s*\\S+", "$1");
						body = body.replaceAll(
								"(<img[^>]*?)\\s+height\\s*=\\s*\\S+", "$1");

						// 添加点击图片放大支持
						body = body.replaceAll("(<img[^>]+src=\")(\\S+)\"",
								"$1$2\" onClick=\"javascript:mWebViewImageListener.onImageClick('$2')\"");

					} else {
						// 过滤掉 img标签
						body = body.replaceAll("<\\s*img\\s+([^>]*)\\s*>", "");
					}

					// 更多关于***软件的信息
					String softwareName = newsDetail.getSoftwareName();
					String softwareLink = newsDetail.getSoftwareLink();
					if (!StringUtils.isEmpty(softwareName)
							&& !StringUtils.isEmpty(softwareLink))
						body += String
								.format("<div id='oschina_software' style='margin-top:8px;color:#FF0000;font-weight:bold'>更多关于:&nbsp;<a href='%s'>%s</a>&nbsp;的详细信息</div>",
										softwareLink, softwareName);

					// 相关新闻
					if (newsDetail.getRelatives().size() > 0) {
						String strRelative = "";
						for (Relative relative : newsDetail.getRelatives()) {
							strRelative += String
									.format("<a href='%s' style='text-decoration:none'>%s</a><p/>",
											relative.url, relative.title);
						}
						body += String.format(
								"<p/><hr/><b>相关资讯</b><div><p/>%s</div>",
								strRelative);
					}

					body += "<div style='margin-bottom: 80px'/>";

					System.out.println(body);

					mWebView.loadDataWithBaseURL(null, body, "text/html",
							"utf-8", null);
					mWebView.setWebViewClient(UIHelper.getWebViewClient());

					// 发送通知广播
					if (msg.obj != null) {
						UIHelper.sendBroadCast(NewsDetail.this,
								(Notice) msg.obj);
					}
				} else if (msg.what == 0) {
					headButtonSwitch(DATA_LOAD_FAIL);

					UIHelper.ToastMessage(NewsDetail.this,
							R.string.msg_load_is_null);
				} else if (msg.what == -1 && msg.obj != null) {
					headButtonSwitch(DATA_LOAD_FAIL);

					((AppException) msg.obj).makeToast(NewsDetail.this);
				}
			}
		};
①在html中添加一个Style 确定默认的html风格


String body = UIHelper.WEB_STYLE + newsDetail.getBody();

这里getBody函数获取的将会是新闻内容的html代码,服务器返回的也是这个代码

②读取加载图片开关,如果是wifi默认会加载图片 如果设置了不能加载图片就把img标签替换为空 

如果加载图片还会做两个事情 过滤width 和height 属性 并且添加onClick的js函数,实现稍后介绍


boolean isLoadImage;
					AppContext ac = (AppContext) getApplication();
					if (AppContext.NETTYPE_WIFI == ac.getNetworkType()) {
						isLoadImage = true;
					} else {
						isLoadImage = ac.isLoadImage();
					}
					if (isLoadImage) {
						// 过滤掉 img标签的width,height属性
						body = body.replaceAll(
								"(<img[^>]*?)\\s+width\\s*=\\s*\\S+", "$1");
						body = body.replaceAll(
								"(<img[^>]*?)\\s+height\\s*=\\s*\\S+", "$1");

						// 添加点击图片放大支持
						body = body.replaceAll("(<img[^>]+src=\")(\\S+)\"",
								"$1$2\" onClick=\"javascript:mWebViewImageListener.onImageClick('$2')\"");

					} else {
						// 过滤掉 img标签
						body = body.replaceAll("<\\s*img\\s+([^>]*)\\s*>", "");
					}


然后还会出来相关资讯等信息 也是替换字符串 

③接着会把加工后的新闻内容显示到webview中去

mWebView.loadDataWithBaseURL(null, body, "text/html","utf-8", null);

④ 我们知道,这个新闻页面同样需要处理超链接等数据,这里由于使用的是webview,所以和前面textview处理超链接有些不同但是更加简单,我们知道Webview有个setWebViewClient函数可以处理超链接跳转等功能,oscapp也是使用了这个方法:


mWebView.setWebViewClient(UIHelper.getWebViewClient());
那么看一下getWebViewClient()这个函数做了什么:



public static WebViewClient getWebViewClient() {
		return new WebViewClient() {
			@Override
			public boolean shouldOverrideUrlLoading(WebView view, String url) {
				showUrlRedirect(view.getContext(), url);
				return true;
			}
		};
	}
showUrlRedirect函数的处理



/**
	 * url跳转
	 * 
	 * @param context
	 * @param url
	 */
	public static void showUrlRedirect(Context context, String url) {
		URLs urls = URLs.parseURL(url);
		if (urls != null) {
			showLinkRedirect(context, urls.getObjType(), urls.getObjId(),
					urls.getObjKey());
		} else {
			openBrowser(context, url);
		}
	}
这里只分析showLinkRedirect方法



public static void showLinkRedirect(Context context, int objType,
			int objId, String objKey) {
		switch (objType) {
		case URLs.URL_OBJ_TYPE_NEWS:
			showNewsDetail(context, objId);
			break;
		case URLs.URL_OBJ_TYPE_QUESTION:
			showQuestionDetail(context, objId);
			break;
		case URLs.URL_OBJ_TYPE_QUESTION_TAG:
			showQuestionListByTag(context, objKey);
			break;
		case URLs.URL_OBJ_TYPE_SOFTWARE:
			showSoftwareDetail(context, objKey);
			break;
		case URLs.URL_OBJ_TYPE_ZONE:
			showUserCenter(context, objId, objKey);
			break;
		case URLs.URL_OBJ_TYPE_TWEET:
			showTweetDetail(context, objId);
			break;
		case URLs.URL_OBJ_TYPE_BLOG:
			showBlogDetail(context, objId);
			break;
		case URLs.URL_OBJ_TYPE_OTHER:
			openBrowser(context, objKey);
			break;
		}
	}
那么我们发现整体的思路和之前我们设想的是一样的,更加WebViewClient中shouldOverrideUrlLoading函数获取到的url参数进行判断然后进行跳转.


URLs urls = URLs.parseURL(url);这句话其实就是解析url为两个参数:链接类型,和对应新闻条目的ID 这样就可以跳转到相应页面的相应条目了

至于最后一句发送广播在后面章节中还会有介绍 

⑤让我们回到前面,新闻详细页面还有一个功能,点击图片显示大图,可以做两点放大缩小等操作等等,也是使用了网上的一个代码,具体原理是使用matrix 缩放图片的bitmap

我们关注的还是图片如何响应点击事件:

前面我们已经个image添加了点击事件:

body = body.replaceAll("(<img[^>]+src=\")(\\S+)\"",
"$1$2\" onClick=\"javascript:mWebViewImageListener.onImageClick('$2')\"");
那么我们如何使用呢?

其实在WebView初始化的事情注册了响应这个onclick 的事件(可以这么说吗?):

UIHelper.addWebImageShow(this, mWebView);

这个注册js函数的实现:

/**
	 * 添加网页的点击图片展示支持
	 */
	@SuppressLint("SetJavaScriptEnabled")
	public static void addWebImageShow(final Context cxt, WebView wv) {
		wv.getSettings().setJavaScriptEnabled(true);
		wv.addJavascriptInterface(new OnWebViewImageListener() {

			@Override
			public void onImageClick(String bigImageUrl) {
				if (bigImageUrl != null)
					UIHelper.showImageZoomDialog(cxt, bigImageUrl);
			}
		}, "mWebViewImageListener");
	}
showImageZoomdialog就是打开查看图片的activity了.


2.其他小知识点介绍

①对于评论的未读条目数显示,这里作者使用了一个开源的控件,

用法如下


bv_comment = new BadgeView(this, mCommentList);
		bv_comment.setBackgroundResource(R.drawable.widget_count_bg2);
		bv_comment.setIncludeFontPadding(false);
		bv_comment.setGravity(Gravity.CENTER);
		bv_comment.setTextSize(8f);
		bv_comment.setTextColor(Color.WHITE);
以本案例为例分析,大体的原理如下:


  获取到评论imageview的 parent 

  new 一个framelayout然后把这个imageview和新new出来的气泡view添加到framelayout中

  把imageview从parent中remove掉 把这个framelayout添加到parent中

这样就显示这个未读提示气泡了


②还有一个小细节是收藏的设计

点击收藏会启动一个线程 请求服务器处理,如果收藏成功,在修改UI的同事也设置内存缓存中的fav 更改,并重新序列化到本地 这样的处理逻辑还是不错的,这里我们就又看到了getCacheKey的用法 代码如下:


final Handler handler = new Handler() {
				public void handleMessage(Message msg) {
					if (msg.what == 1) {
						Result res = (Result) msg.obj;
						if (res.OK()) {
							if (newsDetail.getFavorite() == 1) {
								newsDetail.setFavorite(0);
								mFavorite
										.setImageResource(R.drawable.widget_bar_favorite);
							} else {
								newsDetail.setFavorite(1);
								mFavorite
										.setImageResource(R.drawable.widget_bar_favorite2);
							}
							// 重新保存缓存
							ac.saveObject(newsDetail, newsDetail.getCacheKey());
						}
						UIHelper.ToastMessage(NewsDetail.this,
								res.getErrorMessage());
					} else {
						((AppException) msg.obj).makeToast(NewsDetail.this);
					}
				}
			};
			new Thread() {
				public void run() {
					Message msg = new Message();
					Result res = null;
					try {
						if (newsDetail.getFavorite() == 1) {
							res = ac.delFavorite(uid, newsId,
									FavoriteList.TYPE_NEWS);
						} else {
							res = ac.addFavorite(uid, newsId,
									FavoriteList.TYPE_NEWS);
						}
						msg.what = 1;
						msg.obj = res;
					} catch (AppException e) {
						e.printStackTrace();
						msg.what = -1;
						msg.obj = e;
					}
					handler.sendMessage(msg);
				}
			}.start();
		}


③对于评论和新闻内容的切换 使用了一个 以前没有用过的控件ViewSwitch 其实用法也是很简单,可以省去一堆gone 了

/**
	 * 底部栏切换
	 * 
	 * @param type
	 */
	private void viewSwitch(int type) {
		switch (type) {
		case VIEWSWITCH_TYPE_DETAIL:
			mDetail.setEnabled(false);
			mCommentList.setEnabled(true);
			mHeadTitle.setText(R.string.news_detail_head_title);
			mViewSwitcher.setDisplayedChild(0);
			break;
		case VIEWSWITCH_TYPE_COMMENTS:
			mDetail.setEnabled(true);
			mCommentList.setEnabled(false);
			mHeadTitle.setText(R.string.comment_list_head_title);
			mViewSwitcher.setDisplayedChild(1);
			break;
		}
	}

④双击全屏使用了GestureDetector:

/**
	 * 注册双击全屏事件
	 */
	private void regOnDoubleEvent() {
		gd = new GestureDetector(this,
				new GestureDetector.SimpleOnGestureListener() {
					@Override
					public boolean onDoubleTap(MotionEvent e) {
						isFullScreen = !isFullScreen;
						if (!isFullScreen) {
							WindowManager.LayoutParams params = getWindow()
									.getAttributes();
							params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
							getWindow().setAttributes(params);
							getWindow()
									.clearFlags(
											WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
							mHeader.setVisibility(View.VISIBLE);
							mFooter.setVisibility(View.VISIBLE);
						} else {
							WindowManager.LayoutParams params = getWindow()
									.getAttributes();
							params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
							getWindow().setAttributes(params);
							getWindow()
									.addFlags(
											WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
							mHeader.setVisibility(View.GONE);
							mFooter.setVisibility(View.GONE);
						}
						return true;
					}
				});
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		if (isAllowFullScreen()) {
			gd.onTouchEvent(event);
		}
		return super.dispatchTouchEvent(event);
	}



转载于:https://my.oschina.net/sfshine/blog/142872

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值