安卓开发笔记——打造属于自己的博客园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>
这里是下面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>
这里是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>
关于博客园推荐博客的接口数据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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!