效果圖:android
第一步:編寫須要在ListView中增長頭加載的佈局文件,與底部加載的佈局文件:ide
頭佈局文件:佈局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dip" > <ImageView android:id="@+id/iv_listview_header_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@mipmap/common_listview_headview_red_arrow" /> <ProgressBar android:id="@+id/pb_listview_header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:indeterminateDrawable="@drawable/custom_progressbar" android:visibility="invisible" /> </FrameLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center_horizontal" android:orientation="vertical" > <TextView android:id="@+id/tv_listview_header_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下拉刷新" android:textColor="#FF0000" android:textSize="18sp" /> <TextView android:id="@+id/tv_listview_header_last_update_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dip" android:text="最後刷新時間: 1990-09-09 09:09:09" android:textColor="@android:color/darker_gray" android:textSize="14sp" /> </LinearLayout> </LinearLayout>
底部佈局文件:post
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_centerInParent="true" android:gravity="center_vertical"> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminateDrawable="@drawable/custom_progressbar" /> <TextView android:id="@+id/tv_bottom_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加載更多" android:layout_marginLeft="10dp"/> </LinearLayout> </RelativeLayout>
自定義ListView須要的接口回調給UI,告訴UI ListView執行了下拉加載/上拉加載動做 this
public interface ICustomUpdateListViewBack { public void downUpdateListData(); public void upUpdateListData(); }
自定義ListView:spa
public class CustomUpdateListView extends ListView implements AbsListView.OnScrollListener{ private static final String TAG = CustomUpdateListView.class.getSimpleName(); /** * 下拉刷新 */ private static final int DOWN_UPDATE = 111; /** * 準備刷新 */ private static final int PLAN_UPDATE = 112; /** * 正在刷新 */ private static final int PROCESS_UPDATE = 113; private int thisUpdateStatusValue = DOWN_UPDATE; // 默認一直是下拉刷新 public CustomUpdateListView(Context context, AttributeSet attrs) { super(context, attrs); setOnScrollListener(this); initHeader(); initBottom(); } /** * 定義頭部相關 */ private View headerView; private int headerViewHeight; private ImageView ivHeaderArrow; private ProgressBar pbHeader; private TextView tvHeaderState; private TextView tvHeaderLastUpdateTime; /** * 定義底部相關 */ private View bottomView; private int bottomViewHeight; private TextView tvBottomState; /** * 初始化頭部 佈局View相關 */ private void initHeader() { // 從佈局中拿到一個View headerView = View.inflate(getContext(), R.layout.listview_header, null); // 獲取頭部各個控件的值 ivHeaderArrow = headerView.findViewById(R.id.iv_listview_header_arrow); pbHeader = headerView.findViewById(R.id.pb_listview_header); tvHeaderState = headerView.findViewById(R.id.tv_listview_header_state); tvHeaderLastUpdateTime = headerView.findViewById(R.id.tv_listview_header_last_update_time); tvHeaderLastUpdateTime.setText(getThisTiem()); // getHieight(); 方法只能獲取到控件顯示後的高度 // int headerViewHeight = headerView.getHeight(); // 結果 headerViewHeight: 0 // View的繪製流程:測量 onLayout onDraw // 因此先測量後,就能獲得測量後的高度了 headerView.measure(0, 0); // 注意:傳0系統會自動去測量View高度 // 獲得測量後的高度 headerViewHeight = headerView.getMeasuredHeight(); Log.i(TAG, "headerViewHeight:" + headerViewHeight); headerView.setPadding(0, -headerViewHeight, 0 ,0); addHeaderView(headerView); initHeaderAnimation(); } private void initBottom() { bottomView = View.inflate(getContext(), R.layout.listview_bottom, null); tvBottomState = bottomView.findViewById(R.id.tv_bottom_state); // 先測量 bottomView.measure(0, 0); // 獲取高度 bottomViewHeight = bottomView.getMeasuredHeight(); bottomView.setPadding(0, -bottomViewHeight, 0, 0); addFooterView(bottomView); } private RotateAnimation upRotateAnimation; private RotateAnimation downRotateAnimation; private void initHeaderAnimation() { upRotateAnimation = new RotateAnimation( 0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); upRotateAnimation.setDuration(500); upRotateAnimation.setFillAfter(true); downRotateAnimation = new RotateAnimation( 180, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); downRotateAnimation.setDuration(500); downRotateAnimation.setFillAfter(true); } /** * 滑動的狀態改變 * @param view * @param scrollState 有三種狀態 * SCROLL_STATE_IDLE 表明 滑動中止狀態相似於手指鬆開UP * SCROLL_STATE_TOUCH_SCROLL 表明滑動觸摸狀態 * SCROLL_STATE_FLING 快速滑動 猛的一滑 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 若是是猛地滑動 或者 手指鬆開UP 才顯示底部佈局View if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) { // 判斷必須是底部的Item的時候 if (getLastVisiblePosition() == (getCount() -1)) { bottomView.setPadding(0, 0, 0, 0); // 回調接口方法 if (null != customUpdateListViewBack) { customUpdateListViewBack.upUpdateListData(); } } } } private int firstVisibleItem; /** * ListView滑動的監聽方法 * @param view 當前ListView * @param firstVisibleItem 當前屏幕的第一個顯示的Item * @param visibleItemCount 當前屏幕顯示的Item數量 * @param totalItemCount 總共Item數量 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.firstVisibleItem = firstVisibleItem; } private int downY; @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) ev.getY(); break; case MotionEvent.ACTION_UP: if (thisUpdateStatusValue == DOWN_UPDATE) { headerView.setPadding(0, -headerViewHeight ,0 ,0); } else { headerView.setPadding(0, 0, 0, 0); thisUpdateStatusValue = PROCESS_UPDATE; updateHeaderState(); } break; case MotionEvent.ACTION_MOVE: int cha = (int) ev.getY() - downY; if (this.firstVisibleItem == 0 && cha > 0) { int paddingTop = -headerViewHeight + cha; // Log.i(TAG, "paddingTop:" + paddingTop); if (thisUpdateStatusValue == PROCESS_UPDATE) { break; } if (paddingTop > 0 && thisUpdateStatusValue == DOWN_UPDATE) { // 準備刷新 Log.i(TAG, "paddingTop:" + paddingTop + ">>>準備刷新"); thisUpdateStatusValue = PLAN_UPDATE; updateHeaderState(); } else if (paddingTop < 0 && thisUpdateStatusValue == PLAN_UPDATE) { // 正在刷新 Log.i(TAG, "paddingTop:" + paddingTop + ">>>正在刷新"); thisUpdateStatusValue = DOWN_UPDATE; updateHeaderState(); } headerView.setPadding(0, paddingTop, 0, 0); } break; default: break; } return super.onTouchEvent(ev); // 不返回ture 而是去調用父類的方法,是保證ListView自身的滑動功能正常 } private void updateHeaderState() { switch (thisUpdateStatusValue) { case DOWN_UPDATE: ivHeaderArrow.startAnimation(downRotateAnimation); tvHeaderState.setText("下拉刷新"); break; case PLAN_UPDATE: ivHeaderArrow.startAnimation(upRotateAnimation); tvHeaderState.setText("準備刷新"); break; case PROCESS_UPDATE: ivHeaderArrow.setVisibility(INVISIBLE); ivHeaderArrow.clearAnimation(); pbHeader.setVisibility(VISIBLE); tvHeaderState.setText("正在刷新中..."); if (null != customUpdateListViewBack) { customUpdateListViewBack.downUpdateListData(); } break; default: break; } } private ICustomUpdateListViewBack customUpdateListViewBack; public void setCallback(ICustomUpdateListViewBack back) { this.customUpdateListViewBack = back; } public void updateHeaderResult() { headerView.setPadding(0, -headerViewHeight, 0, 0); // 狀態還原 ivHeaderArrow.clearAnimation(); tvHeaderState.setText("下拉刷新"); ivHeaderArrow.setVisibility(VISIBLE); pbHeader.setVisibility(INVISIBLE); tvHeaderLastUpdateTime.setText(getThisTiem()); thisUpdateStatusValue = DOWN_UPDATE; } private String getThisTiem() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");// HH:mm:ss // 獲取當前時間 Date date = new Date(System.currentTimeMillis()); return simpleDateFormat.format(date); } public void updateBottomResult() { /*tvBottomState.setText("加載成功"); tvBottomState.setTextColor(Color.GREEN);*/ new android.os.Handler().postDelayed(new Runnable() { @Override public void run() { bottomView.setPadding(0, -bottomViewHeight, 0, 0); } }, 2000); } }
把ProgressBar的風格修改,從白色演變成紅色的形式code
custom_progressbar.xml 文件:orm
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="360" > <shape android:innerRadiusRatio="3" android:shape="ring" android:thicknessRatio="10" android:useLevel="false" > <gradient android:centerColor="#FF6A6A" android:endColor="#FF0000" android:startColor="#FFFFFF" android:type="sweep" /> </shape> </rotate>
如何使用自定義的ListView:xml
<heima.custom.CustomUpdateListView android:id="@+id/custom_listview" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/ll"> </heima.custom.CustomUpdateListView>
當把數據查詢出來後,執行:blog
listView.updateHeaderResult(); // 更新頭部佈局復原
listView.updateBottomResult(); // 更新底部佈局復原
listView.setCallback(new ICustomUpdateListViewBack() { @Override public void downUpdateListData() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... voids) { SystemClock.sleep(3000); mData.add(0, "1最新加載出來的數據"); mData.add(1, "2最新加載出來的數據"); mData.add(2, "3最新加載出來的數據"); mData.add(3, "4最新加載出來的數據"); mData.add(4, "5最新加載出來的數據"); mData.add(5, "6最新加載出來的數據"); return null; } @Override protected void onPostExecute(Void aVoid) { // super.onPostExecute(aVoid); adapter.notifyDataSetChanged(); listView.updateHeaderResult(); } }.execute(new Void[]{}); } @Override public void upUpdateListData() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... voids) { SystemClock.sleep(6000); mData.add("1commonlibrary"); mData.add("2commonlibrary"); mData.add("3commonlibrary"); mData.add("4commonlibrary"); mData.add("5commonlibrary"); mData.add("6commonlibrary"); return null; } @Override protected void onPostExecute(Void aVoid) { // super.onPostExecute(aVoid); adapter.notifyDataSetChanged(); listView.updateBottomResult(); } }.execute(new Void[]{}); } });