自定义组件之MoreListView
前言
本文针对自定义组件进行一些分析。还是那句老话“授之于鱼不如授之以渔”。今天要讲的是一个自定义的可以分页的ListView。
网上都讲了些ListView分页的方法,那么为什么我在这里还需要自己写一个呢?
①分页功能是很多时候都需要的;
②网上的很多代码和数据绑定在一起的,要使用的话还需要改些东西,更重要的是代码很啰嗦、很糟糕(当然只是个人风格问题,至少我是这么认为);
③或者是功能太强大而我们仅仅是需要分页功能,但是又不好分离出来。
所以写个分页的listview就很有必要了,尤其是可以直接使用的listview。
效果图
说得再多都是苍白无力的理论,先给个效果图看看吧。
分析
这个组件看上去简单,不过需要处理的细节还是挺多的。首先要明确这个组件是做什么的。简单地讲就是做分页显示或分页加载的。那么需要考虑这两个问题:
1、什么时候需要分页?
那么什么时候需要分页呢,这个答案是很明朗的。当数据数量不能填满一屏的时候那么就不用分页。在这个组件中我是这么来定分页的:首次加载的数量大于一屏显示数量时那么就认为有分页的可能,滑动到底部就需要“more”这个按钮。
2、何时加载分页信息?
至于何时进行分页,这是这个组件的关键。理论上是滑动到listview最底部的时候就需要显示一个“more”按钮,点击后进行分页,但是实际上我们需要做一定的小改动,也就是提前加载,及还没有滑动到最底部就开始加载。这样看来主要的问题就落在了如何判断是否滑动到了最底部。这问题在代码中讨论吧。
代码实现
MoreListView 组件代码:
1 /*********************************************************** 2 *@description : This class function is you can load more datas 3 * 4 * @create author : kwzhang 5 * @create date :2013-6-19 6 * @modify author : 7 * @modify date : 8 * @contact: vanezkw@163.com 9 * 10 **********************************************************/ 11 package com.example.demo; 12 13 import android.content.Context; 14 import android.util.AttributeSet; 15 import android.view.View; 16 import android.widget.AbsListView; 17 import android.widget.Button; 18 import android.widget.ListAdapter; 19 import android.widget.ListView; 20 21 /** 22 * @author kwzhang 23 * 24 */ 25 public class MoreListView extends ListView { 26 27 private View mFooter; 28 private LoadingListener mListener; 29 private boolean mShowMore = true; 30 31 /** 32 * @param context 33 * @param attrs 34 */ 35 public MoreListView(Context context, AttributeSet attrs) { 36 super(context, attrs); 37 initFooter(context); 38 } 39 40 /** 41 * @param mListener 42 * the mListener to set 43 */ 44 public void setListener(LoadingListener mListener) { 45 this.mListener = mListener; 46 setOnScrollListener(innerOnScrollListener); 47 } 48 49 /** 50 * @return the mShowMore 51 */ 52 public boolean isShowMore() { 53 return mShowMore; 54 } 55 56 /** 57 * @param mShowMore 58 * the mShowMore to set 59 */ 60 public void setShowMore(boolean mShowMore) { 61 this.mShowMore = mShowMore; 62 } 63 64 /** 65 * @description :TODO 66 * @author : kwzhang 67 * @create :2013-6-19 68 * @param context 69 * @return :void 70 */ 71 protected void initFooter(Context context, View footer) { 72 if (null == footer) { 73 Button bt = new Button(context); 74 bt.setText("More ..."); 75 mFooter = bt; 76 } else { 77 mFooter = footer; 78 } 79 mFooter.setOnClickListener(new OnClickListener() { 80 @Override 81 public void onClick(View v) { 82 if (getFooterViewsCount() > 0) { 83 removeFooterView(mFooter); 84 } 85 if (null != mListener) { 86 mListener.loadingMore(MoreListView.this); 87 } 88 } 89 }); 90 addFooterView(mFooter); 91 } 92 93 private void initFooter(Context context) { 94 initFooter(context, null); 95 } 96 97 @Override 98 public void setAdapter(ListAdapter adapter) { 99 super.setAdapter(adapter); 100 removeFooterView(mFooter); 101 } 102 103 /** 104 * @param context 105 * @param attrs 106 * @param defStyle 107 */ 108 public MoreListView(Context context, AttributeSet attrs, int defStyle) { 109 super(context, attrs, defStyle); 110 initFooter(context); 111 } 112 113 /** 114 * @param context 115 */ 116 public MoreListView(Context context) { 117 super(context); 118 initFooter(context); 119 } 120 121 private OnScrollListener innerOnScrollListener = new OnScrollListener() { 122 @Override 123 public void onScrollStateChanged(AbsListView view, int scrollState) { 124 // mLastVisibleItemIndex+1 == getAdapter().getCount()时说明滑动到最底端。 125 // mLastVisibleItemIndex+3 是为了在倒数第二个就开始预先加载。不过此方法是采样调用,不一定及时执行。 126 if (mShowMore) { 127 int count = getAdapter().getCount(); 128 if ((mVisibleItemCount < count) && ((mLastVisibleItemIndex + 3) >= count) && (getFooterViewsCount() == 0)) { 129 addFooterView(mFooter); 130 } 131 } 132 } 133 134 private int mLastVisibleItemIndex; 135 private int mVisibleItemCount = -1; 136 137 @Override 138 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 139 mLastVisibleItemIndex = firstVisibleItem + visibleItemCount - 1; 140 141 mVisibleItemCount = visibleItemCount; 142 143 } 144 }; 145 146 public static interface LoadingListener { 147 public void loadingMore(View view); 148 } 149 }
测试Demo Activity:
1 package com.example.demo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.view.View; 6 import android.widget.ArrayAdapter; 7 8 public class ActDemo extends Activity implements MoreListView.LoadingListener { 9 10 private MoreListView listView; 11 private ArrayAdapter<String> mAdapter; 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 listView = (MoreListView) findViewById(R.id.listview); 18 mAdapter = getAdapter(); 19 listView.setAdapter(mAdapter); 20 listView.setListener(this); 21 } 22 23 private ArrayAdapter<String> getAdapter() { 24 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1); 25 for (int i = 0; i < 20; i++) { 26 adapter.add("测试数据" + i); 27 } 28 return adapter; 29 } 30 31 @Override 32 public void loadingMore(View view) { 33 for (int i = 0; i < 10; i++) { 34 mAdapter.add("新数据" + i); 35 } 36 if (mAdapter.getCount() > 60) { 37 listView.setShowMore(false); 38 } 39 } 40 }
测试Demo XML布局:
<com.example.demo.MoreListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/listview" android:layout_width="fill_parent" android:layout_height="fill_parent" />
从上面的测试demo中可以看出使用起来非常方便。
1、protected void initFooter(Context context, View footer)方法说明一下。这个方法是给外部提供传递view的,这个view就是显示“more”的那个view,传null的话就有个默认的Button。
2、public void setListener(LoadingListener mListener)需要分页的话就必须调用这个方法。点击“more”按钮的时候其实去调用了LoadingListener.loadingMore(View view)方法。你可以根据需要进行实现。
总结
这样的组件就比较简单实用,也没有太复杂的代码,一看就能懂。我们需要的不正是这样简单的代码吗。随便说一下如果你需要更强的的Listview的话可以去看看AmazingListView项目。下拉刷新你可以看看johannilsson-android-pulltorefresh项目
PS:对文中有不解之处欢迎交流,有什么好的建议也可以留言。留下个QQ学习群:196761677。