Android 博客园客户端 (八) 下拉刷新、分页,AsyncTask
一直以来,无论是博客、新闻、还是推荐用户列表。只能加载固定的数量,也没有刷新功能。
为了实现这个功能,也试过很多第三方的开源控件,如PullToRefreshListVie等。无意中发现了Google官方发布了一个新的控件(SwipeRefreshLayout),支持下拉刷新,这个控件在Google应用中都有出现过。效果也是非常的不错。
具体的使用方法及代码如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="fill_parent" 3 android:layout_height="fill_parent" 4 android:orientation="vertical" > 5 6 <android.support.v4.widget.SwipeRefreshLayout 7 android:id="@+id/swipeRefreshLayoutHomeBlog" 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" > 10 11 <ListView 12 android:id="@+id/listViewHomeBlog" 13 android:layout_width="match_parent" 14 android:layout_height="wrap_content" > 15 </ListView> 16 </android.support.v4.widget.SwipeRefreshLayout> 17 18 </LinearLayout>
需要注意的一点就是SwipeRefreshLayout中只能包含一个控件。其他属性和其他Layout大体相同。
第一点,下拉刷新:
使用的话,需要在Activity或者Fragment中导入并实现android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener接口,并且要重写onRefresh()方法。
第二点,分页处理:
需要实现OnScrollListener接口,并且重写和onScrollStateChangedonScroll和两个方法。
具体代码如下
1 package com.arlen.cnblogs.fragment; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.content.Intent; 7 import android.os.Bundle; 8 import android.support.v4.app.Fragment; 9 import android.support.v4.widget.SwipeRefreshLayout; 10 import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener; 11 import android.util.Log; 12 import android.view.LayoutInflater; 13 import android.view.View; 14 import android.view.ViewGroup; 15 import android.widget.AbsListView; 16 import android.widget.AbsListView.OnScrollListener; 17 import android.widget.AdapterView; 18 import android.widget.AdapterView.OnItemClickListener; 19 import android.widget.AdapterView.OnItemLongClickListener; 20 import android.widget.ListView; 21 22 import com.arlen.cnblogs.BlogActivity; 23 import com.arlen.cnblogs.R; 24 import com.arlen.cnblogs.adapter.BlogListAdapter; 25 import com.arlen.cnblogs.entity.Blog; 26 import com.arlen.cnblogs.task.BlogListTask; 27 import com.arlen.cnblogs.utils.AppMacros; 28 import com.arlen.cnblogs.utils.AppUtils; 29 30 public class BlogHomeFragment extends Fragment implements 31 OnItemLongClickListener, OnItemClickListener, OnRefreshListener, 32 OnScrollListener { 33 private static final String TAG = BlogHomeFragment.class.getSimpleName(); 34 35 private SwipeRefreshLayout swipeRefreshLayout; 36 private ListView listView; 37 38 private BlogListAdapter adapter; 39 40 private String path; 41 private int pageSize; 42 private int pageIndex = 1; 43 private List<Blog> blogList; 44 45 private int lastVisibleIndex; 46 private int maxVisibleIndex = 400; 47 48 private Intent intent; 49 50 public BlogHomeFragment() { 51 super(); 52 } 53 54 @Override 55 public void onCreate(Bundle savedInstanceState) { 56 super.onCreate(savedInstanceState); 57 Log.i(TAG, "onCreate"); 58 } 59 60 @Override 61 public View onCreateView(LayoutInflater inflater, ViewGroup container, 62 Bundle savedInstanceState) { 63 Log.i(TAG, "onCreateView"); 64 View rootView = inflater.inflate(R.layout.fragment_blog_home, 65 container, false); 66 return rootView; 67 } 68 69 @Override 70 public void onViewCreated(View view, Bundle savedInstanceState) { 71 super.onViewCreated(view, savedInstanceState); 72 Log.i(TAG, "onViewCreated"); 73 initComponent(); 74 initData(); 75 } 76 77 @Override 78 public void onItemClick(AdapterView<?> parent, View view, int position, 79 long id) { 80 Log.i(TAG, "onItemClick -- " + position); 81 showBlogItem(blogList.get(position)); 82 } 83 84 @Override 85 public boolean onItemLongClick(AdapterView<?> parent, View view, 86 int position, long id) { 87 Log.i(TAG, "onItemLongClick -- " + position); 88 return false; 89 } 90 91 @Override 92 public void onRefresh() { 93 new BlogListTask(blogList, swipeRefreshLayout, adapter).execute(path, 94 "refresh"); 95 } 96 97 @Override 98 public void onScrollStateChanged(AbsListView view, int scrollState) { 99 if (adapter.getCount() < maxVisibleIndex) { 100 if (scrollState == OnScrollListener.SCROLL_STATE_IDLE 101 && lastVisibleIndex == adapter.getCount() - 1) { 102 103 pageIndex++; 104 initPath(pageIndex); 105 swipeRefreshLayout.setRefreshing(true); 106 new BlogListTask(blogList, swipeRefreshLayout, adapter) 107 .execute(path, "loadMore"); 108 } 109 } else { 110 // Toast.makeText(getActivity(), "最后一页!", 111 // Toast.LENGTH_SHORT).show(); 112 } 113 } 114 115 @Override 116 public void onScroll(AbsListView view, int firstVisibleItem, 117 int visibleItemCount, int totalItemCount) { 118 lastVisibleIndex = firstVisibleItem + visibleItemCount - 1; 119 } 120 121 private void initComponent() { 122 swipeRefreshLayout = (SwipeRefreshLayout) this.getActivity() 123 .findViewById(R.id.swipeRefreshLayoutHomeBlog); 124 swipeRefreshLayout.setOnRefreshListener(this); 125 swipeRefreshLayout.setColorSchemeResources( 126 android.R.color.holo_blue_bright, 127 android.R.color.holo_green_light, 128 android.R.color.holo_orange_light, 129 android.R.color.holo_red_light); 130 131 listView = (ListView) this.getActivity().findViewById( 132 R.id.listViewHomeBlog); 133 listView.setOnItemClickListener(this); 134 listView.setOnItemLongClickListener(this); 135 listView.setOnScrollListener(this); 136 } 137 138 private void initData() { 139 blogList = new ArrayList<Blog>(); 140 adapter = new BlogListAdapter(getActivity(), blogList); 141 listView.setAdapter(adapter); 142 143 initPath(1); 144 swipeRefreshLayout.setRefreshing(true); 145 new BlogListTask(blogList, swipeRefreshLayout, adapter).execute(path, 146 "init"); 147 } 148 149 private void initPath(int pageIndex) { 150 // http://wcf.open.cnblogs.com/blog/sitehome/paged/{PAGEINDEX}/{PAGESIZE} 151 path = AppMacros.RECENT_BLOGS_PAGED; 152 pageSize = AppMacros.PAGE_SIZE; 153 path = path.replace("{PAGEINDEX}", "" + pageIndex); 154 path = path.replace("{PAGESIZE}", "" + pageSize); 155 156 Log.i(TAG, "pageIndex:" + pageIndex); 157 } 158 159 private void showBlogItem(Blog blogEntry) { 160 intent = new Intent(this.getActivity(), BlogActivity.class); 161 162 if (blogEntry.getAuthorAvatar() != null) { 163 intent.putExtra("avatar", blogEntry.getAuthorAvatar().toString()); 164 } else { 165 intent.putExtra("avatar", ""); 166 } 167 intent.putExtra("title", blogEntry.getBlogTitle()); 168 intent.putExtra("author", blogEntry.getAuthorName()); 169 intent.putExtra("published", 170 AppUtils.parseDateToString(blogEntry.getPublishedDateDate())); 171 intent.putExtra("id", blogEntry.getBlogId()); 172 intent.putExtra("link", blogEntry.getBlogTitle()); 173 174 startActivity(intent); 175 } 176 }
在之前的项目中,使用了Handler+Thread来加载数据刷新控件,但是感觉代码比较混乱。在新的版本中使用了AsyncTask来替换。具体实现如下:
1 package com.arlen.cnblogs.task; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import com.arlen.cnblogs.adapter.BlogListAdapter; 7 import com.arlen.cnblogs.entity.Blog; 8 import com.arlen.cnblogs.utils.AppUtils; 9 import com.arlen.cnblogs.utils.HttpUtil; 10 11 import android.os.AsyncTask; 12 import android.support.v4.widget.SwipeRefreshLayout; 13 14 public class BlogListTask extends AsyncTask<String, Void, Void> { 15 private List<Blog> blogList; 16 private List<Blog> newList = new ArrayList<Blog>(); 17 private SwipeRefreshLayout swipeRefreshLayout; 18 private BlogListAdapter adapter; 19 20 public BlogListTask(List<Blog> blogList, 21 SwipeRefreshLayout swipeRefreshLayout, BlogListAdapter adapter) { 22 super(); 23 this.blogList = blogList; 24 this.swipeRefreshLayout = swipeRefreshLayout; 25 this.adapter = adapter; 26 } 27 28 protected Void doInBackground(String... params) { 29 30 newList = HttpUtil.getBlogList(params[0]); 31 32 if (params[1].equals("init")) { 33 blogList.addAll(newList); 34 } else if (params[1].equals("refresh")) { 35 newList.addAll(blogList); 36 AppUtils.removeDuplicate(newList); 37 blogList.clear(); 38 blogList.addAll(newList); 39 } else if (params[1].equals("loadMore")) { 40 blogList.addAll(newList); 41 AppUtils.removeDuplicate(blogList); 42 } 43 return null; 44 } 45 46 @Override 47 protected void onPreExecute() { 48 super.onPreExecute(); 49 newList.clear(); 50 } 51 52 @Override 53 protected void onPostExecute(Void result) { 54 super.onPostExecute(result); 55 56 swipeRefreshLayout.setRefreshing(false); 57 adapter.updataBlogList(blogList); 58 adapter.notifyDataSetChanged(); 59 } 60 61 }
刷新数据后可能会出现数据重复,要去除重复,首先要在类中重写equals()方法,应为要考虑到不同类型,使用了泛型方法来对User、Blog、News和Comment来进行去重。具体如下:
1 @Override 2 public boolean equals(Object object) { 3 if (object instanceof Blog) { 4 Blog blog = (Blog) object; 5 return String.valueOf(blog.getBlogId()).equals( 6 String.valueOf(this.getBlogId())); 7 } else { 8 return super.equals(object); 9 } 10 }
1 public static <T> void removeDuplicate(List<T> list) { 2 for (int i = 0; i < list.size(); i++) { 3 for (int j = i + 1; j < list.size(); j++) { 4 if (list.get(i).equals(list.get(j))) { 5 list.remove(j); 6 } 7 } 8 } 9 }