91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android 頁面多狀態布局管理的開發

發布時間:2020-10-24 03:29:45 來源:腳本之家 閱讀:202 作者:關注SheHuan 欄目:移動開發

一、現狀

頁面多狀態布局是開發中常見的需求,即頁面在不同狀態需要顯示不同的布局,實現的方式也比較多,最簡單粗暴的方式就是在 XML 中先將不同狀態對應的布局隱藏起來,根據需要改變其可見狀態,如果多個界面公用相同的狀態布局,缺點也很明顯,繁瑣、重復、不優雅等,類似的實現也可以使用 ViewStub,這樣性能會更好些。所以我們要做的就是盡可能避免這些方式所導致的問題,更加高效、優雅的管理不同的狀態布局。

二、目標

我們要實現的 StatusView 要實現的主要功能如下:

  • 可在 Activity、Fragment 、XML 中使用,可作用于XML的根布局View或其子View
  • 支持默認的狀態布局,可進行常規配置
  • 可自定義狀態布局
  • 狀態布局懶加載,僅在初次顯示時初始化

效果預覽如下:

Android 頁面多狀態布局管理的開發

preview

三、實現

這里只對實現過程中一些比較重要的點進行分析。

3.1、初始化

首先有一個最重要的知識點需要明確,XML 布局中的每個View都有其對應的父 View,必然在其父View中都有固定的位置,如果是 Activity 對應的 XML,那XML根布局View的父View是誰呢?其實就是一個 id 為 android.R.id.content 的 View,如果是 Fragment 對應的 XML,那 XML 根布局 View 的父 View 可以通過 fragment.getView() 方法得到。所以現在我們可以得到XML 中每一個View和對應的 LayoutParams 位置信息。

既然有了 View 和其對應的 LayoutParams 位置信息,就可以通過其父 View 將指定的子 View 移除掉,然后將 StatusView 添加到被移除的 View 的位置,進而就可以控制 StatusView 來切換不同的狀態布局。

簡單總結下,就是用 StatusView 替換掉要進行多狀態布局切換的 View,這個 View 可以時 XML 中的任意 View。這也是直接在 Activity、Fragment 中使用 StatusView 要做的核心初始化工作。

那么 StatusView 又是個什么呢?其實就是一個繼承了 FrameLayout 的 ViewGroup,之所以要繼承 FrameLayout,因為 StatusView 此時僅僅是作為父容器存在的,并不關心內部各種狀態 View 的具體情況,所以使用 FrameLayout 就夠了,更有通用性。這樣 StatusView 也就可以在 XML 中使用了

先將上邊這部分內容轉化成代碼:

public class StatusView extends FrameLayout {
  ......
  /**
   * 在 Activity 中的初始化方法,默認頁面的根布局使用多狀態布局
   */
  public static StatusView init(Activity activity) {
    View contentView = ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
    return init(contentView);
  }

  /**
   * 在 Activity 中的初始化方法
   * @param viewId  使用多狀態布局的 ViewId
   */
  public static StatusView init(Activity activity, @IdRes int viewId) {
    View rootView = ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
    View contentView = rootView.findViewById(viewId);
    return init(contentView);
  }

  /**
   * 在Fragment中的初始化方法
   * @param viewId  使用多狀態布局的 ViewId
   */
  public static StatusView init(Fragment fragment, @IdRes int viewId) {
    View rootView = fragment.getView();
    View contentView = null;
    if (rootView != null) {
      contentView = rootView.findViewById(viewId);
    }
    return init(contentView);
  }

  /**
   * 用 StatusView 替換要使用多狀態布局的 View
   */
  private static StatusView init(View contentView) {
    if (contentView == null) {
      throw new RuntimeException("ContentView can not be null!");
    }
    ViewGroup parent = (ViewGroup) contentView.getParent();
    if (parent == null) {
      throw new RuntimeException("ContentView must have a parent view!");
    }
    ViewGroup.LayoutParams lp = contentView.getLayoutParams();
    int index = parent.indexOfChild(contentView);
    parent.removeView(contentView);
    StatusView statusView = new StatusView(contentView.getContext());
    statusView.addView(contentView);
    statusView.setContentView(contentView);
    parent.addView(statusView, index, lp);
    return statusView;
  }
  ......
}

如果在 XML 中使用 StatusView 如何進行初始化呢,自然是通過 onFinishInflate() 方法:

@Override
protected void onFinishInflate() {
  super.onFinishInflate();
  if (getChildCount() == 1) {
    View view = getChildAt(0);
    setContentView(view);
  }
}

3.2、狀態布局的切換

StatusView 默認支持 Loading、Empty、Error 三種狀態布局,加上原始的頁面內容布局,一共四種。切換狀態布局時,我們做法是直接從 StatusView 中移除掉正在顯示的狀態布局,然后添加要顯示的狀態布局:

private void switchStatusView(View statusView) {
  if (statusView == currentView) {
    return;
  }
  removeView(currentView);
  currentView = statusView;
  addView(currentView);
}

3.3、狀態布局的懶加載

在APP使用環境良好的情況下,有些狀態布局可能根本沒有顯示的機會,如果在初始化時一股腦的加載出來自然不可取,影響性能,所以我們要做的就是按需加載,即僅在狀態布局初次顯示時加載并初始化,之后復用即可:

private View generateStatusView(@LayoutRes int layoutId) {
    View statusView = viewArray.get(layoutId);
    if (statusView == null) {
      statusView = inflate(layoutId);
      viewArray.put(layoutId, statusView);
      configStatusView(layoutId, statusView);
    }
    return statusView;
  }

3.4、更自由的用法

一般的多狀態布局管理都會提供默認的 Loading、Empty、Error 三種狀態布局,并可以自定義對應的狀態布局, 并提供對應的開放 api。但這樣會有些局限性,如果有其它業務場景的狀態布局,雖然布局文件可以自定義,但原有的api方法調用起來難免會有違和感,并不友好!所以有必要在常用業務場景的基礎上再提供更加通用的api方法,并不局限于特定的場景。

目前的做法是用狀態布局和對應的索引之間的關系來實現:

// 添加指定索引對應的狀態布局
statusView.setStatusView(int index, @LayoutRes int layoutId)
// 為指定索引的狀態布局設置初次顯示的監聽事件,用來進行狀態布局的相關初始化
statusView.setOnStatusViewConvertListener(int index, StatusViewConvertListener listener)
// 顯示指定索引的狀態布局
statusView.showStatusView(int index)

3.5、注意事項

  • 當 Fragment 布局文件的根 View 使用 StatusView 時,為避免出現的異常問題,建議在 XML 中初始化!
  • 當直接在 Fragment 中使用時,init()方法需要在onCreateView()之后的生命周期方法中執行!
  • 由于StatusView 繼承自 FrameLayout,所以會多一層布局嵌套。

主要的點就這么多了,剩下的就是些屬性配置的內容,其實挺簡單的,更多細節和用法可參考GitHub: StatusView

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

维西| 无锡市| 津南区| 漯河市| 灵山县| 肥西县| 汾阳市| 叶城县| 施秉县| 富裕县| 富顺县| 两当县| 河曲县| 平顶山市| 泽普县| 长海县| 电白县| 米林县| 姜堰市| 宁德市| 新密市| 通州市| 闽清县| 明光市| 南陵县| 科技| 屯留县| 太谷县| 阿勒泰市| 西青区| 曲靖市| 罗田县| 万安县| 南投市| 乐平市| 花莲县| 新河县| 庄浪县| 马鞍山市| 聂荣县| 墨竹工卡县|