Android之下拉刷新的ListView
生活随笔
收集整理的這篇文章主要介紹了
Android之下拉刷新的ListView
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
不廢話,代碼里面注釋很詳細(xì),直接上代碼:
自定義的RefreshableListView代碼:
1 public class RefreshableListView extends ListView implements OnScrollListener { 2 private View header; // ListView頂部布局 3 private LayoutInflater inflater; 4 private int headerHeight; // 頂部布局Header的高度 5 private int firstVisisblePosition; // 當(dāng)前第一個可見的Item的位置 6 private int scrollState; // ListView當(dāng)前的滾動狀態(tài) 7 8 private boolean remarkTop; // 標(biāo)記,當(dāng)前是在ListView的最頂端按下的 9 private int startY; // 手指按下時的Y值 10 11 private int state; // 指示當(dāng)前的狀態(tài) 12 private final int STATE_NORMAL = 0; // 正常狀態(tài) 13 private final int STATE_PULL = 1; // 提示“下拉可以刷新”的狀態(tài) 14 private final int STATE_TOREFRESH = 2; // 提示“松開手指刷新”的狀態(tài) 15 private final int STATE_REFRESHING = 3; // 正在刷新的狀態(tài) 16 17 // Header布局中的四個控件 18 private TextView refreshTip; // 顯示“下拉可以刷新”/“松開手指刷新”的TextView 19 private TextView timeTip; // 顯示上次刷新的時間的TextView 20 private ImageView arrowImg; // 向上/向下的箭頭的ImageView 21 private ProgressBar progressBar; // 刷新數(shù)據(jù)時用到的ProgressBar 22 23 private ListViewRefreshListener listener; // 刷新數(shù)據(jù)的接口 24 25 // 自定義控件都必須實(shí)現(xiàn)以下三個構(gòu)造方法(一個參數(shù)、兩個參數(shù)、三個參數(shù)的構(gòu)造方法) 26 // 我們在一個參數(shù)的構(gòu)造方法中調(diào)用兩個參數(shù)的構(gòu)造方法,在兩個參數(shù)的構(gòu)造方法中調(diào)用三個參數(shù)的構(gòu)造方法,這樣不管我們用哪個構(gòu)造方法,最終的調(diào)用代碼是一樣的 27 // 一個參數(shù)的構(gòu)造方法:這個方法是在Activity中根據(jù)上下文環(huán)境直接生成控件時調(diào)用的 28 public RefreshableListView(Context context) { 29 this(context, null); 30 } 31 32 // 兩個參數(shù)的構(gòu)造方法:這個方法是在使用了系統(tǒng)屬性,沒有使用自定義屬性時調(diào)用的 33 public RefreshableListView(Context context, AttributeSet attrs) { 34 this(context, attrs, 0); 35 } 36 37 // 三個參數(shù)的構(gòu)造方法:這個方法是在使用了自定義屬性時調(diào)用的 38 public RefreshableListView(Context context, AttributeSet attrs, int defStyleAttr) { 39 super(context, attrs, defStyleAttr); 40 initView(context); 41 // 找到Header中的控件 42 refreshTip = (TextView) header.findViewById(R.id.control_header_refreshtip); 43 timeTip = (TextView) header.findViewById(R.id.control_header_timetip); 44 arrowImg = (ImageView) header.findViewById(R.id.control_header_refresharrow); 45 progressBar = (ProgressBar) header.findViewById(R.id.control_header_progressbar); 46 } 47 48 // 初始化界面,添加頂部布局文件到ListView中 49 private void initView(Context context) { 50 inflater = LayoutInflater.from(context); 51 header = inflater.inflate(R.layout.sideworks_layout_header, null); 52 // 測量頂部布局header的高度 53 measureView(context); 54 headerHeight = header.getMeasuredHeight(); 55 setViewTopPadding(-headerHeight); // 設(shè)置ListView的上縮進(jìn):是負(fù)值,表示將header布局縮到屏幕外面去 56 // 把頂部布局添加到ListView的最上面 57 this.addHeaderView(header); 58 // 設(shè)置向下滑動時逐漸顯示頂部布局(接口回掉方法) 59 this.setOnScrollListener(this); 60 } 61 62 // 測量控件的寬高(通知父佈局:我佔(zhàn)用的寬和高) 63 private void measureView(Context context) { 64 ViewGroup.LayoutParams lp = header.getLayoutParams(); // 獲取header布局的寬高屬性 65 if (lp == null) { 66 lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 67 } 68 int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width); 69 int height; // 不能用getChildMeasureSpec方法獲取高度的原因是ListView的高度不確定,而寬度是確定的 70 int tempHeight = lp.height; 71 if (tempHeight > 0) { // 大于0說明定義了ListView的高度,所以我們用精確布局模式EXACTLY 72 height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); 73 } else { // 如果不大于0,則表示沒有定義ListView的高度,即UNSPECIFIED 74 height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 75 } 76 header.measure(width, height); // 這行代碼很容易報(bào)錯:NullPointerException,所以SDK17以前的版本必須將布局的最外層設(shè)置為LinearLayout 77 } 78 79 // 設(shè)置ListView的TopPadding屬性 80 private void setViewTopPadding(int topPadding) { 81 this.setPadding(this.getPaddingLeft(), topPadding, this.getPaddingRight(), this.getPaddingBottom()); 82 this.invalidate(); // invalidate()方法的作用是請求對該控件進(jìn)行重繪 83 } 84 85 @Override 86 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 87 this.firstVisisblePosition = firstVisibleItem; 88 } 89 90 @Override 91 public void onScrollStateChanged(AbsListView view, int scrollState) { 92 this.scrollState = scrollState; 93 } 94 95 @Override 96 public boolean onTouchEvent(MotionEvent ev) { 97 switch (ev.getAction()) { 98 case MotionEvent.ACTION_DOWN: 99 if (firstVisisblePosition == 0) { 100 remarkTop = true; 101 startY = (int) ev.getY(); // 如果按下時是處在ListView最上面的Item,則記錄當(dāng)前的Y坐標(biāo)值 102 } 103 break; 104 case MotionEvent.ACTION_MOVE: 105 onMove(ev); 106 break; 107 case MotionEvent.ACTION_UP: 108 if (state == STATE_TOREFRESH) { // 滑動到了“松開手指刷新數(shù)據(jù)”的高度 109 state = STATE_REFRESHING; 110 refreshViewByState(); 111 listener.refreshListView(); // 調(diào)用接口,刷新數(shù)據(jù) 112 } else if (state == STATE_PULL) { // 還是處在“下拉刷新數(shù)據(jù)”的高度 113 state = STATE_NORMAL; 114 remarkTop = false; 115 refreshViewByState(); 116 } 117 break; 118 } 119 return super.onTouchEvent(ev); 120 } 121 122 // 判斷移動過程中的操作 123 private void onMove(MotionEvent ev) { 124 if (!remarkTop) { // 如果按下地點(diǎn)不是ListView的第一個Item,則不做處理,正常滑動 125 return; 126 } 127 int tempY = (int) ev.getY(); // 當(dāng)前移動到了什么位置(Y坐標(biāo)值) 128 int space = tempY - startY; // 判斷當(dāng)前移動了多大距離(即header布局被拉下來多少),向下拉時是正值 129 int topPadding = space - headerHeight; // 當(dāng)前還在屏幕外面的header布局的高度 130 switch (state) { 131 case STATE_NORMAL: 132 if (space > 0) { 133 state = STATE_PULL; 134 refreshViewByState(); 135 } 136 break; 137 case STATE_PULL: 138 setViewTopPadding(topPadding); 139 if (space > headerHeight && scrollState == SCROLL_STATE_TOUCH_SCROLL) { // 滑動過header高度的一半并且仍然在滑動 140 state = STATE_TOREFRESH; 141 refreshViewByState(); 142 } 143 break; 144 case STATE_TOREFRESH: 145 setViewTopPadding(topPadding); 146 if (space < headerHeight) { 147 state = STATE_PULL; 148 refreshViewByState(); 149 } else if (space <= 0) { 150 state = STATE_NORMAL; 151 remarkTop = false; 152 refreshViewByState(); 153 } 154 break; 155 } 156 } 157 158 // 根據(jù)當(dāng)前狀態(tài),改變界面顯示 159 private void refreshViewByState() { 160 // 箭頭反轉(zhuǎn)的兩個動畫 161 RotateAnimation anim1 = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); 162 anim1.setDuration(500); 163 anim1.setFillAfter(true); 164 RotateAnimation anim2 = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); 165 anim2.setDuration(500); 166 anim2.setFillAfter(true); 167 168 switch (state) { 169 case STATE_NORMAL: 170 setViewTopPadding(-headerHeight); 171 arrowImg.clearAnimation(); 172 break; 173 case STATE_PULL: 174 arrowImg.setVisibility(View.VISIBLE); 175 progressBar.setVisibility(View.GONE); 176 refreshTip.setText("下拉可以刷新!"); 177 arrowImg.clearAnimation(); 178 arrowImg.setAnimation(anim2); 179 break; 180 case STATE_TOREFRESH: 181 arrowImg.setVisibility(View.VISIBLE); 182 progressBar.setVisibility(View.GONE); 183 refreshTip.setText("松開立即刷新!"); 184 arrowImg.clearAnimation(); 185 arrowImg.setAnimation(anim1); 186 break; 187 case STATE_REFRESHING: 188 setViewTopPadding(0); 189 arrowImg.setVisibility(View.GONE); 190 progressBar.setVisibility(View.VISIBLE); 191 refreshTip.setText("正在刷新......"); 192 arrowImg.clearAnimation(); 193 break; 194 } 195 } 196 197 public void onRefreshComplete() { 198 state = STATE_NORMAL; 199 remarkTop = false; 200 refreshViewByState(); 201 String time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()); 202 timeTip.setText(time); 203 } 204 205 // 刷新數(shù)據(jù)的接口,要通過接口回掉的方式更新數(shù)據(jù) 206 public interface ListViewRefreshListener { 207 public void refreshListView(); 208 } 209 210 public void setListViewRefreshListener(ListViewRefreshListener listener) { 211 this.listener = listener; 212 } 213 }header布局界面sideworks_layout_header.xml代碼:
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="50.0dip" 5 android:background="@color/cl_header_bg" 6 android:gravity="center" 7 android:padding="10.0dip" > 8 9 <RelativeLayout 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:background="@color/cl_transparent" > 13 14 <ImageView 15 android:id="@+id/control_header_refresharrow" 16 android:layout_width="wrap_content" 17 android:layout_height="35.0dip" 18 android:layout_centerVertical="true" 19 20 android:layout_marginRight="15.0dip" 21 android:contentDescription="@string/app_name" 22 android:src="@drawable/refresh_arrow" /> 23 24 <ProgressBar 25 android:id="@+id/control_header_progressbar" 26 style="?android:attr/progressBarStyleSmall" 27 android:layout_width="wrap_content" 28 android:layout_height="wrap_content" 29 android:layout_centerVertical="true" 30 android:layout_marginRight="15.0dip" 31 android:visibility="gone" /> 32 33 <LinearLayout 34 android:id="@+id/position_header_tips" 35 android:layout_width="wrap_content" 36 android:layout_height="wrap_content" 37 android:layout_centerVertical="true" 38 android:orientation="vertical" 39 android:paddingLeft="30.0dip" > 40 41 42 <TextView 43 android:id="@+id/control_header_refreshtip" 44 android:layout_width="wrap_content" 45 android:layout_height="wrap_content" 46 android:text="@string/str_header_refreshtip" 47 android:textColor="@color/cl_black" 48 android:textSize="12.0sp" /> 49 50 <TextView 51 android:id="@+id/control_header_timetip" 52 android:layout_width="wrap_content" 53 android:layout_height="wrap_content" 54 android:layout_marginTop="-7.0dip" 55 android:textColor="@color/cl_black" 56 android:textSize="12.0sp" /> 57 </LinearLayout> 58 </RelativeLayout> 59 60 </LinearLayout>主界面布局activity_main.xml代碼:
1 <RelativeLayout 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 6 <com.view.RefreshableListView 7 android:id="@+id/control_main_listview" 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" 10 android:cacheColorHint="@color/cl_transparent" /> 11 12 </RelativeLayout>主界面MainActivity.java代碼:
1 public class MainActivity extends Activity implements ListViewRefreshListener { 2 private RefreshableListView testList; 3 public static List<String> dataList; 4 public static ArrayAdapter<String> listAdapter; 5 6 @Override 7 protected void onCreate(Bundle savedInstanceState) { 8 super.onCreate(savedInstanceState); 9 setContentView(R.layout.activity_main); 10 initView(); 11 } 12 13 private void initView() { 14 testList = (RefreshableListView) findViewById(R.id.control_main_listview); 15 testList.setListViewRefreshListener(this); 16 dataList = getData(); 17 listAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, dataList); 18 testList.setAdapter(listAdapter); 19 } 20 21 private List<String> getData() { 22 dataList = new ArrayList<String>(); 23 for (int i = 0; i < 10; i++) { 24 dataList.add("This is a test data."); 25 } 26 return dataList; 27 } 28 29 @Override 30 public void refreshListView() { 31 // 延時兩秒后顯示兩條新數(shù)據(jù):This is a new data. 32 new Handler().postDelayed(new Runnable() { 33 public void run() { 34 for (int i = 0; i < 2; i++) { 35 dataList.add(0, "This is a new data."); 36 } 37 listAdapter.notifyDataSetChanged(); 38 testList.onRefreshComplete(); 39 } 40 }, 2000); 41 } 42 }?
轉(zhuǎn)載于:https://www.cnblogs.com/blog-wzy/p/5313189.html
總結(jié)
以上是生活随笔為你收集整理的Android之下拉刷新的ListView的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Application.DoEvents
- 下一篇: 天线设计该如何入门