們每天都會接觸到各式各樣的表單頁面,其中包括登錄賬號、填寫單據、購買產品、發布信息等。它作為所有產品里最常見也最普通的設計元素,往往最容易被忽略它們該有的體驗細節,而引起用戶使用中的挫敗感。本篇文章主要通過業務場景和用戶行為的角度來分析,如何打造體驗友好的表單界面。
一、交互設計下的業務場景與用戶行為
二、基于業務場景選擇適合的布局模式
三、基于用戶信任行為的設計原則
四、總結
交互設計的本質即人與機器在一定的業務場景下的信息傳遞、相互作用,我們設計產品可以理解為是在設計或者說培養對應場景下的『機器能力』,良好的『機器能力』可以更好的根據『用戶行為/心理』來交流與反饋,使得系統更懂用戶,更符合自然人際之間的交互行為。
那么如何打造更友好的表單界面,讓它更好的與用戶相互交流,我們需要從業務場景和用戶行為出發做分析,反推機器能力的設計目標。
針對不同的場景需求下的操作路徑,需要有相對合適的布局模式來支持高效操作和錄入,相應的有以下四種類型:
多列流式布局的表單,較多運用于需要大批量數據錄入的場景,此時的多列流式布局可以保證在各類終端上的空間最大化利用,同時該布局節省業務設計與開發成本。
▽下圖為高級表單,是多列流式布局的一種拓展設計,運用場景更廣。
在多列流式布局表單中,用戶需要使用Z型路徑進行掃描和閱讀,在節省空間的同時也犧牲了用戶的瀏覽與理解速度;但是對于單據字段數量較少且內容層級與類型較單一的情況,如果一列展示的話,清晰的瀏覽線可以提供快速且明確的瀏覽路線,提高操作效率。
▽在人員類基礎資料界面的維護場景中,少量的字段更加適合單列布局模式。
上述的多列流式布局或者單列布局,都是針對所有表單的通用模式,這兩種模式通用且開發設計成本較低,但是除此之外,細分場景下我們不應該忽視很多特殊表單的設計需求與用戶習慣,這類特殊表單在線下有著固化的實體樣式,此時采用卡片(擬物)化模式可以讓用戶快速找到閱讀習慣。
▽憑證類的表單,較多直接采用擬物化的布局模式,迎合財務人員的閱讀習慣,提高處理效率。
而對于普通用戶日常生活的使用場景中也特別常見將表單(或其中部分內容)設計成卡片(擬物)模式,例如很多實體票據中的電影票、高鐵票、飛機票等,習慣性的閱讀模式帶來空間利用與閱讀效率的更佳。
▽飛豬中機票訂單填寫界面,將部分內容擬物化設計,方便展示且節省空間。
另外一類布局模式就是步驟向導型布局,主要運用于以下三種情況:
當你在設計一個超長的復雜表單或者是一個無法簡化的流程任務時,此時步驟型向導布局可以降低復雜性,而從提高可用性可易用性。
另外很多時候,復雜表單內容中具有前后依賴關系,在一張表單中,用戶需要嚴格先執行前面任務,才能允許填寫后續任務,而步驟向導恰好可以通過細分的操作流讓用戶分步執行,降低學習曲成本與操作風險。
▽在GitHub的注冊操作中,采用步驟向導型布局,分步完成注冊任務。
對于涉及款項轉賬、賬戶安全等此類的重要表單,此時的填寫成本與風險較高,即使內容較少也推薦試用步驟向導型布局,避免后續出錯所帶來的不必要麻煩。
▽在富途證劵中,在轉入轉出業務中,細分操作流,每一步關注各自的重要信息,降低填寫表單風險。
以下需要談論的是用戶行為下的信任特征所帶來的設計準則。
從用戶行為/心理的角度看,信任是人與人之間交往的先決條件,只有獲得彼此的信任,兩個人之間的對話才能自然和愉悅。
那么對于用戶與產品之間,信任的行為準則依然存在,信任意味著更多耐心和理解,同時帶來了更多的探索欲與試錯接受度。用戶足夠信任產品才能讓表單的錄入變得更加流程與愉悅。
當設計者拿到一份需求文檔時,面對眾多的錄入字段,首要事項就是明確用戶填寫該表單的目的,需要確保終端用戶看到的表單上都是真正需要的字段,因為每增加一個額外的非必要字段都會影響填寫效率/轉化率。要時刻思考我們需要從用戶那里得到哪些必要信息以及怎么進行高效地數據利用。
而針對非必填但是又有用的字段內容,可以采取的策略是在初次交互時并不強制填寫,而讓用戶選填以及在后續合適的場景中重新喚起該內容項的信息收集,適時且自然,保證初始交互簡單可控。
▽Google 日歷中,當創建待辦事項時,只彈出彈窗顯示必要字段,用戶可以自行選擇直接保存或者填寫更多內容項,該設計極大提高快速創建待辦的效率。
很多時候的表單內容多且繁雜,其中的信息項如果只是常規羅列,很容易顯得混亂和難以閱讀,易用性較差,使用戶產生抵觸情緒,久而久之就厭惡使用該產品。
關聯信息、合理組織內容,利用區塊、間隔來進行布局區分;按照內容的類別、層次、順序等進行組的劃分,提高表單的清晰性與可讀性。
▽DELTA中乘客的基本信息和聯系信息為兩項獨立的重要項,分組展示,同時對于每個組內的細分信息也進行分行布局,例如姓名類信息獨行,飛行計劃與號碼、性別與年月日、編號各自獨占一行,清晰易填。
相關閱讀:《設計法則: Fitts’ Law / 菲茨定律(費茨法則)》
表單填寫長久以來被詬病的一點即是其繁瑣性,減少用戶操作以幫助用戶提高輸入效率,從而提高轉化率/效率。
預判默認值
針對一些常規內容,表單場景可以通過獲取用戶資料/個人信息/歷史記錄等來預判默認值。
最為典型的即是當我填寫一張請假單時,系統應該自動代入我的部門、姓名、工號等信息,該類元素的繼承性較強,用戶很少情況會去變更。同樣在性別、地址、轉賬人等都可以使用『預判默認值』的方法來減少用戶操作成本。
軟件應該保持智能的模式,在適當的風險點內,幫使用者選擇決策。
硬件設備自動獲取
在人機交互層面,除了不斷思考軟件能給予用戶的體驗外還需要探索,硬件設備可以提供哪些幫助?
該情況最常見例子即”系統定位”,任何表單或者場景,只要涉及地址選擇,基本現在所以產品都會設置系統快速定位用戶設備所在位置,自動錄入。
而較高級的做法則是更深度挖掘機器的能力,也就是大家常說的『語音識別』與『圖像識別』,當用戶需要錄入一張發票時,系統提前告知用戶拍攝實體發票/二維碼,進行圖像識別后,批量代入數據,極大減少用戶思考與操作成本。
自動生成
另外一些字段,系統可以根據規則來自動生成相應內容,例如編碼、序號等,此時一般不允許用戶進行更改,而針對另外一些用戶自主性更強的字段,可以采用自動生成+建議錄入的方式來幫助用戶。
▽Safari瀏覽器下,密碼字段自動生成強密碼,若用戶使用,則瀏覽器會自動記錄,下次自動填充。
在用戶面對繁雜的表單填寫時,難免會遇到困惑,此時為了防止用戶出錯或者中途流失,我們需要適時給予用戶幫助和導引。
其內容的編寫上可以有以下指導方針:
1. 告訴用戶該項指什么,適用于術語類字段;
2. 告訴用戶為什么要填寫該項,填寫了有什么好處;
此類多見于需要收集用戶信息的場景,例如需要登記用戶手機(非必錄項),此時告知用戶”填寫手機號方便找回密碼”,自然而然的用戶會有更高的意愿去填寫。
3. 告訴用戶要怎么填寫該項,即填寫規則。
在形式上,可以選擇:
1. 靈活利用字段掩碼進行填寫提醒;
▽大韓航空KOREAN AIR中,錄入框里的灰字即字段掩碼,其中有填寫幫助和示例。
字段掩碼可以很好地節省空間,同時可以快速的幫助用戶降低思考成本。
2. 標簽Tips的有效示意;
▽Trip平臺中,填寫姓名項時,出現Tips氣泡浮窗,出現護照卡片圖例示意,形象易懂。
3. 全局性說明規范整體填寫
當表單中多個字段需要進行關聯性說明時,此時需要跳出單個字段的引導,啟用全局性說明進行填寫幫助。
▽Expedia智游網的訂單填寫頁面中,頂部提醒欄對多個字段進行說明示意。
1. 更加貼心;
2. 更加放心(安全性);
▽Google Pay中添加付款方式,特別注釋說明不會泄露用戶的付款信息。
3. 適時的驚喜與成就感。
遵循業務場景和用戶行為分析來反推機器能力的設計方法論不僅適用與表單的設計中,任何交互元素下的設計方式制定都可以從以上兩點出發,來指導設計者打造更加友好的產品體驗。
本文由 @小偉同學 原創發布于人人都是產品經理。未經許可,禁止轉載
題圖來自Unsplash,基于CC0協議
位好,在前面有一期作品中,我們曾經在Excel中實現了萬年歷的制作,當時也有很多網友看過我的那期頭條文章或頭條視頻,附帶有評價和收藏,在此向你們表示感謝。
另外,有個名叫“斑斕虎zcy”的粉絲評論說我做的萬年歷如果含有農歷就完美了,對這位粉絲的提議我欣然接受,另外,雖然我沒采用“斑斕虎zcy”粉絲提供的公歷轉農歷的關鍵技術模版,但對“斑斕虎zcy”粉絲的熱心表示深深的謝意!
剛剛上一期,我為大家分享了自己弄的公歷<---->農歷互轉的技術與方法,也有很多網友看了這期頭條文章或視頻,說明大家對這個方法也很認可的,謝謝各位啦!接下來,我準備用兩種公歷<---->農歷互轉的方法實現帶農歷的萬年歷設計吧!為了區分起見,我們暫定本期的題目為“頭條文章--Excel中帶農歷的萬年歷設計方法一”、下期作品的題目為“頭條文章--Excel中帶農歷的萬年歷設計方法二”。
本期,我們先來用第一種方法實現吧。
一、Excel前端帶農歷萬年歷界面設計
關于界面的設計,這里和上次那一期萬年歷的界面一樣,這里不做過多描述,這里就只以截圖直接呈現給各位吧。如下圖所示
圖1 帶農歷的萬年歷界面
二、用方法一實現帶農歷萬年歷的功能代碼
模塊1中代碼如下:
'強勢自定義“公歷”<---->“農歷”互轉函數
'原創:互聯網
'修正:號作者“跟我學Office高級辦公應用” 2019/10/12
'---農歷數據定義---
'先以 Hexadecimal_To_Binary 函數還原成長度為 18 的字符串,其定義如下:
'前12個字節代表1-12月:1為大月,0為小月;壓縮成十六進制(1-3位)
'第13位為閏月的情況,1為大月30天,0為小月29天;(4位)
'第14位為閏月的月份,如果不是閏月為0,否則給出月份(5位)
'最后4位為當年農歷新年的公歷日期,如0131代表1月31日;當作數值轉十六進制(6-7位)
'定義如下農歷(陰歷)日期常量(1899~2100,共202年,但是事實上我們只需要用到1900~2100這201年即可)
Private Const ylData = "AB500D2,4BD0883," _
& "4AE00DB,A5700D0,54D0581,D2600D8,D9500CC,655147D,56A00D5,9AD00CA,55D027A,4AE00D2," _
& "A5B0682,A4D00DA,D2500CE,D25157E,B5500D6,56A00CC,ADA027B,95B00D3,49717C9,49B00DC," _
& "A4B00D0,B4B0580,6A500D8,6D400CD,AB5147C,2B600D5,95700CA,52F027B,49700D2,6560682," _
& "D4A00D9,EA500CE,6A9157E,5AD00D6,2B600CC,86E137C,92E00D3,C8D1783,C9500DB,D4A00D0," _
& "D8A167F,B5500D7,56A00CD,A5B147D,25D00D5,92D00CA,D2B027A,A9500D2,B550781,6CA00D9," _
& "B5500CE,535157F,4DA00D6,A5B00CB,457037C,52B00D4,A9A0883,E9500DA,6AA00D0,AEA0680," _
& "AB500D7,4B600CD,AAE047D,A5700D5,52600CA,F260379,D9500D1,5B50782,56A00D9,96D00CE," _
& "4DD057F,4AD00D7,A4D00CB,D4D047B,D2500D3,D550883,B5400DA,B6A00CF,95A1680,95B00D8," _
& "49B00CD,A97047D,A4B00D5,B270ACA,6A500DC,6D400D1,AF40681,AB600D9,93700CE,4AF057F," _
& "49700D7,64B00CC,74A037B,EA500D2,6B50883,5AC00DB,AB600CF,96D0580,92E00D8,C9600CD," _
& "D95047C,D4A00D4,DA500C9,755027A,56A00D1,ABB0781,25D00DA,92D00CF,CAB057E,A9500D6," _
& "B4A00CB,BAA047B,B5500D2,55D0983,4BA00DB,A5B00D0,5171680,52B00D8,A9300CD,795047D," _
& "6AA00D4,AD500C9,5B5027A,4B600D2,96E0681,A4E00D9,D2600CE,EA6057E,D5300D5,5AA00CB," _
& "76A037B,96D00D3,4AB0B83,4AD00DB,A4D00D0,D0B1680,D2500D7,D5200CC,DD4057C,B5A00D4," _
& "56D00C9,55B027A,49B00D2,A570782,A4B00D9,AA500CE,B25157E,6D200D6,ADA00CA,4B6137B," _
& "93700D3,49F08C9,49700DB,64B00D0,68A1680,EA500D7,6AA00CC,A6C147C,AAE00D4,92E00CA," _
& "D2E0379,C9600D1,D550781,D4A00D9,DA400CD,5D5057E,56A00D6,A6C00CB,55D047B,52D00D3," _
& "A9B0883,A9500DB,B4A00CF,B6A067F,AD500D7,55A00CD,ABA047C,A5A00D4,52B00CA,B27037A," _
& "69300D1,7330781,6AA00D9,AD500CE,4B5157E,4B600D6,A5700CB,54E047C,D1600D2,E960882," _
& "D5200DA,DAA00CF,6AA167F,56D00D7,4AE00CD,A9D047D,A2D00D4,D1500C9,F250279,D5200D1"
'定義農歷 (陰歷)每月的漢字大寫日期“天”
Private Const ylMd0 = "初一初二初三初四初五初六初七初八初九初十十一十二十三十四十五" _
& "十六十七十八十九二十廿一廿二廿三廿四廿五廿六廿七廿八廿九三十 "
'定義農歷 (陰歷)一年中的漢字大寫日期“月”
Private Const ylMn0 = "正二三四五六七八九十冬臘"
'定義農歷 (陰歷)年中的“天干”(如:甲乙丙丁......等)
Private Const ylTianGan0 = "甲乙丙丁戊已庚辛壬癸"
'定義農歷 (陰歷)年中的“地支”(如:子丑寅卯辰......等)
Private Const ylDiZhi0 = "子丑寅卯辰巳午未申酉戌亥"
'定義農歷 (陰歷)年中的“屬相”(如:鼠牛虎兔龍......等)
Private Const ylShu0 = "鼠牛虎兔龍蛇馬羊猴雞狗豬"
Public shp_year_select As Shape, y '定義公有全局變量年份選擇組合框shp_year_select和用于存儲選擇的年份變量y,以便所有的過程都可以調用和回傳數據
Sub Run_Fill_Calender() '運行填充日歷
[b4].Select
n = shp_year_select.ControlFormat.Value
y = shp_year_select.ControlFormat.List(n)
[O1] = y & " 年歷" & "[" & Mid(GetYLDate(y & "-6-1"), 4, 6) & "]"
Fill_Calender_Datas '調用“填充日歷數據”過程
[a65535] = y '將選擇過的年份存儲在單元格"A65535"中
End Sub
Sub Fill_Calender_Datas() '填充日歷數據
Dim rg(1 To 12) As Range '定義12個元素的的范圍區域對象數組
'為區域對象數組的每個區域對象元素對象指派這12個區域對象具體的實體
Set rg(1) = [b5:h10]: Set rg(2) = [j5:p10]: Set rg(3) = [r5:x10]: Set rg(4) = [z5:af10]
Set rg(5) = [b15:h20]: Set rg(6) = [j15:p20]: Set rg(7) = [r15:x20]: Set rg(8) = [z15:af20]
Set rg(9) = [b25:h30]: Set rg(10) = [j25:p30]: Set rg(11) = [r25:x30]: Set rg(12) = [z25:af30]
For i = 1 To 12
Select Case i
Case 1, 3, 5, 7, 8, 10, 12: days_31 y, i, rg(i)
Case 4, 6, 9, 11: days_30 y, i, rg(i)
Case 2: days_29_Or_28 y, i, rg(i)
End Select
Next
End Sub
Sub Erse_Calender_Datas() '清空日歷數據
Dim rg As Range
Set rg = [5:10,15:20,25:30]
[b4].Select
rg.ClearContents
[O1] = "---- 年歷[-----年]"
yr = Year(Date)
'以下是定位當今日期的年份在表單組合框中顯示
For i = 1 To shp_year_select.ControlFormat.ListCount
If yr = Val(shp_year_select.ControlFormat.List(i)) Then
n = i
Exit For
End If
Next
shp_year_select.ControlFormat.ListIndex = n
End Sub
Sub days_31(y, m, r As Range) '月大--31天
Dim da As Date, d
r.ClearContents
week_str = "日一二三四五六"
d = 1
da = CDate(y & "-" & m & "-" & d) '將字符串動態轉換為真正的日期
ws = Mid(Format(da, "[$-804]aaaa"), 3) '從轉換為星期XX的字符串中提取大寫星期幾的漢字保存在ws中
First_Day_Pos_In_Week_Area = InStr(week_str, ws) '每月初始的1號在日歷星期區域的定位位置
For d = 1 To 31
da = CDate(y & "-" & m & "-" & d) '將字符串動態轉換為真正的日期
ws = Mid(Format(da, "[$-804]aaaa"), 3) '從轉換為星期XX的字符串中提取大寫星期幾的漢字保存在ws中
Other_Day_Pos_In_Week_Area = InStr(week_str, ws)
'實際的每月的號數應該加上每月初始的1號在日歷星期區域的定位位置減去1“”d + (First_Day_Pos_In_Week_Area - 1),為了在第7個位置仍然將該號 _
數放在該行,所以還得再減去1“d + (First_Day_Pos_In_Week_Area - 1) - 1”,然后再除7取整,同時乘以7后加上該號數在日歷中星期區域的實際列數 _
位置,即可得到該號數在日歷區域的設計位置
p = Int((d + (First_Day_Pos_In_Week_Area - 1) - 1) / 7) * 7 + Other_Day_Pos_In_Week_Area
yl_md = Right(GetYLDate(da), 4) '調用轉農歷(陰歷)函數,取后四個漢字月日日期字符
yl_m = Left(yl_md, 2) '拆解陰歷月日中的月份
yl_d = Right(yl_md, 2) '拆解陰歷月日中的日子
If yl_d = "初一" Then yl_d = yl_m '若拆解的日子是“初一”,則即刻用該月的月份替代該陰歷月份的首個日子
r(p) = d & Chr(10) & yl_d '將公歷日期和對應的農歷日期合在一起填入到p處正確位置
If da = Date Then r(p).Select '若選擇年份后不斷瞬時生成的日期da和現在的日期匹配,則將當前填充的日期單元格選擇成活動狀態
Next
End Sub
Sub days_30(y, m, r As Range) '月小--30天
Dim da As Date, d
r.ClearContents
week_str = "日一二三四五六"
d = 1
da = CDate(y & "-" & m & "-" & d) '將字符串動態轉換為真正的日期
ws = Mid(Format(da, "[$-804]aaaa"), 3) '從轉換為星期XX的字符串中提取大寫星期幾的漢字保存在ws中
First_Day_Pos_In_Week_Area = InStr(week_str, ws) '每月初始的1號在日歷星期區域的定位位置
For d = 1 To 30
da = CDate(y & "-" & m & "-" & d) '將字符串動態轉換為真正的日期
ws = Mid(Format(da, "[$-804]aaaa"), 3) '從轉換為星期XX的字符串中提取大寫星期幾的漢字保存在ws中
Other_Day_Pos_In_Week_Area = InStr(week_str, ws)
'實際的每月的號數應該加上每月初始的1號在日歷星期區域的定位位置減去1“”d + (First_Day_Pos_In_Week_Area - 1),為了在第7個位置仍然將該號 _
數放在該行,所以還得再減去1“d + (First_Day_Pos_In_Week_Area - 1) - 1”,然后再除7取整,同時乘以7后加上該號數在日歷中星期區域的實際列數 _
位置,即可得到該號數在日歷區域的設計位置
p = Int((d + (First_Day_Pos_In_Week_Area - 1) - 1) / 7) * 7 + Other_Day_Pos_In_Week_Area
yl_md = Right(GetYLDate(da), 4) '調用轉農歷(陰歷)函數,取后四個漢字月日日期字符
yl_m = Left(yl_md, 2) '拆解陰歷月日中的月份
yl_d = Right(yl_md, 2) '拆解陰歷月日中的日子
If yl_d = "初一" Then yl_d = yl_m '若拆解的日子是“初一”,則即刻用該月的月份替代該陰歷月份的首個日子
r(p) = d & Chr(10) & yl_d '將公歷日期和對應的農歷日期合在一起填入到p處正確位置
If da = Date Then r(p).Select '若選擇年份后不斷瞬時生成的日期da和現在的日期匹配,則將當前填充的日期單元格選擇成活動狀態
Next
End Sub
Sub days_29_Or_28(y, m, r As Range) '閏年2月份29天,平年2月份28天(例如2020年就是閏年)
Dim da As Date, d
r.ClearContents
week_str = "日一二三四五六"
d = 1
da = CDate(y & "-" & m & "-" & d) '將字符串動態轉換為真正的日期
ws = Mid(Format(da, "[$-804]aaaa"), 3) '從轉換為星期XX的字符串中提取大寫星期幾的漢字保存在ws中
First_Day_Pos_In_Week_Area = InStr(week_str, ws) '每月初始的1號在日歷星期區域的定位位置
If Is_LeepYear(y) Then '閏年2月份天數
For d = 1 To 29
da = CDate(y & "-" & m & "-" & d) '將字符串動態轉換為真正的日期
ws = Mid(Format(da, "[$-804]aaaa"), 3) '從轉換為星期XX的字符串中提取大寫星期幾的漢字保存在ws中
Other_Day_Pos_In_Week_Area = InStr(week_str, ws)
'實際的每月的號數應該加上每月初始的1號在日歷星期區域的定位位置減去1“”d + (First_Day_Pos_In_Week_Area - 1),為了在第7個位置仍然將該 _
號數放在該行,所以還得再減去1“d + (First_Day_Pos_In_Week_Area - 1) - 1”,然后再除7取整,同時乘以7后加上該號數在日歷中星期區域的實 _
際列數位置,即可得到該號數在日歷區域的設計位置
p = Int((d + (First_Day_Pos_In_Week_Area - 1) - 1) / 7) * 7 + Other_Day_Pos_In_Week_Area
yl_md = Right(GetYLDate(da), 4) '調用轉農歷(陰歷)函數,取后四個漢字月日日期字符
yl_m = Left(yl_md, 2) '拆解陰歷月日中的月份
yl_d = Right(yl_md, 2) '拆解陰歷月日中的日子
If yl_d = "初一" Then yl_d = yl_m '若拆解的日子是“初一”,則即刻用該月的月份替代該陰歷月份的首個日子
r(p) = d & Chr(10) & yl_d '將公歷日期和對應的農歷日期合在一起填入到p處正確位置
If da = Date Then r(p).Select '若選擇年份后不斷瞬時生成的日期da和現在的日期匹配,則將當前填充的日期單元格選擇成活動狀態
Next
Else '平年2月份天數
For d = 1 To 28
da = CDate(y & "-" & m & "-" & d) '將字符串動態轉換為真正的日期
ws = Mid(Format(da, "[$-804]aaaa"), 3) '從轉換為星期XX的字符串中提取大寫星期幾的漢字保存在ws中
Other_Day_Pos_In_Week_Area = InStr(week_str, ws)
'實際的每月的號數應該加上每月初始的1號在日歷星期區域的定位位置減去1“”d + (First_Day_Pos_In_Week_Area - 1),為了在第7個位置仍然將該 _
號數放在該行,所以還得再減去1“d + (First_Day_Pos_In_Week_Area - 1) - 1”,然后再除7取整,同時乘以7后加上該號數在日歷中星期區域的實 _
際列數位置,即可得到該號數在日歷區域的設計位置
p = Int((d + (First_Day_Pos_In_Week_Area - 1) - 1) / 7) * 7 + Other_Day_Pos_In_Week_Area
yl_md = Right(GetYLDate(da), 4) '調用轉農歷(陰歷)函數,取后四個漢字月日日期字符
yl_m = Left(yl_md, 2) '拆解陰歷月日中的月份
yl_d = Right(yl_md, 2) '拆解陰歷月日中的日子
If yl_d = "初一" Then yl_d = yl_m '若拆解的日子是“初一”,則即刻用該月的月份替代該陰歷月份的首個日子
r(p) = d & Chr(10) & yl_d '將公歷日期和對應的農歷日期合在一起填入到p處正確位置
If da = Date Then r(p).Select '若選擇年份后不斷瞬時生成的日期da和現在的日期匹配,則將當前填充的日期單元格選擇成活動狀態
Next
End If
End Sub
Function Is_LeepYear(y) As Boolean '給定的年份是否為閏年LeepYear的判斷
If (y Mod 400 = 0) Or (y Mod 100 <> 0 And y Mod 4 = 0) Then
Is_LeepYear = True
Else
Is_LeepYear = False
End If
End Function
'自定義“公歷轉農歷”日期函數
Function GetYLDate(ByVal strDate As String) As String
On Error GoTo ExitFunction_Label
If Not IsDate(strDate) Then Exit Function '如果參數strDate非日期的無效字符串,則退出本函數工作
'定義setDate--設置的未來日期,tYear--未來日期的本年份,tMonth--本月份,tDay--本日子
Dim setDate As Date, tYear As Integer, tMonth As Integer, tDay As Integer
setDate = CDate(strDate) '為該GetYLDate()函數參數的字符串轉換后的日期賦予設定的日期
tYear = Year(setDate): tMonth = Month(setDate): tDay = Day(setDate) '年、月、日分別取值
'如果不是有效有日期,退出
If tYear > 2100 Or tYear < 1900 Then Exit Function
'定義daList()--是元素為18位日期二進制字符串數組,conDate--農歷新年日期,thisMonths--本年的二進制 _
月份信息(可能包含閏月)
Dim daList() As String * 18, conDate As Date, thisMonths As String
'定義AddYear--是相對1900年遞增的年,AddMonth--月份增量,AddDay--天數增量,getDay--農歷新年和設 _
之日期相差天數
Dim AddYear As Integer, AddMonth As Integer, AddDay As Integer, getDay As Integer
'定義YLyear--農歷(陰歷)年的字符串,YLShuXing--農歷(陰歷)年的屬相
Dim YLyear As String, YLShuXing As String
'定義dd0--農歷(陰歷)年的陰歷日子,mm0--農歷(陰歷)年的陰歷月,ganzhi()--每個元素為2個字符的天干地 _
支數組
Dim dd0 As String, mm0 As String, ganzhi(0 To 59) As String * 2
'定義RunYue--農歷(陰歷)年是否閏月的布爾型標志,RunYue1--農歷(陰歷)年閏月月份
Dim RunYue As Boolean, RunYue1 As Integer, mDays As Integer, i As Integer
'加載2年內的農歷數據
ReDim daList(tYear - 1 To tYear)
daList(tYear - 1) = Hexadecimal_To_Binary(Mid(ylData, (tYear - 1900) * 8 + 1, 7))
daList(tYear) = Hexadecimal_To_Binary(Mid(ylData, (tYear - 1900 + 1) * 8 + 1, 7))
AddYear = tYear
initYL:
AddMonth = CInt(Mid(daList(AddYear), 15, 2))
AddDay = CInt(Mid(daList(AddYear), 17, 2))
conDate = DateSerial(AddYear, AddMonth, AddDay) '農歷新年日期
getDay = DateDiff("d", conDate, setDate) + 1 '相差天數
If getDay < 1 Then AddYear = AddYear - 1: GoTo initYL
thisMonths = Left(daList(AddYear), 14) '前14位為本年的二進制月份信息(可能有閏月)存于thisMonths中
RunYue1 = Val("&H" & Right(thisMonths, 1)) '閏月月份
If RunYue1 > 0 Then '如果有閏月,則立即修正本年的二進制月份信息thisMonths,形成真正有效的二進制序 _
列信息
thisMonths = Left(thisMonths, RunYue1) & Mid(thisMonths, 13, 1) & Mid(thisMonths, RunYue1 + 1)
End If
thisMonths = Left(thisMonths, 13) '最后一次修正本年的二進制月份信息thisMonths,直接取13個月的情況
For i = 1 To 13 '遍歷1~13個月,找到并計算含閏月的有效天數,同時退出循環
mDays = 29 + CInt(Mid(thisMonths, i, 1))
If getDay > mDays Then
getDay = getDay - mDays
Else
If RunYue1 > 0 Then '如果有閏月,則進一步根據i的值情況做如下處理
If i = RunYue1 + 1 Then RunYue = True '若i確系為閏月,則將閏月標志置為真
If i > RunYue1 Then i = i - 1 '若i大于閏月月份,則將將i回退修正
End If
AddMonth = i '最終記錄下i作為真正的增量月份存入AddMonth
AddDay = getDay '同時,將得到的天數差作為增量天數
Exit For
End If
Next
dd0 = Mid(ylMd0, (AddDay - 1) * 2 + 1, 2) '用查找表的形式定位當前日期對應的農歷(陰歷)日子
mm0 = Mid(ylMn0, AddMonth, 1) + "月" '用查找表的形式定位當前日期對應的農歷(陰歷)月份
For i = 0 To 59 '0~59表示60年一個甲子,表示以60年一個輪回的形式,通過查找表精準定位每年的天干地支
ganzhi(i) = Mid(ylTianGan0, (i Mod 10) + 1, 1) + Mid(ylDiZhi0, (i Mod 12) + 1, 1)
Next
YLyear = ganzhi((AddYear - 4) Mod 60) '通過查找表形式得出陰歷年的天干地支表示形式
YLShuXing = Mid(ylShu0, ((AddYear - 4) Mod 12) + 1, 1) '通過查找表形式得出陰歷年的屬相表示形式
If RunYue Then mm0 = "閏" & mm0 '如果某陰歷月份有閏月,特別加上“閏X月”的形式
GetYLDate = "農歷:" & YLyear & "(" & YLShuXing & ")年" & mm0 & dd0 '拼接當前日期的完整農歷信息
ExitFunction_Label:
End Function
'將壓縮的陰歷字符還原
Private Function Hexadecimal_To_Binary(ByVal strHex As String) As String '十六進制轉二進制
Dim i As Integer, i1 As Integer, tmpV As String
Const hStr = "0123456789ABCDEF"
Const bStr = "0000000100100011010001010110011110001001101010111100110111101111"
tmpV = UCase(Left(strHex, 3))
'以下是十六進制轉二進制的具體操作
For i = 1 To Len(tmpV)
i1 = InStr(hStr, Mid(tmpV, i, 1))
Hexadecimal_To_Binary = Hexadecimal_To_Binary & Mid(bStr, (i1 - 1) * 4 + 1, 4)
Next
Hexadecimal_To_Binary = Hexadecimal_To_Binary & Mid(strHex, 4, 2)
'十六進制轉十進制
Hexadecimal_To_Binary = Hexadecimal_To_Binary & "0" & CStr(Val("&H" & Right(strHex, 2)))
End Function
ThisWorkbook中代碼如下:
Private Sub Workbook_Open() '工作簿一打開即刻初始化表單組合框數據并且在組合框中顯示之前選擇過的年份
Set shp_year_select = Sheets(1).Shapes("年份選擇")
shp_year_select.ControlFormat.RemoveAllItems
'萬年歷的年份范圍初步設定為“1900~2100”
For i = 1900 To 2100
shp_year_select.ControlFormat.AddItem i
Next
'以下是重新還原表單組合框控件之前選定過的年份顯示
yr = [a65535]
For i = 1 To shp_year_select.ControlFormat.ListCount
If yr = Val(shp_year_select.ControlFormat.List(i)) Then
n = i '遍歷整個表單組合框所有元素,查找與yr是否相匹配的元素,若找到即刻記下該編號并存于n中
Exit For
End If
Next
shp_year_select.ControlFormat.ListIndex = n '讓表單組合框顯示找到的之前選擇過的年份
End Sub
三、用方法一實現帶農歷萬年歷運行效果測試
(一)選擇年份,呈待生成帶農歷萬年歷狀態。如下圖所示
圖2 選擇年份準備生成帶農歷萬年歷
(二)點擊選擇的年份,生成實實在在的帶農歷的萬年歷。如下圖所示
圖3 生成帶農歷萬年歷效果
(三)壓下<清除日歷數據>按鈕,準備進行帶農歷的萬年歷數據清除。如下圖所示
圖4 準備清除帶農歷萬年歷數據
(四)壓下狀態下的<清除日歷數據>按鈕情況下點擊該按鈕,完成帶農歷萬年歷數據的清除,并將年份組合框內的顯示提示年份置為最新當前時間的年份。如下圖所示
圖5 清除帶農歷萬年歷數據結果
四、技術亮點小結
(一)充分利用尋找農歷閏月方法和壓縮的農歷字符還原方法完成公歷轉農歷
(二)在定位Excel的萬年歷數據填充單元格時,用字符串處理函數處理農歷生成的數據
(三)存儲記憶上次打開萬年歷的數據
好了,本期我們就分享到這里吧,希望大家喜歡和收藏哦!
最后,還是感謝大家的持續關注(頭條號:跟我學Office高級辦公)、推廣、點評哦!謝謝大家繼續關注下期第二中方法實現帶農歷的萬年歷設計!
歷是一項深入我們生活的設計,最早的日歷是什么樣的?它的設計經歷了哪些改變?本文將會帶領大家一起探索日歷的演化進程。
我們被那些具有歷史感、跨越幾個世紀的老物件包圍著,有時侯我們卻完全忽略了它們。
當你想到日歷的時候,腦海里第一個形象會是什么?可能是一個7列5行(或6行,視第一天而定)的數字表格,用以顯示一個月的日期。無論是在你的手機上還是掛在墻上,我們每天都會用到它。直到今日,日歷的樣式無論在什么媒介上都是雷同的。
我們日常生活中有許多具有歷史感的東西,有時它們已經存在了好幾個世紀,然而卻被我們完全忽略了。每當我看到那些已經深入我們生活的設計時,我都想知道是誰做出了這樣的設計決策——是誰讓這個東西變成了今天的樣子。網格樣式的日歷就是其中之一。
我有一個假設,當設計模式經歷時間的考驗時,它們可能已經達到了最大的改進點,這意味著用戶已經對某個問題的解決方案感到滿意,不需要進一步的迭代來滿足他們的需求。
沒有人會阻止設計師不斷地迭代,設計師會持續添加或優化設計作品的某些特性,例如,日歷的可讀性就是一直處于被優化的狀態(見圖1)。
產品設計曲線圖表
通常,當一個產品未達到設計臨界點時,它很可能就會被新產品超越,新產品解決了同樣的問題,但使用不同的方法和途徑,相比之下之前的產品就顯得已經過時了。但是我們今天所看到日歷樣式已經到處都被使用,似乎沒有人需要一個新的樣式它可能已經處于設計臨界點了,就像我們如今普遍使用的計算器和手機輸入界面一樣。
那么,誰是這個偉大設計的創作者呢?在本文中,由于那些可以說明日歷起源保存至今的文獻較少,所以我只能對它復雜的演化發展做出一些簡要的說明。
但在探索日歷演化進程之前,讓我們先快速瀏覽一下它背后的概念。
以七天作為周期似乎有一個復雜的演化過程,并且在世界各地同步起源,很難追溯和證明誰是最初的發明者:自公元前6世紀以來,七天為一周的日歷一直在猶太教中使用,數字7對巴比倫人來說有一種神秘的意義。它與七個天體有關;太陽、月亮、火星、水星、木星、金星和土星。早在古代中國和日本,人們就以這些天體為基礎,制定了每個周期都是七天。
但是,“七日工作周”的流行——以及它在現代日歷中的突出地位——可以追溯到羅馬人采用它的時候:君士坦丁大帝在公元321年正式采用了“七日工作周”,這意味著這種方法我們已經使用了1700年了。
Fasti Antiates Maiores的修復品,七月和八月的名稱和現在的不同(約公元前60年)。
儒略歷,公元前46年由凱撒大帝引入。迄今為止想要找到那個最初的發明者是很難的。所以我們需要說明的是它有什么特點而受到大家的歡迎。
大家認為是從羅馬人開始將每月定義為30天或31天。當然,他們計算也不是全部正確的,因為他們將一年僅僅劃分為8個月。但由于羅馬在西方文化中的強勢地位,這一錯誤的劃分方式一直持續到了格列高利改革后才被修正。
即使羅馬人給了我們所知道的日歷,但仍然不清楚是誰推廣了當下這種網格樣式。網格似乎是一種自然的選擇,因為它是顯示系列元素最直觀和高效的方式——埃及人已經在他們的日歷中使用了網格樣式。似乎它根本不需要一個發明者,就像我們從來不會問是誰發明了文字。
之后日歷一直遵循儒略歷,直到16世紀到17世紀,月和周的視覺樣式有了改變。
17世紀以前,用圓形圖表示年、月和周是很流行的,因為這是表示周期最好的方法。日歷被認為是每一年都會重復的,而不是用來只預示未來一年的日期,也沒有顯示日期名稱和時間之間的任何關聯——我們所能知道的就是一周有七天。
從卡洛林王朝的手稿中可以看出一天和一周的劃分(來源:Wikimedia Commons)
中世紀折疊年歷,15世紀(來源:Wikimedia Commons)
在接下來的幾十年里,人們一直使用這種重復的萬年歷,它可以計算出未來某一天是星期幾——而不是像今天的日歷一樣可以顯示出一年的具體日期。直到工業革命后印刷變得非常便宜,商家開始在廣告上印出整年整年的日歷,供普羅大眾使用。
塞繆爾·莫蘭的萬年歷d 1650年 (由http://www.britishmuseum.org提供)
現代歷法與其他年代的歷法一樣都在演進,它們也采用網格式的表單收集宗教、文化、氣象、天文和星象等信息。但是在15世紀后期,日歷的樣式發生了重大的變化,從之前的圓周永續的樣式,改為了表單排列的形式。
不僅僅是因為在1582年進行了的日歷的修訂,而且還可能是因為約翰內斯·古登堡(Johannes Gutenberg)引入了印刷術。
我沒有歷史事實來支持我的假設,但可以看出標準化表單網格系統在樣式的靈活和固有的印刷屬性之間存在著必然聯系。任何特殊的設計都需要印刷商聘請藝術家制作單獨的印刷模版。
模板通常是木質的,而不是我們日常使用中看起來的樣子。所以可能是印刷技術在很大程度上使得網格日歷的樣式成為更實用的標準,尤其是他可以很容易地將一年中的月份排列出來。
威廉·帕森斯 17世紀歐洲年表
塞繆爾·莫蘭(Samuel Morland)寫于1650年的《萬年歷》(perpetual almanac)是一個早期使用這種網格表單樣式的例子。有趣的是,其中沒有提到工作日的名字,只用字母表的前7個字母表示,這種格式在17世紀早期威廉·帕森斯(William Parsons)的手稿上也能看到。
在1773年,作為出版商的羅伯特·艾特肯(Robert Aitken)發明了他所聲稱的美國第一個日程表,為客戶提供一個已經排好的板式,與現在看到的還是有所不同。有史以來第一次,美國人民可以在一個表單里看到自己一天要做的
事情,就像今天我們使用的to-do list(任務計劃表),這已經超出了原本使用年歷來預示日期的常規用法。
伊麗莎白·諾爾斯(ElizabethKnowles)的刺繡萬年歷,1787年。(由V&A收藏提供)
除了它們的實際用途之外,日歷已經成為一種日常裝飾品。18世紀后期和第二次工業革命期間,廣告日歷已經成為商家在用戶家庭中傳播品牌特征的一種流行方式。酒店、加油站、銀行、大小品牌都會提供帶有廣告的免費日歷,以此提高他們的曝光率。
日歷品牌贊助 1888
直到20世紀60年代回歸到了極簡主義風格,開始把圖形設計和字體設計作為優化的對象,也通過引入黑體字體樣式和現代主義的方式來促進設計的改良。
在那時最著名的和最讓人興奮的產品恰好是兩位意大利人同一年各自創作的經典之作:Vignelli著名的大型掛歷和Enzo Mari的永久年歷,兩者仍然都在家庭中使用。
日歷達到最大可用性了嗎?也許是這樣,但是在Android(安卓)或iOS(蘋果)上出現的最新數字版本證明:只要人們有意愿嘗試新穎的設計形式,毫無疑問日歷的設計仍然存在著提升空間。
一些設計師試圖打破慣例。其中最著名的是約翰·梅達(John Maeda)。他設計了新穎的日歷界面體驗-Line Calendar(線性日歷https://www.youtube.com/watch?v=NbIhng27qTk)已經被MoMA(紐約現代藝術博物館)永久收藏。
最近的是一家加拿大的設計機構Teehan+Lax(現在已經被Facebook收購了),玩出了一個新的范例,你可以在這里看它的原型(https://vimeo.com/69623560)。
當然,對于許多日歷來說,沒有什么比我們所知道的簡單網格更好了。
《斯坦迪格歷法》(Stendig Calendar),馬西莫·維涅尼尼(MassimoVigne11i)著,1966年
Android(安卓)日歷app 2017
321:君士坦丁大帝頒布了一周七天工作制
900-1500:這一時期的文物顯示,日歷的樣式都是圓形
1500:古騰堡印刷機和寬邊印刷出現
1582年:格利高里公歷修訂
1650年:薩繆爾·莫蘭年鑒-萬年歷
1650-1800:萬年歷以網格形式出現
1860年及以后:出現了帶有網格日歷的海報 1966年:馬西莫·維格內利,《斯坦迪格日歷》
1966年:恩佐·馬里,東帝汶萬年歷
1995:約翰·梅達 2000年設計的線性日歷
原文作者:Francesco Bertell(弗朗西斯科·貝爾泰利)
本文由@one點設計 翻譯發布于人人都是產品經理。未經許可,禁止轉載
題圖來自Unsplash,基于CC0協議
*請認真填寫需求信息,我們會在24小時內與您取得聯系。