zoukankan      html  css  js  c++  java
  • 安卓开发笔记——打造属于自己的博客园APP(三)

      在上一篇文章《安卓开发笔记——打造属于自己的博客园APP(二)》中,我们基本上实现了主界面的搭建,网络框架的搭建,各博客列表页面的展示包括更新效果,对图片做了三级缓存处理(后面会把文章,新闻做成离线闪存,实现无网络也能照常浏览)。

      今天来讲讲博客详情页和评论页面的实现,国际惯例,先上效果图:(动态图片比较大,加载需要点时间)

     

      这里说下,关于上篇文章XML的解析,我后来查了下确实有一些方便解析的工具,例如:FastXML,Xstram等且效率更高,这里是在它的官方找到的一张数据图:

    文章里我就还是先采用原生的pull解析了,等重构代码时候再换上工具类吧,先把项目做完。 

      好了,废话不多说了,直接进入主题:

    1、关于RecyclerView的点击事件

      首先先来说下关于RecyclerView的点击监听事件,在上篇文章提到,RecyclerView是ListView升级版,顾名思义它是为效率而生的,它不关心多余的任何事情,比如Item项的动作监听,Item项的分割线,Item项的添加动画效果,只专注于数据的展示实现,相比ListView它更符合软件设计原则,更加解耦。

      上面提到它不关心Item项的动作监听,很自然,它没有和ListView控件一样提供类似setOnItemClickListener这种监听方法,需要我们自己来实现,那么很自然的,我们会选择在Adapter里去设置监听事件,关于RecyclerView不熟悉的朋友可以先看下这篇文章:http://blog.csdn.net/lmj623565791/article/details/45059587

      首先我们在Adapter设置一个点击回调接口,并提供setter方法:

     1     /**
     2      * 自定义点击回调接口
     3      */
     4     public interface RecyclerViewListener {
     5         void setOnclickListener(View view, int pos);
     6     }
     7 
     8     private RecyclerViewListener mRecyclerViewListener;
     9 
    10     /**
    11      * 提供setter方法
    12      *
    13      * @param recyclerViewListener
    14      */
    15     public void setRecyclerViewListener(RecyclerViewListener recyclerViewListener) {
    16         this.mRecyclerViewListener = recyclerViewListener;
    17     }

      然后我们在onBindViewHolder中设置监听事件:

    1         //设置点击监听
    2         viewholder.itemView.setTag(i);
    3         viewholder.mMore.setTag(Integer.MAX_VALUE);
    4         viewholder.itemView.setOnClickListener(new ItemClick());
    5         viewholder.mMore.setOnClickListener(new ItemClick());

      再来个实现接口:

        /**
         * 点击事件实现类
         */
        public class ItemClick implements View.OnClickListener{
            @Override
            public void onClick(View v) {
                if(mRecyclerViewListener!=null){
                    mRecyclerViewListener.setOnclickListener(v,(int)v.getTag());
                }
            }
        }

      这样子,我们就可以在我们的博客列表页面设置点击事件了,关于下面的showPopUpMenu是一个点击弹出窗口,可以实现收藏博文和关注博主的功能,我们下一篇文章会提到。这里我们实现当点击RecyclerView条目的时候会通过Intent传递Blog对象到博文详情页面。

     1         //设置条目点击监听
     2         mBlogListAdapter.setRecyclerViewListener(new BlogListAdapter.RecyclerViewListener() {
     3             @Override
     4             public void setOnclickListener(View view, int pos) {
     5 
     6                 if (view.getId() == R.id.ib_more) {
     7                     //点击菜单按钮
     8                     showPopUpMenu(view, pos);
     9                 } else {
    10                     //点击条目,传递对象
    11                     Intent intent = new Intent();
    12                     intent.setClass(getActivity(), BlogContentActivity.class);
    13                     Bundle bundle = new Bundle();
    14                     bundle.putSerializable("blog", mBlogs.get(pos));
    15                     intent.putExtras(bundle);
    16                     startActivity(intent);
    17                 }
    18             }
    19         });

    2、关于博文详情页面的实现

      这里是关于博文详情的接口:http://wcf.open.cnblogs.com/blog/post/body/{POSTID} (POSTID代表文章Id)

    这里是关于博文详情的XML解析代码:

     1 package com.lcw.rabbit.myblog.parser;
     2 
     3 import org.xmlpull.v1.XmlPullParser;
     4 import org.xmlpull.v1.XmlPullParserException;
     5 import org.xmlpull.v1.XmlPullParserFactory;
     6 
     7 import java.io.IOException;
     8 import java.io.InputStream;
     9 
    10 /**
    11  * 对博文详情xml数据的解析
    12  * Created by Lichenwei
    13  * Date: 2015-08-17
    14  * Time: 13:32
    15  */
    16 public class BlogContentXmlParser {
    17 
    18 
    19     /**
    20      * 用于解析博文详情的xml,返回Avatar的List集合对象
    21      *
    22      * @param inputStream
    23      * @param encode
    24      * @return
    25      * @throws XmlPullParserException
    26      * @throws IOException
    27      */
    28     public static String getBlogContent(InputStream inputStream, String encode) throws XmlPullParserException, IOException {
    29 
    30         String info="";
    31         //获取XmlPullParser实例
    32         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    33         XmlPullParser parser = factory.newPullParser();
    34         parser.setInput(inputStream, encode);
    35         //获取解析事件
    36         int eventType = parser.getEventType();
    37         //当xml文档未到尾端时
    38         while (eventType != XmlPullParser.END_DOCUMENT) {
    39             switch (eventType) {
    40                 //解析根标签的时候,实例化集合
    41                 case XmlPullParser.START_DOCUMENT:
    42                     break;
    43                 case XmlPullParser.START_TAG:
    44                     if("string".equals(parser.getName())){
    45                         parser.next();
    46                         info=parser.getText();
    47                     }
    48                     break;
    49             }
    50             eventType = parser.next();
    51         }
    52         return info;
    53 
    54     }
    55 
    56 }

      关于博文详情页的实现,我做了很多方法的尝试,因为我们获取的数据是Html代码,我们很自然的会想到用WebView,但是用WebView来展示,我们需要一个固定的样式来控制页面内容,不然会导致页面格式无法控制,比如文字的排布换行,图片的大小控制,包括整体页面的屏幕适配,由于我们获取的只是部分Html代码,所以会纯在很多问题,并且有滑动卡顿(对于安卓来说,WebView本来就是个软肋)。

      然后我尝试着用Html类下的fromHtml方法来实现对页面代码的格式化,它是基于TextView的,发现滑动很流畅,文字大小也可以控制的很好,但又有一个问题出现了,关于图片的展示问题,虽然fromHtml提供了另外一个包含ImageGetter的构造方法,但是在我们通过异步获取图片的时候它不发预知图片尺寸的大小,导致最后获取出来的图片会覆盖了文字。

    效果如图:

      后来我尝试的去GitHub上找找开源的组件发现了HtmlTextView:https://github.com/sufficientlysecure/html-textview,但是也不能够达到想要的效果,最后无奈的用了一个最笨的方法去做,就是在我们获取到Html格式数据的时候,我们通过String的replace方法用正则表达式方式去判断位置,比如<img/>标签,然后通过添加换行符号<br/>来人工换行,这样子看似没什么问题,但是图片毕竟有很多,有大有小,也并不是很完美的可以解决问题。

      最后我觉得不应该老在安卓端里去考虑问题,毕竟我们的数据是从网络获取下来的,我们对它们具有完全的掌控权,为啥不从Html代码端去考虑问题的解决呢?然后我决定写一个静态的Html模板,对它做了移动端的屏幕适配,然后再把我们获取到的内容插入进去。(这里而外再提一个方法,我们也可以通过Jsoup去解析Html代码,然后去创建一个实体对象,把内容加入到List集合,然后再通过页面展示)

      好了,看下我的Html静态模板的实现:

    这是Html模板:

     1 <html>
     2 <head>
     3     <title>Blog Content</title>
     4     <meta name="viewport"
     5           content="width=device-width, minimum-scale=0.5, initial-scale=1.2, maximum-scale=2.0, user-scalable=1"/>
     6     <link rel="stylesheet" type="text/css" href="css.css"/>
     7 
     8 </head>
     9 <body>
    10 <div id="header">
    11     <h3>
    12         #title#
    13     </h3>
    14 
    15     <div class="describe"><p/>#author#<p/>
    16 
    17         <div id="info">#info#<p/>#time#</div>
    18     </div>
    19 </div>
    20 <div id="content">
    21     #content#
    22 </div>
    23 </body>
    24 </html>
    BlogContent.html

    这是Css文件:

     1 body{font-family:Helvetica,"Microsoft Yahei",Verdana,Helvetica,SimSun,Arial,"Arial Unicode MS",MingLiu,PMingLiu,"MS Gothic",sans-serief;margin:0;padding:0 8px;background-color:#efeff0;color:#333;word-wrap:break-word;}
     2 p{margin-top:0;margin-bottom:5pt;line-height: 1.6em;}  
     3 #header{text-align:center;background:transparent url('webBgLine.png') repeat-x scroll center bottom; padding-top:6pt;margin-bottom:5pt;-webkit-background-size:320px 2px;}
     4 #header h3{margin-bottom:0px; margin-top:5px;font-size:14pt;padding:0 5pt;color:#464646;line-height:1.3em;}
     5 .describe{color:#8e8e8e;font-size:12pt;padding:4pt 0; color:#333;}
     6 #info{ font-size:10pt;line-height:1.6; color:#787878;}
     7 #content{ font-size:12pt;line-height:1.8;}
     8 img{max-width:80%;height:auto;}
     9 div.bimg{text-align:center;padding:0;}
    10 .photo_title{font-weight:bold;font-size:14pt;margin-top:15px;}
    11 .langs_cn{color:#006200;}
    12 audio{width:100%}
    13 *{-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
    14     /*-webkit-text-size-adjust: none;*/ /* prevent webkit from resizing text to fit */
    15     -webkit-tap-highlight-color: rgba(0,0,0,0.15); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
    16     /*-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
    17 }
    18 @media screen and (-webkit-device-pixel-ratio: 2) {
    19     #header{background-image:transparent url('webBgLine@2x.png') repeat-x scroll center bottom;-webkit-background-size:320px 1px;}
    20 }
    Css.css

      这样我们就可以通过Java端实现动态插入了,由于是加载了静态模板,滑动起来也不会出现卡顿。

    1 InputStream inputStream = getAssets().open("NewsDetail.html");
    2                     byte[] temp = AppUtil.readInputStream(inputStream);
    3                     String content = new String(temp);
    4                     mWebView.loadDataWithBaseURL("file:///android_asset/", content.replace("#title#", mBlog.getBlogTitle()).replace("#author#", "作者:" + mBlog.getAuthorName()).replace("#info#", "推荐:" + mBlog.getBlogDiggs() + "		评论:" + mBlog.getBlogComments() + "		浏览:" + mBlog.getBlogViews()).replace("#time#", TimeUtil.ParseDateToString(TimeUtil.ParseUTCDate(mBlog.getBlogPublished())))
    5                             .replace("#content#", mInfo), "text/html", "utf-8", null);

      看下布局文件,这里用到了一个Material Design里的FAB(Floating Action Button)浮动按钮,这个控件很简单,只需要设置对应的属性,然后其他用法和普通控件是保持一致的。

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:app="http://schemas.android.com/apk/res-auto"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:fitsSystemWindows="true"
     6     android:orientation="vertical">
     7 
     8     <!--ToolBar-->
     9     <include layout="@layout/activity_toolbar" />
    10 
    11 
    12     <FrameLayout
    13         android:layout_width="match_parent"
    14         android:layout_height="match_parent">
    15 
    16         <WebView
    17             android:id="@+id/wv_blog_content"
    18             android:layout_width="match_parent"
    19             android:layout_height="match_parent"
    20             android:scrollbars="vertical"
    21             />
    22 
    23         <ProgressBar
    24             android:id="@+id/pb_bar"
    25             style="@style/MyProgressBar"
    26             android:layout_width="match_parent"
    27             android:layout_height="wrap_content"
    28             android:layout_gravity="center_vertical"
    29             android:indeterminate="true" />
    30 
    31         <android.support.design.widget.FloatingActionButton
    32             android:id="@+id/fab_comment"
    33             android:layout_width="wrap_content"
    34             android:layout_height="wrap_content"
    35             android:layout_gravity="right|bottom"
    36             android:layout_marginBottom="30dp"
    37             android:layout_marginRight="20dp"
    38             android:src="@mipmap/ic_star_outline_white_24dp"
    39             app:backgroundTint="@color/md_green_600"
    40             app:borderWidth="0dp"
    41             app:elevation="8dp"
    42             app:fabSize="normal"
    43             app:pressedTranslationZ="12dp"
    44             app:rippleColor="@color/md_green_800" />
    45     </FrameLayout>
    46 </LinearLayout>
    BlogContent.xml

      这里只是先简单的一个图片替换和Toast的显示,具体操作等下篇文章我们用到数据库的时候再说。

     1         mFloatingActionButton = (FloatingActionButton) findViewById(R.id.fab_comment);
     2 
     3         mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
     4             @Override
     5             public void onClick(View v) {
     6                 if (!isLike) {
     7                     isLike = true;
     8                     mFloatingActionButton.setImageResource(R.mipmap.ic_grade_white_24dp);
     9                     Toast.makeText(BlogContentActivity.this, "文章已收藏", Toast.LENGTH_SHORT).show();
    10                     //添加收藏文章到数据库
    11                 } else {
    12                     isLike = false;
    13                     mFloatingActionButton.setImageResource(R.mipmap.ic_star_outline_white_24dp);
    14                     Toast.makeText(BlogContentActivity.this, "取消文章收藏", Toast.LENGTH_SHORT).show();
    15                     //从数据库删除收藏文章
    16                 }
    17             }
    18         });

      然后这里是关于WebView的设置,大家也可以参考这篇文章自己做设置:http://www.pedant.cn/2014/09/10/webview-optimize-points/#0-tsina-1-5518-397232819ff9a47a7b7e80a40613cfe1

     1 mWebView = (WebView) findViewById(R.id.wv_blog_content);
     2         //避免中文乱码
     3         mWebView.getSettings().setDefaultTextEncodingName("utf-8");
     4         //适配低版本,关闭图片自动加载
     5         if (Build.VERSION.SDK_INT >= 19) {
     6             mWebView.getSettings().setLoadsImagesAutomatically(true);
     7         } else {
     8             mWebView.getSettings().setLoadsImagesAutomatically(false);
     9         }
    10         //等页面加载完毕再加载图片
    11         WebViewClient webViewClient = new WebViewClient() {
    12             @Override
    13             public void onPageFinished(WebView view, String url) {
    14                 //关闭Progress
    15                 mProgressBar.setVisibility(View.GONE);
    16                 if (!view.getSettings().getLoadsImagesAutomatically()) {
    17                     view.getSettings().setLoadsImagesAutomatically(true);
    18                 }
    19             }
    20         };
    21         mWebView.setWebViewClient(webViewClient);
    22         mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
    23         mWebView.getSettings().setJavaScriptEnabled(false);
    24         mWebView.getSettings().setSupportZoom(false);
    25         mWebView.getSettings().setBuiltInZoomControls(false);
    26         mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
    27         mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);

    3、关于评论详情页面的实现

      这里是关于博文详情的接口:http://wcf.open.cnblogs.com/blog/post/{POSTID}/comments/{PAGEINDEX}/{PAGESIZE} (POSTID代表文章Id,PAGEINDEX代表评论页数,PAGESIZE代表评论每页展示的条数)这里不得不吐槽下接口的残疾,都没提供用户头像数据,只能用默认灰白头像做了。

      这个就很简单了,直接可以复制我们之前做首页列表的XML布局文件甚至是Fragment里的主逻辑代码,只需要做一下页面内容的小改动就可以了,下面直接给代码。

    博文评论主页样式:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:app="http://schemas.android.com/apk/res-auto"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:fitsSystemWindows="true"
     6     android:gravity="center"
     7     android:orientation="vertical">
     8 
     9     <!--ToolBar-->
    10     <include layout="@layout/activity_toolbar" />
    11     <android.support.v4.widget.SwipeRefreshLayout
    12         android:id="@+id/swipe_refresh"
    13         android:layout_width="match_parent"
    14         android:layout_height="match_parent"
    15         android:layout_margin="4dp">
    16 
    17         <android.support.v7.widget.RecyclerView
    18             android:id="@+id/rv_view"
    19             android:layout_width="match_parent"
    20             android:layout_height="match_parent"
    21             android:background="@color/md_grey_200"
    22             android:scrollbars="vertical"
    23             />
    24     </android.support.v4.widget.SwipeRefreshLayout>
    25 
    26     <com.lcw.rabbit.myblog.view.MyProgressBar
    27         android:id="@+id/progressbar"
    28         android:layout_width="match_parent"
    29         android:layout_height="20dp"
    30         android:layout_gravity="bottom"
    31         android:visibility="gone"
    32         />
    33 
    34 
    35 
    36 </LinearLayout>
    activity_blog_comment.xml

    列表样式和详细内容样式:

     1 <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:app="http://schemas.android.com/apk/res-auto"
     3     android:id="@+id/cv_cardview"
     4     android:layout_width="match_parent"
     5     android:layout_height="wrap_content"
     6     android:layout_margin="8dp"
     7     android:gravity="center"
     8     app:cardCornerRadius="6dp">
     9 
    10     <include layout="@layout/recyclerview_item_blogcommentlist_content" />
    11 
    12 </android.support.v7.widget.CardView>
    recyclerview_blogcommentlist.xml
     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:app="http://schemas.android.com/apk/res-auto"
     3     android:layout_width="match_parent"
     4     android:layout_height="wrap_content"
     5     android:background="?android:selectableItemBackground"
     6     android:orientation="horizontal"
     7     android:padding="3dp">
     8 
     9     <!--信息内容-->
    10     <LinearLayout
    11         android:layout_width="0dp"
    12         android:layout_height="wrap_content"
    13         android:layout_marginLeft="3dp"
    14         android:layout_weight="1"
    15         android:orientation="vertical">
    16 
    17         <LinearLayout
    18             android:layout_width="wrap_content"
    19             android:layout_height="wrap_content"
    20             android:layout_marginLeft="3dp"
    21             android:layout_weight="1"
    22             android:padding="3dp"
    23             android:orientation="horizontal">
    24 
    25             <!--头像-->
    26             <com.makeramen.roundedimageview.RoundedImageView
    27                 android:id="@+id/iv_userhead"
    28                 android:layout_width="25dp"
    29                 android:layout_height="25dp"
    30                 android:layout_gravity="center_vertical"
    31                 android:layout_marginRight="5dp"
    32                 android:src="@mipmap/avatar_default"
    33                 app:riv_border_color="#ffffff"
    34                 app:riv_border_width="2dip"
    35                 app:riv_corner_radius="30dip"
    36                 app:riv_mutate_background="true"
    37                 app:riv_oval="true"
    38                 app:riv_tile_mode="repeat" />
    39 
    40             <TextView
    41                 android:id="@+id/tv_name"
    42                 android:layout_width="match_parent"
    43                 android:layout_height="wrap_content"
    44                 android:layout_gravity="center_vertical"
    45                 android:layout_marginTop="2dp"
    46                 android:layout_weight="1"
    47                 android:ellipsize="end"
    48                 android:singleLine="true"
    49 
    50                 android:text="用户昵称"
    51                 android:textColor="@color/md_green_700"
    52                 android:textSize="16sp"
    53                 android:textStyle="bold" />
    54         </LinearLayout>
    55 
    56         <TextView
    57             android:id="@+id/tv_comment"
    58             android:layout_width="match_parent"
    59             android:layout_height="wrap_content"
    60             android:layout_weight="1"
    61             android:maxLines="3"
    62             android:layout_marginLeft="3dp"
    63             android:text="文章评论内容"
    64             android:textSize="16sp" />
    65 
    66         <LinearLayout
    67             android:layout_width="match_parent"
    68             android:layout_height="match_parent"
    69             android:layout_margin="1dp"
    70             android:layout_weight="1"
    71             android:padding="3dp"
    72             android:orientation="horizontal">
    73 
    74             <TextView
    75                 android:layout_width="wrap_content"
    76                 android:layout_height="match_parent"
    77                 android:gravity="center_vertical"
    78                 android:text="评论时间:"
    79                 android:textColor="@color/md_grey_500"
    80                 android:textSize="12sp" />
    81 
    82             <TextView
    83                 android:id="@+id/tv_time"
    84                 android:layout_width="wrap_content"
    85                 android:layout_height="match_parent"
    86                 android:layout_marginRight="5dp"
    87                 android:gravity="center_vertical"
    88                 android:textColor="@color/md_grey_500"
    89                 android:textSize="12sp" />
    90 
    91         </LinearLayout>
    92 
    93     </LinearLayout>
    94 
    95 
    96 </LinearLayout>
    recyclerview_blogcommentlist_content.xml

    主代码:

      1 package com.lcw.rabbit.myblog;
      2 
      3 import android.os.Bundle;
      4 import android.support.v4.widget.SwipeRefreshLayout;
      5 import android.support.v7.app.AppCompatActivity;
      6 import android.support.v7.widget.DefaultItemAnimator;
      7 import android.support.v7.widget.LinearLayoutManager;
      8 import android.support.v7.widget.RecyclerView;
      9 import android.support.v7.widget.Toolbar;
     10 import android.view.View;
     11 
     12 import com.android.volley.Request;
     13 import com.android.volley.Response;
     14 import com.android.volley.VolleyError;
     15 import com.android.volley.toolbox.StringRequest;
     16 import com.lcw.rabbit.myblog.adapter.BlogCommentListAdapter;
     17 import com.lcw.rabbit.myblog.entity.Comment;
     18 import com.lcw.rabbit.myblog.parser.BlogCommentXmlParser;
     19 import com.lcw.rabbit.myblog.utils.VolleyRequestQueueManager;
     20 import com.lcw.rabbit.myblog.view.MyProgressBar;
     21 import com.mugen.Mugen;
     22 import com.mugen.MugenCallbacks;
     23 import com.mugen.attachers.BaseAttacher;
     24 
     25 import org.xmlpull.v1.XmlPullParserException;
     26 
     27 import java.io.ByteArrayInputStream;
     28 import java.io.IOException;
     29 import java.util.ArrayList;
     30 import java.util.List;
     31 
     32 /**
     33  * 博文评论页
     34  */
     35 public class BlogCommentActivity extends AppCompatActivity {
     36     private Toolbar mToolbar;
     37     private SwipeRefreshLayout mSwipeRefreshLayout;
     38     private RecyclerView mRecyclerView;
     39     private BlogCommentListAdapter mBlogCommentListAdapter;
     40     //无限滚动
     41     private BaseAttacher mBaseAttacher;
     42     private MyProgressBar myProgressBar;
     43 
     44     private String mBlogId;
     45     private List<Comment> mComments;
     46 
     47     //默认第一页
     48     private int currentPage = 1;
     49     //是否正在加载
     50     private boolean isLoading = false;
     51 
     52 
     53     @Override
     54     protected void onCreate(Bundle savedInstanceState) {
     55         super.onCreate(savedInstanceState);
     56         setContentView(R.layout.activity_blog_comment);
     57         initView();
     58         initData();
     59         initAction();
     60         getBlogComment(mBlogId, 1, 10);
     61     }
     62 
     63     private void initAction() {
     64         //设置下拉刷新
     65         mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
     66             @Override
     67             public void onRefresh() {
     68                 getBlogComment(mBlogId, 1, 10);
     69             }
     70         });
     71 
     72 
     73         //设置无限滚动,上拉加载
     74         mBaseAttacher = Mugen.with(mRecyclerView, new MugenCallbacks() {
     75             @Override
     76             public void onLoadMore() {
     77                 //加载更多
     78                 isLoading = true;
     79                 mBaseAttacher.setLoadMoreEnabled(false);
     80                 myProgressBar.setVisibility(View.VISIBLE);
     81                 getBlogComment(mBlogId, (currentPage + 1), 10);
     82             }
     83 
     84             @Override
     85             public boolean isLoading() {
     86                 return isLoading;
     87             }
     88 
     89             @Override
     90             public boolean hasLoadedAllItems() {
     91                 return isLoading;
     92             }
     93         }).start();
     94         //离底部一项的时候加载更多
     95         mBaseAttacher.setLoadMoreOffset(1);
     96     }
     97 
     98     /**
     99      * 获取Intent传递过来的数据
    100      */
    101     private void initData() {
    102         mBlogId = getIntent().getExtras().getString("blogId");
    103         //设置空数据源
    104         mComments=new ArrayList<Comment>();
    105         mBlogCommentListAdapter = new BlogCommentListAdapter(this,mComments);
    106         mRecyclerView.setAdapter(mBlogCommentListAdapter);
    107     }
    108 
    109 
    110     /**
    111      * 获取博客评论数据并设置
    112      * @param blogId
    113      * @param page
    114      * @param num
    115      */
    116     private void getBlogComment(String blogId, final int page, int num) {
    117         //更新当前页数
    118         currentPage = page;
    119         StringRequest request = new StringRequest(Request.Method.GET, "http://wcf.open.cnblogs.com/blog/post/" + blogId + "/comments/" + page + "/" + num, new Response.Listener<String>() {
    120             @Override
    121             public void onResponse(String s) {
    122                 try {
    123                     //获取评论数据数据并解析XML
    124                     ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes());
    125                     List<Comment> comments = BlogCommentXmlParser.getBlogComment(inputStream, "utf-8");
    126                     if (page == 1) {
    127                         //清空之前的数据预防重复加载
    128                         mComments.clear();
    129                     }
    130                     for (Comment comment : comments) {
    131                         //整理数据源
    132                         mComments.add(comment);
    133                     }
    134 
    135                     if (mBlogCommentListAdapter == null) {
    136                         //如果Adapter不存在
    137                         mBlogCommentListAdapter = new BlogCommentListAdapter(BlogCommentActivity.this,mComments);
    138                         mRecyclerView.setAdapter(mBlogCommentListAdapter);
    139                     } else {
    140                         //存在通知adatper数据源更新
    141                         mBlogCommentListAdapter.refreshData(mComments);
    142 
    143                     }
    144                     //关闭下拉刷新样式
    145                     mSwipeRefreshLayout.setRefreshing(false);
    146                     isLoading = false;
    147                     myProgressBar.setVisibility(View.GONE);
    148                     mBaseAttacher.setLoadMoreEnabled(true);
    149 
    150 
    151                 } catch (XmlPullParserException e) {
    152                     e.printStackTrace();
    153                 } catch (IOException e) {
    154                     e.printStackTrace();
    155                 }
    156             }
    157         }, new Response.ErrorListener() {
    158             @Override
    159             public void onErrorResponse(VolleyError volleyError) {
    160 
    161             }
    162         });
    163         VolleyRequestQueueManager.addRequest(request, "BlogCommentList");
    164 
    165     }
    166 
    167     private void initView() {
    168         mToolbar = (Toolbar) findViewById(R.id.activity_toolbar);
    169         mToolbar.setTitle("文章评论");
    170         setSupportActionBar(mToolbar);
    171         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    172 
    173         mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
    174         //设置拉下刷新滚动条颜色
    175         mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);
    176         mRecyclerView = (RecyclerView) findViewById(R.id.rv_view);
    177         mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    178         mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    179         myProgressBar = (MyProgressBar)findViewById(R.id.progressbar);
    180 
    181 
    182     }
    183 
    184 
    185 }

    适配器代码:

      1 package com.lcw.rabbit.myblog.adapter;
      2 
      3 import android.content.Context;
      4 import android.content.res.Resources;
      5 import android.graphics.Bitmap;
      6 import android.graphics.BitmapFactory;
      7 import android.support.v7.widget.RecyclerView;
      8 import android.text.Html;
      9 import android.view.LayoutInflater;
     10 import android.view.View;
     11 import android.view.ViewGroup;
     12 import android.widget.TextView;
     13 
     14 import com.lcw.rabbit.myblog.R;
     15 import com.lcw.rabbit.myblog.entity.Comment;
     16 import com.lcw.rabbit.myblog.utils.TimeUtil;
     17 import com.makeramen.roundedimageview.RoundedImageView;
     18 
     19 import java.util.List;
     20 
     21 /**
     22  * 推荐博文评论列表适配器
     23  * Created by Lichenwei
     24  * Date: 2015-08-16
     25  * Time: 22:34
     26  */
     27 public class BlogCommentListAdapter extends RecyclerView.Adapter<BlogCommentListAdapter.RecyclerViewViewHolder> {
     28 
     29     private Context mContext;
     30     private List<Comment> mComments;
     31 
     32     public BlogCommentListAdapter(Context context, List<Comment> comments) {
     33         this.mContext = context;
     34         this.mComments = comments;
     35     }
     36 
     37     /**
     38      * 设置新的数据源,提醒adatper更新
     39      *
     40      * @param comments
     41      */
     42     public void refreshData(List<Comment> comments) {
     43         this.mComments = comments;
     44         this.notifyDataSetChanged();
     45     }
     46 
     47     /**
     48      * 创建ViewHolder
     49      *
     50      * @param viewGroup
     51      * @param i
     52      * @return
     53      */
     54     @Override
     55     public RecyclerViewViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
     56         View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerview_item_blogcommentlist, viewGroup, false);
     57         return new RecyclerViewViewHolder(view);
     58     }
     59 
     60     /**
     61      * 根据资源ID返回Bitmap对象
     62      *
     63      * @param resId
     64      * @return
     65      */
     66     public Bitmap getBitmapFromRes(int resId) {
     67         Resources res = mContext.getResources();
     68         return BitmapFactory.decodeResource(res, resId);
     69 
     70     }
     71 
     72     /**
     73      * 绑定数据
     74      *
     75      * @param viewholder
     76      * @param i
     77      */
     78     @Override
     79     public void onBindViewHolder(RecyclerViewViewHolder viewholder, int i) {
     80         //设置头像
     81 //        if (mAuthors.get(i).getAuthorPic() != null && !"".equals(mAuthors.get(i).getAuthorPic())) {
     82 //            ImageCacheManager.loadImage(mAuthors.get(i).getAuthorPic(), viewholder.mUserhead, getBitmapFromRes(R.mipmap.avatar_default), getBitmapFromRes(R.mipmap.avatar_default));
     83 //        } else {
     84 //            viewholder.mUserhead.setImageResource(R.mipmap.avatar_default);
     85 //        }
     86         viewholder.mName.setText(mComments.get(i).getAuthorName());
     87         //处理评论内容里的Html代码
     88         viewholder.mContent.setText(Html.fromHtml(mComments.get(i).getCommentContent()));
     89         //处理日期特殊格式
     90         viewholder.mTime.setText(TimeUtil.DateToChineseString(TimeUtil.ParseUTCDate(mComments.get(i).getCommentTime())));
     91     }
     92 
     93     @Override
     94     public int getItemCount() {
     95         return mComments.size();
     96     }
     97 
     98     /**
     99      * 自定义ViewHolder
    100      */
    101     public static class RecyclerViewViewHolder extends RecyclerView.ViewHolder {
    102         private RoundedImageView mUserhead;
    103         private TextView mName;
    104         private TextView mContent;
    105         private TextView mTime;
    106 
    107         public RecyclerViewViewHolder(View view) {
    108             super(view);
    109             mUserhead = (RoundedImageView) view.findViewById(R.id.iv_userhead);
    110             mName = (TextView) view.findViewById(R.id.tv_name);
    111             mContent = (TextView) view.findViewById(R.id.tv_comment);
    112             mTime = (TextView) view.findViewById(R.id.tv_time);
    113 
    114         }
    115 
    116 
    117     }
    118 }

    获取博文详情XML解析:

     1 package com.lcw.rabbit.myblog.parser;
     2 
     3 import com.lcw.rabbit.myblog.entity.Comment;
     4 
     5 import org.xmlpull.v1.XmlPullParser;
     6 import org.xmlpull.v1.XmlPullParserException;
     7 import org.xmlpull.v1.XmlPullParserFactory;
     8 
     9 import java.io.IOException;
    10 import java.io.InputStream;
    11 import java.util.ArrayList;
    12 import java.util.List;
    13 
    14 /**
    15  * 对博文评论页xml数据的解析
    16  * Created by Lichenwei
    17  * Date: 2015-08-17
    18  * Time: 13:32
    19  */
    20 public class BlogCommentXmlParser {
    21 
    22 
    23     /**
    24      * 用于解析博文评论详情的xml,返回Avatar的List集合对象
    25      *
    26      * @param inputStream
    27      * @param encode
    28      * @return
    29      * @throws XmlPullParserException
    30      * @throws IOException
    31      */
    32     public static List<Comment> getBlogComment(InputStream inputStream, String encode) throws XmlPullParserException, IOException {
    33         List<Comment> mComments = null;
    34         Comment mComment = null;
    35 
    36         //获取XmlPullParser实例
    37         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    38         XmlPullParser parser = factory.newPullParser();
    39         parser.setInput(inputStream, encode);
    40         //获取解析事件
    41         int eventType = parser.getEventType();
    42         //当xml文档未到尾端时
    43         while (eventType != XmlPullParser.END_DOCUMENT) {
    44             switch (eventType) {
    45                 //解析根标签的时候,实例化集合
    46                 case XmlPullParser.START_DOCUMENT:
    47                     mComments = new ArrayList<Comment>();
    48                     mComment = new Comment();
    49                     break;
    50                 case XmlPullParser.START_TAG:
    51                     //当解析到entry标签的时候,实例化Blog对象
    52                     if ("entry".equals(parser.getName())) {
    53                         mComment = new Comment();
    54                     }
    55                     if ("id".equals(parser.getName())) {
    56                         parser.next();
    57                         mComment.setCommentId(parser.getText());
    58                     } else if ("published".equals(parser.getName())) {
    59                         parser.next();
    60                         mComment.setCommentTime(parser.getText());
    61                     } else if ("name".equals(parser.getName())) {
    62                         parser.next();
    63                         mComment.setAuthorName(parser.getText());
    64                     } else if ("uri".equals(parser.getName())) {
    65                         parser.next();
    66                         mComment.setAuthorUrl(parser.getText());
    67                     } else if ("content".equals(parser.getName())) {
    68                         parser.next();
    69                         mComment.setCommentContent(parser.getText());
    70                     }
    71                     break;
    72                 case XmlPullParser.END_TAG:
    73                     //当解析到entry标签结束的时候添加入Blogs集合,清空Blog对象
    74                     if ("entry".equals(parser.getName())) {
    75                         mComments.add(mComment);
    76                         mComment = null;
    77                     }
    78                     break;
    79 
    80             }
    81             //手动跳转第一次遍历
    82             eventType = parser.next();
    83         }
    84 
    85         return mComments;
    86 
    87     }
    88 }

    好了,今天先写到这里,改天继续更新,有什么建议或疑问,可以在文章评论给我留言。

    接下篇:《安卓开发笔记——打造属于自己的博客园APP(四)

    作者:李晨玮
    出处:http://www.cnblogs.com/lichenwei/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
    正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!

  • 相关阅读:
    java—数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result[i] = input数组中,除了input[i] 之外的所有数的乘积,不用考虑溢出例如 input {2, 3, 4, 5} output: {60, 40, 30, 24}
    深入理解按位异或运算符
    针对数组的三中排序方式:冒泡排序,选择排序,插入排序
    笔试题目整理
    Android中AIDL通信机制分析
    android消息处理机制之2handler与looper,MessageQueue:的关系
    Android消息处理机制(Handler 与Message)---01
    vim 批处理
    React 组件条件渲染的几种方式
    vim命令行模式
  • 原文地址:https://www.cnblogs.com/lichenwei/p/4754444.html
Copyright © 2011-2022 走看看