編輯:關於Android編程
大多App中的一個必備功能:用listView實現下拉刷新和上拉加載,其實有很多大牛都寫了類似的Blog,但我還想記錄一下,梳理自己的思路,而且我會想之前寫的輪播圖博客一樣,我的重點是在如何寫的思路,不願直接貼代碼,想看代碼的直接看文章最下面吧 :)
如上gif動圖所示,接下來我們要完成下拉刷新的實現。
1.完成listView的頭布局
結合了幀布局和線性布局
<framelayout android:layout_height="50dp" android:layout_margin="5dp" android:layout_width="50dp"> </framelayout>
2. 自定義ProgressBar
這裡紅色漸變的圓圈為自定義的xml<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
1. 自定義RefreshListView繼承 Listview
繼承3個構造方法,並在init()中寫initHeaderView()方法,初始化頭布局
public PullToRefreshListView(Context context) { super(context); init() } public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init() } public PullToRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init() }
2. 初始化頭布局並隱藏
默認讓頭布局隱藏,主要就是測得我們剛寫的xml文件頭布局的高度,用padding將它隱藏起來即可,再設置listView添加頭布局
/** * 初始化頭布局 */ private void initHeaderView() { mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null); mArrowView = mHeaderView.findViewById(R.id.iv_arrow); pb = (ProgressBar) mHeaderView.findViewById(R.id.pb); mTitleText = (TextView) mHeaderView.findViewById(R.id.tv_title); mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh); // 提前手動測量寬高 mHeaderView.measure(0, 0);// 按照設置的規則測量 mHeaderViewHeight = mHeaderView.getMeasuredHeight(); System.out.println(" measuredHeight: " + mHeaderViewHeight); // 設置內邊距, 可以隱藏當前控件 , -自身高度 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); // 在設置數據適配器之前執行添加 頭布局/腳布局 的方法. addHeaderView(mHeaderView); }
3. 滑動事件處理(重點)
萬事俱備,頭布局也已經隱藏掉,接下來我們想通過手指的滑動,讓頭布局顯示出來,這時就涉及到 onTouchEvent 事件。
public static final int PULL_TO_REFRESH = 0;// 下拉刷新狀態 public static final int RELEASE_REFRESH = 1;// 釋放刷新狀態 public static final int REFRESHING = 2; // 刷新中狀態 @Override public boolean onTouchEvent(MotionEvent ev) { // 判斷滑動距離, 給Header設置paddingTop switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downY = ev.getY(); System.out.println("downY: " + downY); break; case MotionEvent.ACTION_MOVE: moveY = ev.getY(); System.out.println("moveY: " + moveY); // 如果是正在刷新中, 就執行父類的處理 if(currentState == REFRESHING){ return super.onTouchEvent(ev); } float offset = moveY - downY; // 移動的偏移量 // 只有 偏移量>0, 並且當前第一個可見條目索引是0, 才放大頭部 if(offset > 0 && getFirstVisiblePosition() == 0){ //int paddingTop = -自身高度 + 偏移量 int paddingTop = (int) (- mHeaderViewHeight + offset); mHeaderView.setPadding(0, paddingTop, 0, 0); if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 頭布局完全顯示 System.out.println("切換成釋放刷新模式: " + paddingTop); // 切換成釋放刷新模式 currentState = RELEASE_REFRESH; updateHeader(); // 根據最新的狀態值更新頭布局內容 }else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 頭布局不完全顯示 System.out.println("切換成下拉刷新模式: " + paddingTop); // 切換成下拉刷新模式 currentState = PULL_TO_REFRESH; updateHeader(); // 根據最新的狀態值更新頭布局內容 } return true; // 當前事件被我們處理並消費 } break; case MotionEvent.ACTION_UP: // 根據剛剛設置狀態 if(currentState == PULL_TO_REFRESH){ // - paddingTop < 0 不完全顯示, 恢復 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); }else if(currentState == RELEASE_REFRESH){ // - paddingTop >= 0 完全顯示, 執行正在刷新... mHeaderView.setPadding(0, 0, 0, 0); currentState = REFRESHING; updateHeader(); } break; default: break; } return super.onTouchEvent(ev); }
首先要了解三個狀態:
(1)PULL_TO_REFRESH下拉刷新:拉的過程中會進入到“釋放刷新”,要是拉到一半就放手即未進入“釋放刷新”,就不會去刷新數據。
(2)RELEASE_REFRESH釋放刷新:拉到了一定距離,只要放手就會進入”刷新中”狀態。
(3)REFRESHING刷新中:狀態為正在刷新,此時就可以去請求數據了。
其次從 Down 、Move、Up這三個動作來分析邏輯:
(1) Down: 即手指剛剛點下去,在這裡只需要獲取當前的 y坐標值(上下滑動,只在意Y值)
(2) Move: 即手指滑動的過程,在這裡需要根據移動的距離 offset 和 當前的狀態 來 顯示出頭布局 並 改變狀態。
1. 當前狀態為“正在刷新”,即不處理(也不看第二點了,return出去)。 2. 只有 偏移量 offset>0, 並且當前第一個可見條目索引是0, 才顯示頭布局。 paddingTop : 需要隱藏的距離= - 自身高度 + 偏移量。 2.1 (paddingTop >= 0 且 狀態不等於“釋放刷新”):頭布局完全顯示,此時狀態改為“釋放刷新” 2.2 (paddingTop < 0 且 狀態不等於“釋放刷新”): 頭布局不完全顯示,此時狀態改為“下拉刷新” 2.3 其余情況不管:
(3) Up: 即手指抬起,根據Move過程後改變的狀態來判斷是否進行刷新的邏輯 和 狀態修改。
1.狀態為”下拉刷新“ :即收起頭布局,不需要進行刷新的邏輯。 2.狀態為”釋放刷新“ :顯示完全頭布局,修改狀態為”正在刷新“,進行刷新的邏輯。
(以上代碼中的refreshState方法留到下下一點講,其實就是一個UI的變化)
(監聽對象mListener 采取回調,第六點講解)
4.下拉刷新箭頭旋轉動畫
以上完成後,接下來完善一下頭布局的動畫,需要在init()中初始化initAnimation()
/** * 初始化頭布局的動畫 */ private void initAnimation() { // 向上轉, 圍繞著自己的中心, 逆時針旋轉0 -> -180. rotateUpAnim = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateUpAnim.setDuration(300); rotateUpAnim.setFillAfter(true); // 動畫停留在結束位置 // 向下轉, 圍繞著自己的中心, 逆時針旋轉 -180 -> -360 rotateDownAnim = new RotateAnimation(-180f, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateDownAnim.setDuration(300); rotateDownAnim.setFillAfter(true); // 動畫停留在結束位置 }
5. refreshState()修改頭布局中UI的變化
第五點同第四點都是有關於UI的變化,比較簡單,雖然在onTouchevent中根據不同情況修改了 頭布局的顯示多少 ,但是頭布局中的小控件都有相應的動畫或改變,需要抽取一個方法更細致的展示。
/** * 根據當前狀態刷新界面 */ private void refreshState() { switch (mCurrentState) { case STATE_PULL_TO_REFRESH: tvTitle.setText("下拉刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); ivArrow.startAnimation(animDown); break; case STATE_RELEASE_TO_REFRESH: tvTitle.setText("松開刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); ivArrow.startAnimation(animUp); break; case STATE_REFRESHING: tvTitle.setText("正在刷新..."); ivArrow.clearAnimation();// 清除箭頭動畫,否則無法隱藏 pbProgress.setVisibility(View.VISIBLE); ivArrow.setVisibility(View.INVISIBLE); break; default: break; } }
6.下拉刷新監聽(重點!!!!!!回調!!!!)
運用回調,使在手指抬起Up後,能夠調用方法,進行刷新的邏輯。
(1)下拉刷新的回調接口
public interface OnRefreshListener { public void onRefresh(); }
(2)暴露接口,設置監聽
public void setOnRefreshListener(OnRefreshListener listener) { mListener = listener; }
(3)定義成員變量,接收監聽對象
private OnRefreshListener mListener;
(4) 在合適的地方進行回調
(之前第三點中onTouchevent事件中進行回調)
if (mListener != null) { mListener.onRefresh(); }
5. 前端界面設置回調(並非RefreshListView類,是使用了該listView類中寫!!!)
lvList.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { // 刷新數據 getDataFromServer(); } });
7. 刷新結束,收起控件
最後一步,刷新完成,恢復控件原始位置,狀態恢復,標題恢復,刷新完成的條件下更新時間。
public void onRefreshComplete(boolean success) { mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); mCurrentState = STATE_PULL_TO_REFRESH; tvTitle.setText("下拉刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); if (success) {// 只有刷新成功之後才更新時間 setCurrentTime(); } }
以上就是所有的邏輯,有關於”下拉刷新“的重點,其實各個細節都會有耦合的部分,盡量抽取出一個個小模塊進行講解,其中最重要的就是 第三點.滑動事件處理 和 第六點.下拉刷新監聽回調。大致這幾個步驟,講的還是蠻細的,適合入門吧,也不知道這樣分開講好不好,總之,希望對你們有幫助 :)
(全部代碼等上拉加載寫完後 貼出來:)
首先什麼是Transition? 安卓5.0中Activity和Fragment變換是建立在名叫Transitions的安卓新特性之上的。這個誕生於4.4的transit
Android Studio系列-HelloWorld前言Hello 各位,小巫這裡要記錄一些關於如何使用Android Studio開發Android app,這一篇是
因為案例比較簡單,所以簡單用AndroidApplication -> Game -> Stage 搭建框架 一、主入口,無特殊 復制代碼 代碼如下: pub
關於Android4.4的圖片路徑獲取,如果回來的Uri的格式有兩種 content://com.android.providers.med