Box(視圖組件)如何在多個頁面不同視覺規範下的復用

来源:https://www.cnblogs.com/163yun/archive/2018/05/28/9099666.html
-Advertisement-
Play Games

本文來自 網易雲社區 。 問題描述 Android App中的頁面元素,都是由一個個Box(可以理解成一個個自定義View組件和Widget同級)組成,這些Box可以在不同的頁面、不同的模塊達到復用的效果。但是,現在遇到了一個對於開發復用棘手的問題, A頁面的組件間距和B頁面的組件間距可能不同。 A ...


本文來自 網易雲社區

 

問題描述

Android App中的頁面元素,都是由一個個Box(可以理解成一個個自定義View組件和Widget同級)組成,這些Box可以在不同的頁面、不同的模塊達到復用的效果。但是,現在遇到了一個對於開發復用棘手的問題,

  • A頁面的組件間距和B頁面的組件間距可能不同。

 

 

 

  • A頁面的Box1與Box1間距,和Box1與Box2的間距不一樣。

 

  • Box和Box之間的分割線,有粗有細,有的有左邊距。

 

 

等等還有許多需要動態調整的地方。

然後做這些Box組件,就是為了復用它們,但現在又要對於每個Box的外邊距,如Padding值,進行修改,以適應不同頁面的要求。

方案

方案一

當然首先想到的是每個Box就和View一樣,都有自己的屬性,可以更改它的邊距等屬性,每個Box都有自己的視圖模型ViewModel,可以在ViewModel中添加配置項,如:

public class ViewModel {
    int leftPadding;
    int topPadding;
    int rightPadding;
    int bottomPadding;

    public void setPadding(int left, int top, int right, int bottom
    {...}    
}

//在Box的update方法,根據配置的ViewModel,更新視圖
public void update() {
    if(viewModel != null) {
        setPadding(viewModel.leftPadding, viewModel.topPadding,
        viewModel.rightPadding, viewModel.bottomPadding);
    }
}

這種辦法,確實可行,但是會增加了開發的工作量,增加了代碼的繁瑣程度。畢竟Box的ViewModel應該儘量簡單,ViewModel的存在應該只是讓內部可以配置,而且主要配置的是Box要顯示的數據。

如果每個要復用的Box都要增加這些屬性,然後適配ViewModel的時候還要考慮每一個Box的四周邊距,有時候還不能統一對待。比如一個List存放著同一種類型的Box,但是恰好最後一個Box它需要底邊距不一樣,那豈不還要適配ViewModel的時候還要做序數判斷?

可想而知,開發代價是多高。

方案二

下麵講個實踐中覺得最好的方案: 每個頁面其實就是負責組裝這些Box,因此負責組裝的頁面應該知道Box與Box之間的邊距,分割線等外置屬性。因此這些外置邊距就交給RecyclerView.ItemDecoration類。

首先我們需要知道RecyclerView.ItemDecoration類能幹什麼?

 

假設綠色區域代表的是我們的內容,紅色區域代表我們自己繪製的裝飾,可以看到:

圖1:代表了getItemOffsets(),可以實現類似padding的效果。

圖2:代表了onDraw(),可以實現類似繪製背景的效果,內容在上面。

圖3:代表了onDrawOver(),可以繪製在內容的上面,覆蓋內容。

不過在交給它之前,還需要明確Box的邊界。

  • 藍色框內為可復用的Box
  • 紅色外框是RecycleView中的一個ChildView大小

然後就有瞭如下方案:

約定:要復用的Box內部不應該有距離上下左右的邊距,要保證最小邊界。

Box與Box之間的邊距交給RecyclerView.ItemDecorationgetItemOffset()方法處理。

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    DividerDesc dividerDesc = getDividerDesc(view, parent);
    outRect.set(dividerDesc.leftPadding, dividerDesc.topPadding, dividerDesc.rightPadding, dividerDesc.bottomPadding);
}

我們可以通過getDividerDesc()方法,通過當前View及RecycleView里的Adapter里的ItemType。得知當前View需要在四周有怎麼樣的屬性及分割線。

DividerDesc類可以是這樣:

private static class DividerDesc {

        final boolean needLeftMargin;    //水平分割線左邊距
        final int leftPadding;            //左邊距
        final int topPadding;            //上邊距
        final int rightPadding;            //右邊距
        final int bottomPadding;        //底邊距
        final int dividerHeight;        //底部分割線高度

        DividerDesc(boolean needLeftMargin, int leftPadding, int topPadding, int rightPadding, int bottomPadding, int dividerHeight) {
            this.needLeftMargin = needLeftMargin;
            this.leftPadding = leftPadding;
            this.topPadding = topPadding;
            this.rightPadding = rightPadding;
            this.bottomPadding = bottomPadding;
            this.dividerHeight = dividerHeight;
        }

        DividerDesc() {
            this(false, 0, 0, 0, 0, 0);
        }
    }

RecyclerView.ItemDecorationdrawOver()方法中,畫出需要分割線的地方。

@Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left,right,top,bottom;

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();

            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            DividerDesc dividerDesc = getDividerDesc(child, parent);
            //帶左邊距
            if (dividerDesc.needLeftMargin) {
                left += sThinMarginHorizontal;
            }

            bottom = child.getBottom() + dividerDesc.bottomPadding;
            top = bottom - dividerDesc.dividerHeight;

            if (dividerDesc.dividerHeight == sThinHeight) {
                //畫細線
                mDividerThin.setBounds(left, top, right, bottom);
                mDividerThin.draw(c);
            } else if (dividerDesc.dividerHeight == sThickHeight){
                //畫粗線
                mDividerThick.setBounds(left, top, right, bottom);
                mDividerThick.draw(c);
            }

        }

Demo效果如下:

未加邊距:

 

增加邊距:

 

如果你也遇到了為Box復用而沒法處理繁瑣的視覺調整問題,或者有更好的方案,歡迎一塊討論 :)

參考文章

 

本文已由作者陳柏寧授權網易雲社區發佈,原文鏈接:Box(視圖組件)如何在多個頁面不同視覺規範下的復用


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這是mysql登錄時找不到套接字的問題。 首先需要明白的是,Linux端的mysql server啟動時會開啟一個socket,Linux上的MySQL的客戶端在不使用IP連接時mysql server時,預設也會通過套接字來鏈接mysql server。 1.mysql server的套接字文件。 ...
  • 目錄 一、關於Reducer全排序 1.1、 什麼叫全排序 1.2、 分區的標準是什麼 二、全排序的三種方式 2.1、 一個Reducer 2.2、 自定義分區函數 2.3、 採樣 一、關於Reducer全排序 1.1、什麼叫全排序? 在所有的分區(Reducer)中,KEY都是有序的: 正確舉例: ...
  • 一,打開Xcode >File-->New-->Workspace >AllProject. 二,打開桌面上的AllProject >File >New >Project >iOS >Application >輸入名字:iosProject. 註意:Add to 到我們建立的工作空間中。 三,打開桌 ...
  • 在提交PR的時候,無意間發現了在xcworkspace/xcshareddata中多了一個名為IDEWorkspaceChecks.plist的文件。自己並沒有手動創建這個文件,因此就在網上查了一下,最終對其有個大概的瞭解。 ...
  • 在Android中實現非同步任務機制有兩種,Handler和AsyncTask。優缺點自己百度,推薦使用AsyncTask。 先執行onPreExecute(),然後doInBackground(),當裡面有publishProgress()調用的時候觸發onProgressUpdate(),最後執行 ...
  • 項目中用到AutoCompleteTextView 自動提示功能,如果用自帶的ArrayAdapter就一種樣式,非常醜,而且每一項提示文字過多的話不會自動換行。 所以自己自定義了一個適配器。 效果圖: 1、每一項的佈局文件:(可以自己定義) 這裡用android:lineSpacingExtra= ...
  • 用block封裝最常用的就是網路請求的回調,其實也可以結合category封裝button的按鈕事件,同時利用runtime的對象關聯; UIButton+wkjButton.h 文件 UIButton+wkjButton.m 文件 ...
  • IPv6的簡介 IPv4 和 IPv6的區別就是 IP 地址前者是 .(dot)分割,後者是以 :(冒號)分割的(更多詳細信息自行搜索)。 PS:在使用 IPv6 的熱點時候,記得手機開 飛行模式 哦,保證手機只在 Wi-Fi 下上網,以免手機在連接不到網路時候,會預設跳轉到使用 蜂窩移動網路(即2 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...