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

  在上篇文章《安卓开发笔记——打造属于自己的博客园APP(三)》中,我们对博客文章的详情页和评论页进行了实现,慢慢的一个APP已经出现雏形了,当然这只是完成了"表面效果",要真正做好一个APP并不是一件很轻松的事情,有很多细节需要我们一点一滴的去完善。

  好了,来讲下今天要完成的效果,在优化了之前部分代码的前提下,今天来说下关于博客搜索和博客详情页的实现,依旧国际惯例,来看下效果图:(动态图片比较大,加载需要点时间)

  效果比较简单,很多东西我们还是可以复用之前的代码,毕竟这种列表长得都差不多,然后大致功能基本完成了,从下篇文章开始可以引入我们的数据库了,开始实现缓存操作。

1、关于搜索页面的实现:

  很简单,分成两部分,上面一个EditText,下面一个RecyclerView,当我们刚进入页面的时候默认展示博客园给我们的推荐用户,当搜索的时候更新列表数据。

  这里是关于博客园推荐用户的接口: http://wcf.open.cnblogs.com/blog/bloggers/recommend/{PAGEINDEX}/{PAGESIZE} {PAGEINDEX}代表页码,{PAGESIZE}代表每页展示的条数

这里是搜索页面的主布局文件:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical">
 6
 7
 8     <!--ToolBar-->
 9     <include layout="@layout/activity_toolbar" />
10
11     <LinearLayout
12         android:layout_width="match_parent"
13         android:layout_height="wrap_content"
14         android:background="@color/md_green_700"
15         android:paddingBottom="10dp"
16         android:paddingLeft="20dp"
17         android:paddingRight="20dp"
18         android:paddingTop="10dp">
19
20         <RelativeLayout
21             android:layout_width="match_parent"
22             android:layout_height="40dp"
23             android:background="@drawable/bg_search"
24             android:gravity="center_vertical">
25
26             <ImageButton
27                 android:id="@+id/ib_search"
28                 android:layout_width="wrap_content"
29                 android:layout_height="wrap_content"
30                 android:layout_alignParentRight="true"
31                 android:background="@drawable/bt_search_selector" />
32
33             <EditText
34                 android:id="@+id/et_text"
35                 android:layout_width="match_parent"
36                 android:layout_height="wrap_content"
37                 android:layout_toLeftOf="@id/ib_search"
38                 android:background="@null"
39                 android:hint="搜索其他博客" />
40         </RelativeLayout>
41     </LinearLayout>
42
43     <FrameLayout
44         android:layout_width="match_parent"
45         android:layout_height="match_parent">
46
47         <android.support.v7.widget.RecyclerView
48             android:id="@+id/rv_view"
49             android:layout_width="match_parent"
50             android:layout_height="match_parent"
51             android:background="@color/md_grey_200"
52             android:scrollbars="none" />
53
54         <com.lcw.rabbit.myblog.view.MyProgressBar
55             android:id="@+id/progressbar"
56             android:layout_width="match_parent"
57             android:layout_height="20dp"
58             android:layout_gravity="bottom"
59             android:visibility="gone" />
60     </FrameLayout>
61 </LinearLayout>

activity_search.xml

这里是下面RecyclerView列表的Item布局:

 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:layout_width="match_parent"
 4     android:layout_height="wrap_content"
 5     android:layout_margin="8dp"
 6     android:gravity="center"
 7     app:cardCornerRadius="6dp">
 8
 9     <include layout="@layout/recyclerview_item_authorlist_content" />
10
11 </android.support.v7.widget.CardView>

recyclerview_item_authorlist.xml

这里是Item布局的详细:

  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     <com.makeramen.roundedimageview.RoundedImageView
 10         android:id="@+id/iv_userhead"
 11         android:layout_width="60dp"
 12         android:layout_height="60dp"
 13         android:layout_gravity="center_vertical"
 14         android:layout_marginRight="5dp"
 15         android:src="@mipmap/avatar_default"
 16         app:riv_border_color="#ffffff"
 17         app:riv_border_width="2dip"
 18         app:riv_corner_radius="30dip"
 19         app:riv_mutate_background="true"
 20         app:riv_oval="true"
 21         app:riv_tile_mode="repeat" />
 22     <!--信息内容-->
 23     <LinearLayout
 24         android:layout_width="0dp"
 25         android:layout_height="wrap_content"
 26         android:layout_marginLeft="3dp"
 27         android:layout_weight="1"
 28         android:orientation="vertical">
 29
 30         <TextView
 31             android:id="@+id/tv_name"
 32             android:layout_width="match_parent"
 33             android:layout_height="wrap_content"
 34             android:layout_marginTop="2dp"
 35             android:layout_weight="1"
 36             android:ellipsize="end"
 37             android:singleLine="true"
 38             android:text="测试标题"
 39             android:textColor="@color/md_grey_900"
 40             android:textSize="16sp"
 41             android:textStyle="bold" />
 42
 43         <TextView
 44             android:id="@+id/tv_url"
 45             android:layout_width="match_parent"
 46             android:layout_height="wrap_content"
 47             android:layout_weight="1"
 48             android:maxLines="3"
 49             android:text="浏览器类型判断方法有两种:根据浏览器特性来判断根据来检测具体使用哪种方法要看具体需求的场景场景一:为了让用户有较流畅完整的体验,在站点提示用户使用或者,这种场景对浏览器类型的判断并非特别严格,可以使用检测的方法。(因为很多浏览器厂商会篡改标识)。场景二...."
 50             android:textSize="14sp" />
 51
 52         <LinearLayout
 53             android:layout_width="match_parent"
 54             android:layout_height="match_parent"
 55             android:layout_margin="1dp"
 56             android:layout_weight="1"
 57             android:orientation="horizontal">
 58
 59             <TextView
 60                 android:layout_width="wrap_content"
 61                 android:layout_height="match_parent"
 62                 android:gravity="center_vertical"
 63                 android:text="博文数:"
 64                 android:textColor="@color/md_grey_500"
 65                 android:textSize="12sp" />
 66
 67             <TextView
 68                 android:id="@+id/tv_sum"
 69                 android:layout_width="wrap_content"
 70                 android:layout_height="match_parent"
 71                 android:layout_marginRight="5dp"
 72                 android:gravity="center_vertical"
 73                 android:textColor="@color/md_grey_500"
 74                 android:textSize="12sp" />
 75
 76             <TextView
 77                 android:layout_width="wrap_content"
 78                 android:layout_height="match_parent"
 79                 android:gravity="center_vertical"
 80                 android:text="最后更新:"
 81                 android:textColor="@color/md_grey_500"
 82                 android:textSize="12sp" />
 83
 84             <TextView
 85                 android:id="@+id/tv_time"
 86                 android:layout_width="wrap_content"
 87                 android:layout_height="match_parent"
 88                 android:layout_marginRight="5dp"
 89                 android:textColor="@color/md_grey_500"
 90                 android:textSize="12sp" />
 91
 92         </LinearLayout>
 93
 94     </LinearLayout>
 95
 96     <!--操作按钮-->
 97     <ImageButton
 98         android:id="@+id/ib_more"
 99         android:layout_width="25dp"
100         android:layout_height="match_parent"
101         android:background="?android:selectableItemBackground"
102         android:gravity="center_horizontal"
103         android:src="@mipmap/triangle" />
104 </LinearLayout>

recyclerview_item_authorlist_content.xml

关于博客园推荐博客的接口数据XML解析,和我们之前在做博文列表数据的解析都是一样的,只不过是XML的节点不同,这里贴出解析代码:

  1 package com.lcw.rabbit.myblog.parser;
  2
  3 import com.lcw.rabbit.myblog.entity.Author;
  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 AuthorListXmlParser {
 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<Author> getListAuthor(InputStream inputStream, String encode) throws XmlPullParserException, IOException {
 33
 34         List<Author> mAuthors = null;
 35         Author mAuthor = null;
 36
 37         //获取XmlPullParser实例
 38         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
 39         XmlPullParser parser = factory.newPullParser();
 40         parser.setInput(inputStream, encode);
 41         //获取解析事件
 42         int eventType = parser.getEventType();
 43         //当xml文档未到尾端时
 44         while (eventType != XmlPullParser.END_DOCUMENT) {
 45             switch (eventType) {
 46                 //解析根标签的时候,实例化集合
 47                 case XmlPullParser.START_DOCUMENT:
 48                     mAuthors = new ArrayList<Author>();
 49                     mAuthor = new Author();
 50
 51                     break;
 52                 case XmlPullParser.START_TAG:
 53                     //当解析到entry标签的时候,实例化Avatar对象
 54                     if ("entry".equals(parser.getName())) {
 55                         mAuthor = new Author();
 56                     }
 57                     if ("id".equals(parser.getName())) {
 58                         parser.next();
 59                         mAuthor.setAuthorUrl(parser.getText());
 60                     } else if ("title".equals(parser.getName())) {
 61                         parser.next();
 62                         if (parser.getText().indexOf("博客园") == -1) {
 63                             mAuthor.setAuthorName(parser.getText());
 64                         }
 65                     } else if ("avatar".equals(parser.getName())) {
 66                         parser.next();
 67                         mAuthor.setAuthorPic(parser.getText());
 68                     } else if ("blogapp".equals(parser.getName())) {
 69                         parser.next();
 70                         mAuthor.setBlogApp(parser.getText());
 71                     } else if ("postcount".equals(parser.getName())) {
 72                         parser.next();
 73                         mAuthor.setBlogCount(parser.getText());
 74                     } else if ("updated".equals(parser.getName())) {
 75                         parser.next();
 76                         //区分日期格式
 77                         if (parser.getText().indexOf("+") != -1) {
 78                             mAuthor.setUpdated(parser.getText());
 79                         }
 80
 81                     }
 82                     break;
 83                 case XmlPullParser.END_TAG:
 84                     //当解析到entry标签结束的时候添加入Avatar集合,清空Avatar对象
 85                     if ("entry".equals(parser.getName())) {
 86                         mAuthors.add(mAuthor);
 87                         mAuthor = null;
 88                     }
 89                     break;
 90
 91             }
 92             //手动跳转第一次遍历
 93             eventType = parser.next();
 94         }
 95
 96
 97         return mAuthors;
 98
 99     }
100
101 }

  这里是搜索主页面代码,当我们点击搜索按钮的时候,获取输入框的博客关键字,进行搜索,将得到的数据更新数据源重新展示在RecyclerView。

  关于搜索博客关键字的接口:http://wcf.open.cnblogs.com/blog/bloggers/search?t={TERM} {TERM}代表作者名(多关键字匹配),搜索到的XML数据信息刚好又和我们前面的推荐博客内容格式一致,所以我们可以复用之前的解析工具类,然后一样具备上拉刷新和下拉加载功能:

  1 package com.lcw.rabbit.myblog;
  2
  3 import android.content.Intent;
  4 import android.os.Bundle;
  5 import android.support.v7.app.AppCompatActivity;
  6 import android.support.v7.widget.LinearLayoutManager;
  7 import android.support.v7.widget.RecyclerView;
  8 import android.support.v7.widget.Toolbar;
  9 import android.view.View;
 10 import android.widget.EditText;
 11 import android.widget.ImageButton;
 12 import android.widget.Toast;
 13
 14 import com.afollestad.materialdialogs.MaterialDialog;
 15 import com.android.volley.Request;
 16 import com.android.volley.Response;
 17 import com.android.volley.VolleyError;
 18 import com.android.volley.toolbox.StringRequest;
 19 import com.lcw.rabbit.myblog.adapter.AuthorListAdapter;
 20 import com.lcw.rabbit.myblog.entity.Author;
 21 import com.lcw.rabbit.myblog.entity.Blog;
 22 import com.lcw.rabbit.myblog.parser.AuthorListXmlParser;
 23 import com.lcw.rabbit.myblog.utils.VolleyRequestQueueManager;
 24 import com.lcw.rabbit.myblog.view.MyProgressBar;
 25 import com.mugen.Mugen;
 26 import com.mugen.MugenCallbacks;
 27 import com.mugen.attachers.BaseAttacher;
 28
 29 import org.xmlpull.v1.XmlPullParserException;
 30
 31 import java.io.ByteArrayInputStream;
 32 import java.io.IOException;
 33 import java.util.ArrayList;
 34 import java.util.List;
 35
 36 /**
 37  * 博客搜索页
 38  */
 39 public class SearchActivity extends AppCompatActivity {
 40
 41     //界面控件
 42     private Toolbar mToolbar;
 43     private EditText mEditText;
 44     private ImageButton mImageButton;
 45     private RecyclerView mRecyclerView;
 46     private MaterialDialog materialDialog;
 47     //无限滚动
 48     private BaseAttacher mBaseAttacher;
 49     private MyProgressBar myProgressBar;
 50
 51     //数据源
 52     private AuthorListAdapter mAuthorListAdapter;
 53     private List<Author> mAuthors;
 54
 55     //标志变量
 56     private boolean isLoading = false;
 57     private int currentPage = 1;
 58
 59
 60     @Override
 61     protected void onCreate(Bundle savedInstanceState) {
 62         super.onCreate(savedInstanceState);
 63         setContentView(R.layout.activity_search);
 64         initView();
 65         initData();
 66         initAction();
 67     }
 68
 69     /**
 70      * 设置控件监听
 71      */
 72     private void initAction() {
 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                 getData((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         mImageButton.setOnClickListener(new View.OnClickListener() {
 99             @Override
100             public void onClick(View v) {
101                 materialDialog = new MaterialDialog.Builder(SearchActivity.this).content("内容加载中..").progress(true, 0).show();
102                 String name = mEditText.getText().toString();
103                 getDataByName(name.trim());
104             }
105         });
106
107         //设置RecyclerView点击监听
108         mAuthorListAdapter.setRecyclerViewListener(new AuthorListAdapter.RecyclerViewListener() {
109             @Override
110             public void setOnclickListener(View view, String value) {
111                 //封装Bolg对象传递
112                 Blog blog = new Blog();
113                 blog.setBlogApp(mAuthors.get(Integer.parseInt(value)).getBlogApp());
114                 blog.setAuthorName(mAuthors.get(Integer.parseInt(value)).getAuthorName());
115                 Intent intent = new Intent();
116                 intent.setClass(SearchActivity.this, AuthorActivity.class);
117                 Bundle bundle = new Bundle();
118                 bundle.putSerializable("blog", blog);
119                 intent.putExtras(bundle);
120                 startActivity(intent);
121             }
122         });
123     }
124
125     /**
126      * 初始化控件
127      */
128     private void initView() {
129         //ToolBar
130         mToolbar = (Toolbar) findViewById(R.id.activity_toolbar);
131         setSupportActionBar(mToolbar);
132         //需要放在setSupportActionBar后设置
133         mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
134             @Override
135             public void onClick(View v) {
136                 finish();
137             }
138         });
139         getSupportActionBar().setTitle("博客搜索");
140         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
141
142         //输入搜索
143         mEditText = (EditText) findViewById(R.id.et_text);
144         mImageButton = (ImageButton) findViewById(R.id.ib_search);
145         //列表
146         mRecyclerView = (RecyclerView) findViewById(R.id.rv_view);
147         mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
148         //当高度确定,提高效率
149         mRecyclerView.setHasFixedSize(true);
150         //友好提醒
151         myProgressBar = (MyProgressBar) findViewById(R.id.progressbar);
152
153     }
154
155     /**
156      * 初始化数据
157      */
158     private void initData() {
159         //显示下拉刷新样式
160         mAuthors = new ArrayList<Author>();
161         //设置空数据
162         mAuthorListAdapter = new AuthorListAdapter(this, mAuthors);
163         mRecyclerView.setAdapter(mAuthorListAdapter);
164         getData(1, 10);
165     }
166
167     /**
168      * 根据博主昵称搜索博客
169      *
170      * @param name
171      */
172     public void getDataByName(String name) {
173         String url = "http://wcf.open.cnblogs.com/blog/bloggers/search?t=" + name;
174         StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
175             @Override
176             public void onResponse(String s) {
177                 try {
178                     ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes());
179                     List<Author> authors = AuthorListXmlParser.getListAuthor(inputStream, "utf-8");
180                     if (authors.size() != 0) {
181                         //清空之前的数据预防重复加载
182                         mAuthors.clear();
183                         for (Author author : authors) {
184                             mAuthors.add(author);
185                         }
186
187                         if (mAuthorListAdapter == null) {
188                             mAuthorListAdapter = new AuthorListAdapter(SearchActivity.this, mAuthors);
189                             mRecyclerView.setAdapter(mAuthorListAdapter);
190                         } else {
191                             //通知adatper数据源更新
192                             mAuthorListAdapter.refreshData(mAuthors);
193                         }
194
195                         materialDialog.dismiss();
196                     } else {
197                         //如果匹配不到关键字
198                         Toast.makeText(SearchActivity.this, "找不到相关用户,请重新搜索", Toast.LENGTH_SHORT).show();
199                         materialDialog.dismiss();
200                     }
201                 } catch (XmlPullParserException e) {
202                     e.printStackTrace();
203                 } catch (IOException e) {
204                     e.printStackTrace();
205                 }
206             }
207         }, new Response.ErrorListener() {
208             @Override
209             public void onErrorResponse(VolleyError volleyError) {
210                 Toast.makeText(SearchActivity.this, volleyError.getMessage(), Toast.LENGTH_SHORT).show();
211             }
212         });
213
214         VolleyRequestQueueManager.addRequest(request, "getAuthorList");
215
216     }
217
218
219     /**
220      * 获取推荐数据
221      *
222      * @param page
223      * @param num
224      */
225     public void getData(final int page, int num) {
226         this.currentPage = page;
227         String url = "http://wcf.open.cnblogs.com/blog/bloggers/recommend/" + page + "/" + num;
228         StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
229             @Override
230             public void onResponse(String s) {
231                 try {
232                     ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes());
233                     List<Author> authors = AuthorListXmlParser.getListAuthor(inputStream, "utf-8");
234                     if (page == 1) {
235                         //清空之前的数据预防重复加载
236                         mAuthors.clear();
237                     }
238                     for (Author author : authors) {
239                         mAuthors.add(author);
240                     }
241
242                     if (mAuthorListAdapter == null) {
243                         mAuthorListAdapter = new AuthorListAdapter(SearchActivity.this, mAuthors);
244                         mRecyclerView.setAdapter(mAuthorListAdapter);
245                     } else {
246                         //通知adatper数据源更新
247                         mAuthorListAdapter.refreshData(mAuthors);
248                     }
249
250                     isLoading = false;
251                     mBaseAttacher.setLoadMoreEnabled(true);
252                     myProgressBar.setVisibility(View.GONE);
253
254                 } catch (XmlPullParserException e) {
255                     e.printStackTrace();
256                 } catch (IOException e) {
257                     e.printStackTrace();
258                 }
259             }
260         }, new Response.ErrorListener() {
261             @Override
262             public void onErrorResponse(VolleyError volleyError) {
263                 Toast.makeText(SearchActivity.this, volleyError.getMessage(), Toast.LENGTH_SHORT).show();
264             }
265         });
266
267         VolleyRequestQueueManager.addRequest(request, "getAuthorList");
268
269
270     }
271
272 }

这里是博客列表的适配器类,和之前的博文列表一样,我们需要自己添加点击监听,再点击Item项的时候将Blog对象传递:

  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.view.LayoutInflater;
  9 import android.view.View;
 10 import android.view.ViewGroup;
 11 import android.widget.ImageButton;
 12 import android.widget.TextView;
 13
 14 import com.lcw.rabbit.myblog.R;
 15 import com.lcw.rabbit.myblog.entity.Author;
 16 import com.lcw.rabbit.myblog.utils.ImageCacheManager;
 17 import com.lcw.rabbit.myblog.utils.TimeUtil;
 18 import com.makeramen.roundedimageview.RoundedImageView;
 19
 20 import java.util.List;
 21
 22 /**
 23  * 推荐博主列表适配器
 24  * Created by Lichenwei
 25  * Date: 2015-08-16
 26  * Time: 22:34
 27  */
 28 public class AuthorListAdapter extends RecyclerView.Adapter<AuthorListAdapter.RecyclerViewViewHolder> {
 29
 30     private Context mContext;
 31     private List<Author> mAuthors;
 32
 33     public AuthorListAdapter(Context context, List<Author> authors) {
 34         this.mContext = context;
 35         this.mAuthors = authors;
 36     }
 37
 38     /**
 39      * 设置新的数据源,提醒adatper更新
 40      *
 41      * @param authors
 42      */
 43     public void refreshData(List<Author> authors) {
 44         this.mAuthors = authors;
 45         this.notifyDataSetChanged();
 46     }
 47
 48     /**
 49      * 自定义点击回调接口
 50      */
 51     public interface RecyclerViewListener {
 52         void setOnclickListener(View view, String value);
 53     }
 54
 55     private RecyclerViewListener mRecyclerViewListener;
 56
 57     /**
 58      * 提供setter方法
 59      *
 60      * @param recyclerViewListener
 61      */
 62     public void setRecyclerViewListener(RecyclerViewListener recyclerViewListener) {
 63         this.mRecyclerViewListener = recyclerViewListener;
 64     }
 65
 66
 67     /**
 68      * 创建ViewHolder
 69      *
 70      * @param viewGroup
 71      * @param i
 72      * @return
 73      */
 74     @Override
 75     public RecyclerViewViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
 76         View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerview_item_authorlist, viewGroup, false);
 77         return new RecyclerViewViewHolder(view);
 78     }
 79
 80     /**
 81      * 根据资源ID返回Bitmap对象
 82      *
 83      * @param resId
 84      * @return
 85      */
 86     public Bitmap getBitmapFromRes(int resId) {
 87         Resources res = mContext.getResources();
 88         return BitmapFactory.decodeResource(res, resId);
 89
 90     }
 91
 92     /**
 93      * 绑定数据
 94      *
 95      * @param viewholder
 96      * @param i
 97      */
 98     @Override
 99     public void onBindViewHolder(RecyclerViewViewHolder viewholder, int i) {
100         //设置头像
101         if (mAuthors.get(i).getAuthorPic() != null && !"".equals(mAuthors.get(i).getAuthorPic())) {
102             ImageCacheManager.loadImage(mAuthors.get(i).getAuthorPic(), viewholder.mUserhead, getBitmapFromRes(R.mipmap.avatar_default), getBitmapFromRes(R.mipmap.avatar_default));
103         } else {
104             viewholder.mUserhead.setImageResource(R.mipmap.avatar_default);
105         }
106         viewholder.mName.setText(mAuthors.get(i).getAuthorName());
107         viewholder.mUrl.setText(mAuthors.get(i).getAuthorUrl());
108         viewholder.mSum.setText(mAuthors.get(i).getBlogCount());
109         viewholder.mTime.setText(mAuthors.get(i).getUpdated());
110         //处理日期特殊格式
111         String date = TimeUtil.DateToChineseString(TimeUtil.ParseUTCDate(mAuthors.get(i).getUpdated()));
112         viewholder.mTime.setText(date);
113
114         //设置点击监听
115         viewholder.itemView.setTag(i);
116         viewholder.mMore.setTag(i);
117         viewholder.itemView.setOnClickListener(new ItemClick());
118         viewholder.mMore.setOnClickListener(new ItemClick());
119     }
120
121     @Override
122     public int getItemCount() {
123         return mAuthors.size();
124     }
125
126     /**
127      * 自定义ViewHolder
128      */
129     public static class RecyclerViewViewHolder extends RecyclerView.ViewHolder {
130         private RoundedImageView mUserhead;
131         private TextView mName;
132         private TextView mUrl;
133         private TextView mTime;
134         private TextView mSum;
135         private ImageButton mMore;
136
137         public RecyclerViewViewHolder(View view) {
138             super(view);
139             mUserhead = (RoundedImageView) view.findViewById(R.id.iv_userhead);
140             mName = (TextView) view.findViewById(R.id.tv_name);
141             mUrl = (TextView) view.findViewById(R.id.tv_url);
142             mTime = (TextView) view.findViewById(R.id.tv_time);
143             mSum = (TextView) view.findViewById(R.id.tv_sum);
144             mMore = (ImageButton) view.findViewById(R.id.ib_more);
145
146         }
147
148     }
149
150
151     /**
152      * 点击事件实现类
153      */
154     public class ItemClick implements View.OnClickListener {
155         @Override
156         public void onClick(View v) {
157             if (mRecyclerViewListener != null) {
158                 mRecyclerViewListener.setOnclickListener(v, String.valueOf(v.getTag()));
159             }
160         }
161     }
162 }

2、关于博客首页的实现:

  页面的整体结构还是类似,上半部分是信息展示,下半部分是一个RecyclerView列表,列表项里面的布局和相关操作下拉刷新上拉加载,我们都可以复用之前首页的博文展示,这里只不过是数据源的不同。

  由于我们在搜索页面点击Item项的时候传递了Blog对象,我们就可以根据作者名来获取关于博客作者的一些信息,这里是接口:http://wcf.open.cnblogs.com/blog/u/{BLOGAPP}/posts/{PAGEINDEX}/{PAGESIZE} {BLOGAPP}代表作者的账号,{PAGEINDEX}代表页码,{PAGESIZE}代表每页的数据条数。

  重复的东西这里就不再多说了,这里讲点新引入的东西,细心的朋友可能有发现当我们更新数据(上拉刷新,下拉加载)的时候,列表底部有个弹出提示框,这个就是安卓5.0用来取代Toast的SnackBar:

1             //snackbar提醒
2                     Snackbar snackbar = Snackbar.make(mToolbar,"当前更新"+mBlogs.size()+"条博客信息",Snackbar.LENGTH_LONG);
3                     snackbar.show();

用法很简单,类似于Toast,不过不一样的是,它是具有交互效果的,也就是说我们可以在Snackbar上添加点击事件,关于Snackbar的详细用法,有兴趣的朋友自己网上找找资料吧。

  然后再来说下关于这个FAB(Floating Action Button)的移动效果,这里我采用的是共享元素移动,由于我们的博文详情页和博主详情页都具有这个控件,所以我们在切换Activity的时候可以进行动画设置,这里makeSceneTransitionAnimation的第二个参数为共享View,第三个参数为标志参数要与xml里的transitionName保持一致。

1     android:transitionName="fab"
1  startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(AuthorActivity.this, mFloatingActionButton, "fab").toBundle());

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

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

时间: 2024-08-01 10:46:45

安卓开发笔记——打造属于自己的博客园APP(四)的相关文章

安卓开发笔记——打造属于自己的博客园APP(二)

在上一篇文章<安卓开发笔记——打造属于自己的博客园APP(一)>中,我们基本上实现了博客园的主体UI框架(后面可能会有些小变化,等遇到了再说).今天来讲讲博客园首页模块的大体实现,国际惯例,先来看下效果图: 整体UI效果: 下拉刷新和上拉加载的动画效果: 在上篇文章中,我们定义的Tabs主题文字分别是(首页,精华,候选,推荐),这边的命名我是根据博客园网站首页的栏目来命名的,那时候我还没仔细看过博客园的开放接口,后来才发现原来博客园没有对应开放这些栏目的接口,博客园只开放了(文章列表,48小时

安卓开发笔记——打造属于自己的博客园APP(一)

最近事情比较多,博客更新又落下了,平时有个习惯,喜欢睡前看看博客园里博友的文章,但一直感觉APP市场上下载下来的博客园客户端用起来并不是很舒服,近来发现博客园也有对外开放的数据接口,所以打算自己写个博客园的客户端. 近来谷歌推出了一套全新的UI设计规范——Material Design,不清楚的朋友看看<Material design非官方中文指导手册>,相比之前谷歌在Android Holo风格上平平淡淡的表现不同,Material Design现在是被Google所比较重视的.在推出这门全

Xamarin Android 打造属于自己的博客园APP(2)

好尴尬,隔了四个月才写第二篇幅,自己都不好意思了.这个拖延症好尴尬,妈蛋! 为那些在期待续集的同学说声sorry! 下面开始正文: 完成功能: 1.分页获取首页文章 2.分页获取精华文章 3.分页获取新闻 4.分页获取知识库文章 5.博客园账号登录,获取收藏文章,我的博客 6.文章评论 在写第一篇的时候,那个时候APP接口是调用的博客园公开的wcf接口,很多功能都有所限制,有诸多的不方便.我看见官方有ios版本的博客园,我用fiddler监听了下请求地址,发现域名是以api.cnblogs.co

Xamarin Android 打造属于自己的博客园APP(1)

目前完成情况 目前已有功能:分页获取首页博客,登录,查看文章详情,以及查看提交评论. 目前查看评论有一个Bug,提交的评论在APP上不能够立即刷新出来,但是在网页上能及时刷新出来,我已经确认过我代码很多次了,确定没有问题.纠结了有好几天了,不知道是不是WCF接口有问题. 未做功能:想按照分类获取文章,离线收藏,以后APP一些地方优化. 主要是作为一个个人开发者,白天上班,晚上才有时间搞.所以进度较慢. 偷个懒,直接发个大图吧,太累了,要睡觉了. 代码的话还需要整理下,不过目前的APK我上传到百度

Xamarin Android 打造属于自己的博客园APP(3)

打造通用下拉刷新上拉加载更多组件 android开发中最常用的就是列表组件,如ListView,recycleView,用到它们感觉就会涉及到数据更新,分页加载. 最开始的时候,刷新组件我是在技术群里头找了一个被人绑定好的库,是绑定的github上一个星星很多的java原生组件.但是demo很简单,对于当时小白的我懵逼了,不晓得咋个用,而且一直觉得banding的库总感觉有问题,就想着直接找一个java的库翻译成C#版本的.功夫不负苦心人,在csdn上找到了一篇 http://blog.csdn

【原】博客园第三方客户端-i博客园App开源

[原]博客园第三方客户端-i博客园App开源 本文转载请注明出处 —— polobymulberry-博客园 1.前言 目前i博客园App已经更新到2.0.0版本了,使用了最新的博客园Web API.相比于第一个版本,添加了很多新的功能,也修改了很多功能.整体来说改动比较大,代码也比较混乱.所以趁着清明假期,把代码好好整理了一番.目前基本的架构已成型(当然,后期还需要不断优化),但App基本功能方面还有很多需要添加的,后面会集中把App功能完善. 上面简单介绍了下目前App的情况,回到开源的话题

基于Corova的博客园APP

       背景: 自从今年下半年接触一个基于ReactJS 的手机APP项目.开始了解到了Corodva这个神奇的东西.后续自己也自作了一些小的APP放到了应用宝上.8月份开始想做一个博客园APP.于是就私聊博客园团队申请了博客园API访问权限(当然其实应用宝里面也有博客园APP了.而且用fiddler抓取了一下.发现API还很完善.说实话比博客园团队提供的API完善多了.我想应该是开发者用爬虫软件爬的页面然后自己解析的吧.) 第一步: 申请API: 私聊博客园,获取了OAuth的Clien

安卓开发笔记——打造万能适配器(Adapter)

为什么要打造万能适配器? 在安卓开发中,用到ListView和GridView的地方实在是太多了,系统默认给我们提供的适配器(ArrayAdapter,SimpleAdapter)经常不能满足我们的需要,因此我们时常要去继承BaseAdapter类去实现一个自定义的适配器来满足我们的场景需要. 如果你是开发一个简单点的APP还好,可能ListView和GridView的数量不会太多,我们只要去写几个BaseAdapter实现类就可以了. 但如果有一天,你需要开发一个APP里面具有几十个ListV

安卓开发笔记——深入Activity

在上一篇文章<安卓开发笔记——重识Activity >中,我们了解了Activity生命周期的执行顺序和一些基本的数据保存操作,但如果只知道这些是对于我们的开发需求来说是远远不够的,今天我们继续探索Activity,来了解下关于Activity任务栈和Activity四种启动模式的区别. 为什么需要了解关于Activity的任务栈,其实最直接的体现就是提高用户交互友好性. 举个例子,当我们去浏览一个新闻客户端的时候,我们进入了新闻详情页,在这个页面有相隔两条的新闻标题,当我们去点击这个标题的时