項目實戰:硅谷金融APPday03學習筆記—自定義View 及 代碼抽取
不否認努力,繼續加油!
學習整理重點、盲區,筆記如下:乾乾巴巴,麻麻賴賴,一點都不圓潤……
day03
內容
1. 自定義圓形進度條ui-RoundProgress
- 設置圓形進度條,並在中間設置文本,實時更新數據,並設置進度條動態效果,效果圖如下:
- 自定義進度條詳細步驟,以及==自定義屬性的使用== 在 : 自定義View_圓形進度條&自定義屬性的定義和使用.
2. 自定義 ui-MyScrollView,實現頭尾部的下拉、上拉
-
創建 MyScrollView.java 繼承自 ScrollView ;實現,劃到頭還可以繼續滑,劃到尾也可以繼續劃;
-
不需要重寫 onLayout();因爲不是在 Scroll 佈局上下添加布局,而是,在觸摸事件時,對其進行一個重新佈局;
-
當 ScrollView 滑動到邊緣(頭 或 尾時),或者自身動畫結束時,纔對其處理,否則按照 ScrollView 的默認操作 super();
-
滑動原理:手指滑動時,記下佈局處於臨界位置的座標,將座標記錄在 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; }
-
事件衝突處理;這裏當 down 在 ViewPager上時,滑動效果有異常情況;故這裏在 onInterceptTouchEvent() 中,讓父視圖對子視圖的事件進行攔截;
3. Fragment 的抽取
- 將 用到的 多個 Fragment 中公共部分抽取成單獨的基類;
4. 聯網加載數據的4種狀態的抽取及代碼優化
-
面臨的問題;每一個 Fragment 都對應四種狀態,
加載中;聯網獲取數據成功且有數據;聯網獲取數據失敗、聯網獲取數據成功但數據爲空;
對應的四種佈局; -
如果每個 Fragment 都創建這四種狀態,獲得請求狀態後再將多於頁面隱藏,也不是不可以……
-
創建抽象類 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; } }
-
在 基類 中使用 LoadingPage;
-
此時 基類 的回調方法 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;
-
HomeFragment 等 Fragment 中重寫所有 基類 中的抽象方法;
-
需要 BaseFragment 的 onActivityCreated() 中提供 LoadingPage 中聯網操作的調用:
public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); loadingPage.show(); }
-
盲區
- 聲明:本博客根據尚硅谷項目實戰: 硅谷金融.學習整理;
- 對於畫布繪製圓、矩形、圓弧以及確定文本的座標有所疏忽,也就是有多回顧和收穫;
- 對於爲什麼聲明自定義屬性的理解有了深入認識:讓屬性就定義在屬性的 xml 中;對於如何定義並使用自定義屬性有了認識!
- 對於代碼的抽取,不熟悉,花費時間太長了;尤其是對於今天的 LoadingPage 的抽取,真是要了我老命,我現在還沒整明白……
- handler.postDelayed() 失效?難道必須放在主線程?又是一個盲區!handler
彩蛋
-
菜單是我一個 BUG 改了一天的成果;
-
問題如下:
handler.postDelayed(new Runnable() { @Override public void run() { //中間的代碼不執行! } }, 1000);
-
解決辦法就是,拒絕延遲;
new Runnable() { @Override public void run() { } }.run();
-
那麼問題來了,爲什麼 postDelayed() 裏面的
Runnable() 不執行呢??? -
或者說, handler 使用,必須擱在主線程??
-
不知不覺有暴露出一個盲區嗷……
其他筆記
金融App
商城
Android項目實戰—— 商城APP.
新聞
Android項目實戰—— 新聞APP.