自定義 MyScrollView & 聯網加載數據的4種狀態的抽取及代碼優化 — 金融APP03


不否認努力,繼續加油!
學習整理重點、盲區,筆記如下:乾乾巴巴,麻麻賴賴,一點都不圓潤……

day03

內容

1. 自定義圓形進度條ui-RoundProgress

  1. 設置圓形進度條,並在中間設置文本,實時更新數據,並設置進度條動態效果,效果圖如下:
    在這裏插入圖片描述
  2. 自定義進度條詳細步驟,以及==自定義屬性的使用== 在 : 自定義View_圓形進度條&自定義屬性的定義和使用.

2. 自定義 ui-MyScrollView,實現頭尾部的下拉、上拉

  1. 創建 MyScrollView.java 繼承自 ScrollView ;實現,劃到頭還可以繼續滑,劃到尾也可以繼續劃;

  2. 不需要重寫 onLayout();因爲不是在 Scroll 佈局上下添加布局,而是,在觸摸事件時,對其進行一個重新佈局;

  3. 當 ScrollView 滑動到邊緣(頭 或 尾時),或者自身動畫結束時,纔對其處理,否則按照 ScrollView 的默認操作 super();

  4. 滑動原理:手指滑動時,記下佈局處於臨界位置的座標,將座標記錄在 new Rectf 中,並將原佈局放置在,拖動後的高度;回彈時,設置動畫將佈局慢慢的回彈到矩形記錄的原始座標處;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (childView == null || !isFinishAnimation) {
            return super.onTouchEvent(ev);
        }
        int eventY = (int) ev.getY();//獲取當前的y軸座標
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = eventY;
                break;
            case MotionEvent.ACTION_MOVE:
                int dy = eventY - lastY;//微小的移動量
                if (isNeedMove()) {
                    if (normal.isEmpty()) {
                        //記錄了childView的臨界狀態的左、上、右、下
                        normal.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
                    }
                    //重新佈局
                    childView.layout(childView.getLeft(), childView.getTop() + dy / 2, childView.getRight(), childView.getBottom() + dy / 2);
                }
                lastY = eventY;//重新賦值
                break;
            case MotionEvent.ACTION_UP:
                if (isNeedAnimation()) {
                    //使用平移動畫
                    int translateY = childView.getBottom() - normal.bottom;
                    TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, 0, -translateY);
                    translateAnimation.setDuration(200);
                  	translateAnimation.setFillAfter(true);//停留在最終位置上
                    translateAnimation.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                            isFinishAnimation = false;
                        }
                        @Override
                        public void onAnimationEnd(Animation animation) {
                            isFinishAnimation = true;
                            childView.clearAnimation();//清除動畫
                            //重新佈局;避免視圖動畫的屬性沒有移過來;
                            childView.layout(normal.left, normal.top, normal.right, normal.bottom);
                            //清除normal的數據
                            normal.setEmpty();
                        }
                        @Override
                        public void onAnimationRepeat(Animation animation) {
                        }
                    });
                    childView.startAnimation(translateAnimation);
                }
                break;
        }
        return super.onTouchEvent(ev);
    }
    
    private boolean isNeedMove() {
        //獲取子視圖的高度
        int childMeasuredHeight = childView.getMeasuredHeight();
        //獲取佈局的高度
        int scrollViewMeasuredHeight = this.getMeasuredHeight();
        //dy >= 0
        int dy = childMeasuredHeight - scrollViewMeasuredHeight;
        //獲取用戶在y軸方向上的偏移量 (上 + 下 -)
        int scrollY = this.getScrollY();
        if (scrollY <= 0 || scrollY >= dy) {
        //按照我們自定義的MyScrollView的方式處理
        return true;
    	}
    	//其他處在臨界範圍內的,返回false。即表示,仍按照ScrollView的方式處理
        return false;
    }
    
  5. 事件衝突處理;這裏當 down 在 ViewPager上時,滑動效果有異常情況;故這裏在 onInterceptTouchEvent() 中,讓父視圖對子視圖的事件進行攔截;

3. Fragment 的抽取

  1. 將 用到的 多個 Fragment 中公共部分抽取成單獨的基類;

4. 聯網加載數據的4種狀態的抽取及代碼優化

  1. 面臨的問題;每一個 Fragment 都對應四種狀態,加載中;聯網獲取數據成功且有數據;聯網獲取數據失敗、聯網獲取數據成功但數據爲空;對應的四種佈局;

  2. 如果每個 Fragment 都創建這四種狀態,獲得請求狀態後再將多於頁面隱藏,也不是不可以……

  3. 創建抽象類 LoadingPage.java

    //提供4種不同的顯示狀態及當前的狀態
    private static final int PAGE_STATE_LOADING = 1;
    private static final int PAGE_STATE_ERROR = 2;
    private static final int PAGE_STATE_EMPTY = 3;
    private static final int PAGE_STATE_SUCCESS = 4;
    //當前的狀態:默認是加載狀態
    private int page_state_current = 1;
    //2.提供4種不同的界面,並在第3步中初始化
    private View loadingView;
    ……
    //3.1構造器的初始化方法中根據當前的狀態,加載不同的界面顯示
     private void init() {
        params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
        if(loadingView == null){
            loadingView = UIUtils.getXmlView(R.layout.page_loading);
     addView(loadingView, params);
     }
    	if (errorView == null) {
            errorView = UIUtils.getXmlView(R.layout.page_error);
     addView(errorView, params);
     }
        if (emptyView == null) {
            emptyView = UIUtils.getXmlView(R.layout.page_empty);
     addView(emptyView, params);
     }
        //界面中顯示 View 的操作:需要在主線程中執行
        showSafePage();
    }
    //3.2 showSafePage():保證內部的操作在主線程中執行。
    private void showSafePage() {
    	UIUtils.runOnUiThread(new Runnable() {
            @Override
            public void run() {
        		showPage();
     		}
     	});
    }
    //3.3 在如下方法中確定顯示的View
    private void showPage() {
        loadingView.setVisibility(page_state_current == PAGE_STATE_LOADING ? View.VISIBLE : View.GONE);
        errorView.setVisibility(page_state_current == PAGE_STATE_ERROR ? View.VISIBLE : View.GONE);
        emptyView.setVisibility(page_state_current == PAGE_STATE_EMPTY ? View.VISIBLE : View.GONE);
    
        if(successView == null){
            successView = UIUtils.getXmlView(layoutId());
     addView(successView,params);
    	 }
        successView.setVisibility(page_state_current == PAGE_STATE_SUCCESS ? View.VISIBLE : View.GONE);
    }
    //4.LadingPage中要顯示哪個View,取決於聯網獲取數據的情況。鑑於不同頁面都有聯網的需求,進而將聯網操作聲明在LoadingPage中。
    //定義如下的方法,聯網下載數據,並決定了當前的頁面狀態(加載、失敗、空數據、成功)
    public void show(){
        if(page_state_current != PAGE_STATE_LOADING){
            page_state_current = PAGE_STATE_LOADING;
     	}
      	String url = url();
        if(TextUtils.isEmpty(url)){
            resultState = ResultState.SUCCESS;
            resultState.setContent("");
         	loadingPage();
     	}else{
            client.get(url(),params(),new AsyncHttpResponseHandler(){...}
       	}
    }
    //5.使用到的枚舉類:
    public enum ResultState {
        ERROR(2), EMPTY(3), SUCCESS(4);
    	private int state;
    	ResultState(int state) {
       		this.state = state;
    	}
    	private String content;//保存的內部數據
    	public String getContent() {
          	return content;
    	}
    	public void setContent(String content) {
    	     this.content = content;
    	}
    }
    
  4. 在 基類 中使用 LoadingPage;

    1. 此時 基類 的回調方法 onCreateView() 將 LoadingPage 作爲返回值,實例化 LoadingPage ,並重寫所有的抽象方法。

      loadingPage = new LoadingPage(container.getContext()){
          @Override
          public int layoutId() {
              return getLayoutId();
      	 }
      
      	 @Override
      	 protected void onSuccess(ResultState resultState, View successView) {
      		findViews(view_success);
      	 	initTitle();
      	 	initData(resultState.getContent());
      	 }
      
      	 @Override
      	 protected RequestParams params() {
      	     return getParams();
      	 }
      	@Override
      	protected String url() {
      	     return getUrl();
      	}
      };
      return loadingPage;
      
    2. HomeFragment 等 Fragment 中重寫所有 基類 中的抽象方法;

    3. 需要 BaseFragment 的 onActivityCreated() 中提供 LoadingPage 中聯網操作的調用:

      public void onActivityCreated(@Nullable Bundle savedInstanceState) {
          super.onActivityCreated(savedInstanceState);
          loadingPage.show();
      }
      

      LoadingPage的執行流程by宋紅康(尚硅谷老師)

盲區

  1. 聲明:本博客根據尚硅谷項目實戰: 硅谷金融.學習整理;
  2. 對於畫布繪製圓、矩形、圓弧以及確定文本的座標有所疏忽,也就是有多回顧和收穫;
  3. 對於爲什麼聲明自定義屬性的理解有了深入認識:讓屬性就定義在屬性的 xml 中;對於如何定義並使用自定義屬性有了認識!
  4. 對於代碼的抽取,不熟悉,花費時間太長了;尤其是對於今天的 LoadingPage 的抽取,真是要了我老命,我現在還沒整明白……
  5. handler.postDelayed() 失效?難道必須放在主線程?又是一個盲區!handler

彩蛋

  1. 菜單是我一個 BUG 改了一天的成果;

  2. 問題如下:

    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
        //中間的代碼不執行!
            }
    	}, 1000);
    
  3. 解決辦法就是,拒絕延遲;

    new Runnable() {
        @Override
        public void run() {
            }
    }.run();
    
  4. 那麼問題來了,爲什麼 postDelayed() 裏面的
    Runnable() 不執行呢???

  5. 或者說, handler 使用,必須擱在主線程??

  6. 不知不覺有暴露出一個盲區嗷……

其他筆記

金融App

  1. 金融APP01—頁面架構.
  2. 金融APP02—主頁及工具類創建

商城

Android項目實戰—— 商城APP.

新聞

Android項目實戰—— 新聞APP.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章