廣州大劇院2020年好戲不斷_包裝設計

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

廣州大劇院2020演出季發布會近日在廣州大劇院舉行。發布會介紹了2020廣州大劇院的重點項目,今年廣州大劇院在歌劇、音樂劇、戲劇戲曲及舞劇等陣營上將繼續上演眾多重磅作品,演出陣容更是星光熠熠,濮存昕、徐帆、靳東、韓雪、阿雲嘎、鄭雲龍等實力派演員都將帶着自己的舞台作品登陸廣州大劇院。

今年將開啟“央地合作”

2020年,廣州大劇院迎來開業十周年。據介紹,十年來,有1300多个中外藝術團體和32000多名藝術家先後登上廣州大劇院的舞台,帶來了3900多場精彩表演和1800多場藝術活動。同時,廣州大劇院充分利用中演院線以及絲綢之路國際劇院聯盟等國內、國外的演藝平台和渠道,积極推動廣東演藝院團、優秀演藝項目“走出去”。2018年12月,廣州芭蕾舞團赴馬耳他瓦萊塔地中海會議中心演出《芭蕾詩篇》;2019年8月,廣州芭蕾舞團編創的現代芭蕾舞劇《布蘭詩歌》和民族芭蕾舞劇《洛神》登陸美國紐約林肯中心大衛·寇克劇院;2020年,廣州歌舞劇院的民族舞劇《醒·獅》將赴美國波士頓博赫中心舒伯特劇院、華盛頓肯尼迪中心歌劇院上演。作為廣州、廣東的大劇院,廣州大劇院也堅持紮根本土,弘揚嶺南文化,十年來,劇院累計上演廣東本土優秀劇目300餘場。

今年,廣州大劇院將開啟文化領域“央地合作”新征程,作為中演院線的旗艦劇院,依託院線資源與故宮博物院開展合作,開發聯名文創產品,開辦系列講座,讓一南一北兩座文化殿堂迸發新火花。

發布會現場還舉行了中演院線廣州大劇院戲劇藝術中心授牌儀式,邀請著名劇作家唐棟、著名戲劇導演傅勇凡、著名舞美設計師秦立運等業內大咖出謀划策,加強原創劇目生產。中演院線執行董事、總經理,廣州大劇院董事長張利表示:“廣州大劇院在向生產型劇院邁進。以前有《馬可·波羅》,之後,創作會成為常態。”此外,廣州大劇院還聯合星海音樂廳推出貝多芬誕辰250周年紀念系列,組織全球超過500名藝術家,呈現貝多芬作品的大全集。

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

2020年好戲提前看

歌劇方面,今年廣州大劇院又有大動作,原創歌劇《馬可·波羅》 院慶十周年特別版將上演。此次邀請到人氣與實力兼具的王凱、洪之光、劉彬濠等新生代演員加盟,還有田浩江、周曉琳、宋倩、劉穎等蜚聲海內外的歌唱家坐鎮,同時聯手廣東珠影樂團和深圳歌劇舞劇院,為“大灣區歌劇孵化基地”的初試啼聲打下堅實基礎。發布會上,洪之光獻演劇中詠嘆調,提前讓觀眾感受到該劇的魅力。洪之光透露,其在劇中飾演文天祥,這個角色圓了自己想演中國原創歌劇的願望。此外,中國歌劇舞劇院的民族歌劇《小二黑結婚》和德國波恩歌劇院的貝多芬誕辰250周年獻禮歌劇《費德里奧》也備受期待,歌劇《費德里奧》是貝多芬偉大的作品之一,該劇有關於愛、生命、自由的表達,也有對婚姻中美好與聖潔的讚美。

戲劇方面,今年廣州大劇院也將迎來多部重磅作品,包括國家大劇院與廣州話劇藝術中心聯合製作原創話劇《林則徐》、北京人民藝術劇院話劇《天下第一樓》、俄羅斯瑪斯特卡雅劇院話劇《靜靜的頓河》以及北京當代話劇團《麥克白》、莎翁經典《哈姆雷特》等。

音樂劇陣營更是流量與明星雲集,韓雪、劉令飛領銜主演年度爆款音樂劇《白夜行》。中文原創音樂劇《圖蘭朵》的製作由中外頂級團隊聯合打造。音樂戲劇《叢林之書》由法國最重要劇院之一的巴黎城市劇院特邀享譽世界的先鋒戲劇大師羅伯特·威爾遜執導,超現實民謠姐妹花可可羅希為該劇創作了獨特音樂和歌曲,以全新的形式再現奇幻世界。

舞蹈方面,楊麗萍作品《十面埋伏》備受期待,該劇以中國舞蹈為主調,融合行為、裝置藝術、民樂及傳統戲劇等綜合藝術語言創造了“舞蹈劇場”,“風暴舞者”胡沈員、朱鳳偉將加盟演繹。大型原創民族舞劇《花木蘭》同樣陣容強大,飾演花木蘭的是荷花獎金獎獲得者郝若琦。本土兩大原創舞劇《沙灣往事》《醒·獅》也將再次登台。

戲曲方面,去年以一出《穆桂英挂帥》在廣州掀起了戲曲熱的的國家京劇院今年將帶來《龍鳳呈祥》《滿江紅》《鳳還巢》三部京劇作品,繼續由於魁智、李勝素領銜主演。此外,今年仍有多個世界名團的音樂會登陸廣州大劇院,包括巴黎室內樂團與別列佐夫斯基音樂會、波恩貝多芬交響樂團貝多芬交響曲音樂會等,精彩不斷,驚喜不斷。(記者 徐紹娜)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

一篇文章講透Dijkstra最短路徑算法_網頁設計

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

Dijkstra也叫迪傑斯特拉,是典型最短路徑算法,計算一個起始節點到路徑中其他所有節點的最短路徑的算法和思想。在一些專業課程中如數據結構,圖論,運籌學等都有介紹。其思想是一種基礎的求最短路徑的算法,通過基礎思想的變化可以解決很多複雜問題,如導航線路,動態規劃等。

Dijkstra 算法思想介紹

如下圖是一個多節點,多路徑圖。下面以該圖為例子講解dijkstra算法尋找最短路徑的過程。

以A點為起始點,求A點到其他點 B C D E F 5個點的最短路徑,最後得出A到其他點的最短路徑。

因為要求A到其他5個點的最短距離,所以構造一個數組記錄A到B C D E F 5個點的路徑距離。約定:

  • 如果A能夠直接達到節點,則使用路徑長度即權值作為其距離
  • 如果A節點不能直接達到節點則使用無窮大表示A到該點距離。
  • 任何點到自身都為0

那麼在最開始時,A點到圖中所有點的距離數組如下:

A B C D E F
0 10 無窮大 4 無窮大 無窮大

dijkstra的算法思想是從以上最短距離數組中每次選擇一個最近的點,將其作為下一個點,然後重新計算從起始點經過該點到其他所有點的距離,更新最短距離數據。已經選取過的點就是確定了最短路徑的點,不再參与下一次計算。

可能看到這裏你完全不明白dijkstra算法的思想,心裏可能想:這是說的人話嗎?不要緊,如果算法一句話就能解釋清楚,那就不會出現那麼多算法書了。下面我們就從實際的選取過程中理解這個思想的精髓。

第一次選取

構建好的數組是這樣的:

A B C D E F
0 10 無窮大 4 無窮大 無窮大

第一步選取該最短路徑數組中值最小的一個點。因為A點到本身不需要參与運算,所以從剩下的點中選擇最短的一個是D。
第二步以A-D的距離為最近距離更新A點到所有點的距離。即相當於A點經過D點,計算A到其他點的距離。

A-A : 0
A-B : A-D-B:6
A-C : A-D-C:19
A-D : A-D:4
A-E : A-D-E:10
A-F : A-D-F:去窮大

A B C D E F
0 6 19 4 10 無窮大

將現在A到各個點的距離和之前的比較,到相同點取最小值。更新了B C E的距離,得到如下新的最短距離數組:

A B C D E F
0 6 19 4 10 無窮大

同時現在A D兩點已經計算過,不參与下面的計算。

第二次選取

第二次選取的數組為第一次中更新過最短距離的數組

A B C D E F
0 6 19 4 10 無窮大

第一步:因為A D 不參与選取,所有從剩下的點中選取最近距離是點B
第二步:以B為最新點,更新最短數組

A-A : 0
A-B : A-D-B:6
A-C : A-D-B-C:14
A-D : A-D:4
A-E : A-D-B-E:12
A-F : A-D-B-F:無窮大

A B C D E F
0 6 14 4 12 無窮大

對比現在的最短距離和上一個數組的距離,到相同節點取最小的,C點由19更新成14,E點走A-D-E為10,距離更短所以不更新(敲黑板,這個重要),得到如下數組:

A B C D E F
0 6 14 4 10 無窮大

此時B點加入最短路徑範圍中。

第三次選取

上一步得到的數組為:

A B C D E F
0 6 14 4 10 無窮大

第一步:選取除了A B D節點之外的剩餘節點中最短節點,為點E
第二步:以E點為最新節點,更新最短路徑數組

因為在上一部中計算達到E點的距離時沒有更新距離,A-D-E 為10 最短,所以更新E點到B C F點的距離時走的路徑是A-D-E。注意這裏的最短距離有對應的路徑,選擇最小值就是選擇最短距離。

A-A : 0
A-B : A-D-B:6
A-C : A-D-E-C:11
A-D : A-D:4
A-E : A-D-E:10
A-F : A-D-E-F:22

A B C D E F
0 6 11 4 10 22

對比現在的最短距離和上一個數組的距離,到相同節點取最小的,更新C點走A-D-E-C 為11,比之前的A-D-B-C14距離更近,更新到F點距離,得到如下數組:

A B C D E F
0 6 11 4 10 22

此時E點加入最短路徑範圍中。

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

第四次選取

A B C D E F
0 6 11 4 10 22

第一步:選取除了A B D E節點之外的剩餘節點中最短節點,為點C
第二步:以C點為最新節點,更新最短路徑數組

A-A : 0
A-B : A-D-B:6
A-C : A-D-E-C:11
A-D : 4
A-E : A-D-E:10
A-F : A-D-E-C-F:16

A B C D E F
0 6 11 4 10 16

對比現在的最短距離和上一個數組的距離,到相同節點取最小的,更新到F點距離,可以得到如下數組:

A B C D E F
0 6 11 4 10 16

第五次選取

A B C D E F
0 6 11 4 10 16

第一步:選取除了A B C D E節點之外的剩餘節點中最短節點,也就是最後一個節點:F
第二步:以F點為最新節點,更新最短路徑數組。由於F點是最後一個點,所以也不用更新數組,目前的數組就是所求數組
將F點加入最短路徑範圍中,此時所有的點都加入了最短路徑範圍,也就是說A點到所有點的距離都找到了。最總得出的距離值為:

最終得到的結果為:

A B C D E F
0 6 11 4 10 16

最終結果

相應的A點到所有點的最短路徑走法最終得到的結果為:

A B C D E F
0 6 11 4 10 16

A-A:0
A-B : A-D-B:6

A-C : A-D-E-C:11

A-D:4

A-E:A-D-E:10

A-F:A-D-E-C-F:16

算法總結

Dijkstra算法作為求最短路徑的經典算法,個人理解為算法提供了一種思想,每走一步都是找到最短的路徑,並且每走一步都實時更新所有距離,保證每次都選擇最短路徑。

python實現Dijkstra

將以上的過程使用python來實現。
首先總結一個Dijkstra算法的核心思想,分成兩步走:

  1. 構造一個最短路徑數組,每次找到數組中未訪問的節點里最小的點
  2. 以上一步的節點為最新節點,更新起始點到所有點的距離

使用python就是實現這兩步即可

數據準備

二維矩陣

如何描述一個圖呢?通常有兩種方式,分別是:十字鏈表和二維矩陣。因為二維矩陣更加直觀,所以選擇二維矩陣。

將上面的圖描述成一個二維矩陣

無窮大使用MAX = float('inf')表示,該數值是python中表示無窮大的一個值。

這個二維矩陣真正直觀之處在哪裡呢?是能夠看到任意一個點到其他點的距離。如想看D點到其他點的距離,就是:

在我們的算法兩步走中第二步要更新A點經過某點到其他點的距離,正是使用了這個特徵。

MAX= float('inf')

matrix = [
    [0,10,MAX,4,MAX,MAX],
    [10,0,8,2,6,MAX],
    [MAX,8,10,15,1,5],
    [4,2,15,0,6,MAX],
    [MAX,6,1,6,0,12],
    [MAX,MAX,5,MAX,12,0]
    ]

最短路徑數組

在上面講解算法過程中有一個重要的的最短路徑數組,不斷更新該數組直到所有的點都被訪問到。使用python語言,構造該數組:

distance = [MAX] * len(matrix)

len(matrix) 實際上算出的圖的點的個數。初始化時所有的節點都是不可達。

在算法過程中還有一個重要的數組,並沒有體現出來,但是在python計算時也很重要,那就是訪問過的點。每一次訪問之後就要將訪問過的點加入到該數組中,這樣做是為了避免重複訪問。

used_node = [False] * len(matrix)

初始化時認為所有點都沒有訪問到

代碼實現



MAX= float('inf')

matrix = [
    [0,10,MAX,4,MAX,MAX],
    [10,0,8,2,6,MAX],
    [MAX,8,10,15,1,5],
    [4,2,15,0,6,MAX],
    [MAX,6,1,6,0,12],
    [MAX,MAX,5,MAX,12,0]
    ]


def dijkstra(matrix, start_node):
    
    #矩陣一維數組的長度,即節點的個數
    matrix_length = len(matrix)

    #訪問過的節點數組
    used_node = [False] * matrix_length

    #最短路徑距離數組
    distance = [MAX] * matrix_length

    #初始化,將起始節點的最短路徑修改成0
    distance[start_node] = 0
    
    #將訪問節點中未訪問的個數作為循環值,其實也可以用個點長度代替。
    while used_node.count(False):
        min_value = float('inf')
        min_value_index = 999
        
        #在最短路徑節點中找到最小值,已經訪問過的不在參与循環。
        #得到最小值下標,每循環一次肯定有一個最小值
        for index in range(matrix_length):
            if not used_node[index] and distance[index] < min_value:
                min_value = distance[index]
                min_value_index = index
        
        #將訪問節點數組對應的值修改成True,標誌其已經訪問過了
        used_node[min_value_index] = True

        #更新distance數組。
        #以B點為例:distance[x] 起始點達到B點的距離,
        #distance[min_value_index] + matrix[min_value_index][index] 是起始點經過某點達到B點的距離,比較兩個值,取較小的那個。
        for index in range(matrix_length):
            distance[index] = min(distance[index], distance[min_value_index] + matrix[min_value_index][index])

    return distance




start_node = int(input('請輸入起始節點:'))
result = dijkstra(matrix,start_node)
print('起始節點到其他點距離:%s' % result)

結果:

請輸入起始節點:0
起始節點到其他點距離:[0, 6, 11, 4, 10, 16]

簡單總結

學習python實現Dijkstra重要的地方有幾點:

  1. 數據構造 二維矩陣表示圖
  2. 圖的訪問方式 更新最短路徑數組的過程無非就是分別比較二維矩陣數組中某一行的值和最短路徑數組的值

熟悉這樣的處理方式,再有類似的算法也能找到解決的思路。例如一個二維矩陣,從起始點開始只能走向下的相鄰的元素,求達到某點的最短路徑。
希望通過該篇文章,能夠深刻理解Dijkstra算法,做到心中有數,手中有活。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

2020本科校招-從小白到拿到30k offer的學習經歷_貨運

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

本文是個人的2020年年中總結

還有十幾天就要畢業,面臨着身份從學生到互聯網社畜的轉變,未來的一切捉摸不定,但凡心中萬千情緒,也只能「但行好事,莫問前程」。

介紹下博主背景:計算機本科大四,剛進大三時還是個沒有實習、沒有項目經歷的小白,
經過一年時間的刻意練習[deliberate practice],最後在校招中拿到了一些大廠offer,
標題中30k的offer已經拒掉了,有了更好的去處,只是用來舉例。

文章同步自 https://github.com/mio4/interview

0x0 前期調研

以下經驗根據個人的經驗來談,有一定的局限性,僅供參考。

1. 為什麼不選擇算法

計算機專業的就業方向很多,對於應屆生來說,工種大概可以分為移動端、前端、後端、算法、運維、測試。

由於近年來ML和DL的快速發展,特別是16年AlphaGo以及視覺檢測、自然語言處理方向的進展,人工智能方向成為了繼大數據之後的又一個學術熱點。

周圍接觸到的同學,大部分集中於算法和後端方向。我最開始就是準備深入後端方向,並且決定本科直接就業。但是我有兩點糾結:

  1. 後端是否不如算法?
  2. 對於後端,本科學歷是否適合職業發展預期?
1. 本科生以算法作為工作目標難度太高,和字節跳動的面試官交流,目前字節AI Lab的成員組成是大部分碩博,以及少部分優秀的本科生(acm區域金作為參考)
2. 算法需要的三點:實習、競賽和paper,我都沒有信心和諸多轉專業的工科碩士抗衡
3. 個人對於算法沒有強烈興趣,發展空間不明朗,行業未來搭上5G的車,可能會有比較大的想象空間,風險和收益並存。

參考

2021 校招算法崗, 勸退還是繼續

如何看待 2020 屆校招算法工程師崗位求職人數遠大於招聘崗位的現象?

2. 為什麼選擇Java後端

如果認定了後端,後端語言這麼多,如何選擇適合自己的方向?

業界有一種說法:『語言只是工具,數據結構和算法是解決問題的根本』。這種說法本身是沒有錯的,但是從普遍來看,本科生從有工作的想法到秋招,只有(或者不到)一年的時間,泛泛了解多個方向不如深入特定方向,更有助於求職。

後端主要的開發語言有JavaC++PythonPHPGo,PHP目前除了百度和騰訊部分部門之外,使用範圍不廣,不予討論。另外,Golang作為高併發場景的常見支持,在雲計算領域使用比較廣泛。

互聯網和其他資本市場沒有本質區別,既然是市場,就存在供求關係。 對於算法這種供遠遠大於求的情況,就是買方市場,賣方(求職者)需要更好的產品(技能)來獲得同等的價格。對於應屆生來說,找到一個良好的買方市場,更加有利。

調研基於兩個預設條件:

  1. 崗位數目越多,市場的需求越大。
  2. 高薪崗位越多,該方向的發展前景越大。

我們參考www.lagou.com,互聯網大部分獵頭或者對外招聘崗位都會發布在上面,有一定的參考價值。

地點選擇北京,薪資範圍選擇25~50k/month。

Java

(1)職位500+,可以看出需求缺口很大

(2)除此之外,Java生態系統最為完善,e.g. Spring全家桶。

(3)Java是電商網站的技術首選,阿里、京東、拼多多等電商公司的技術棧。

Python

Python主要是作為腳本語言,適用於運維開發和算法崗位,目前在字節跳動使用比較廣泛,可以看出崗位相對較少。適合作為輔助開發語言,畢竟寫腳本算得上是程序員的必備技能了。

C++

C++是遊戲開發(其次還有lua)和系統開發的首選語言,但是因為上手成本比較高(指針和內存管理)以及需要對操作系統深入了解,市場崗位需求也不如Java,所以不推薦。不過如果有acm或者信息競賽基礎,也不成問題。

0x1 秋招準備

隨着互聯網從業人數越來越多,競爭也呈現白熱化。科班學生不僅要和同專業同學競爭,也會感受到轉專業同學(大部分是碩士)帶來的壓力。參考同系講師的數據,我航15年時6系本科同學比較容易就能去BAT,到了19年BAT頭條對於大部分同學不是想進就進了。

從宏觀意義上來看招聘,企業需要找到適配崗位的員工,學生需要謀取職業生涯的第一份工作。對於學生來說,秋招和高考在本質上是相似的,都是一種選拔,需要證明自己的能力比別人更強。

在準備的時候,定量的標準往往更加具有說服力,『我守望先鋒玩的很厲害』遠遠不如『我守望先鋒全國天梯4000分』,『我學習Java花了很多時間』不如『我閱讀了《Java核心思想》和《Effective Java》以及…,並且復現了書中的代碼,在github倉庫xxx』。

我是從2018.8月下旬(進入大三)開始準備,到找到工作總共花了一年的時間,基本上分為三個時期:

  1. 2018.8 – 2018.12 :基本素質培養
  2. 2019.1 – 2019.8 : 百度智能雲實習
  3. 2019.7 – 2019.8 : 秋招提前批

整個秋招的核心思路 = 基礎+實習+項目

1. 基本素質

第一次認清自己的水平,是在大二下學期結束的暑假。當時投了百度暑期實習,毫無準備的前提下,只用了20分鐘就掛在了一面。

面試慘跪之後,我分析了自己的處境,當時的我GPA排名40%,沒有參加過ACM程序競賽,沒有實習經驗,除了OO課程,沒有寫過”大型”項目。從編程能力上看,大一數據結構60分醬油飄過,算法課程也限於理論,只能說能夠應付考試不掛科,Java只會語法,web框架更是沒有項目經驗。

這時候的我深刻意識到自己在編程方面還是個在新手村找不到北的green hand,記得剛上大一的時候,哪位神仙說我校混的差躺着也能去BAT?夢想很美好,現實很骨感。

相對於隔壁北郵,我航更加註重學生的深造(校內讀研和出國留學),對於工作方面的指導甚少。我大部分的時間都是一個人準備,所以經常陷入迷茫orz,這期間主要的問題是:

  1. 選擇什麼方向? (前面已經對市場進行了就業分析)
  2. 要學習什麼內容?(算法太高級了,玩玩可以,找工作水平不夠)
  3. 要學到什麼程度,才能找到實習/工作?

整個秋招的過程,就是我解決這三個問題的過程。

於是,首先我使用Hexo + Github.io 搭建了個人博客:mio4

博客有兩個作用:

  1. 能夠體系化自己的學習,方便複習
  2. 量化自己的學習成果,面試的時候,給面試官帶來好印象

其次開始有針對性的訓練自己的編程能力,

Java程序員的基本素質有:Java基礎(語法+JVM+多線程),Spring框架(SSM/SpringBoot,進階可以SpringCloud),算法能力(刷題)

2. BAT實習

找工作本身就是一個經驗和需求矛盾的過程:

找實習,認準BAT TMD,其中BATT(頭條)基本屬於一個檔;外企的話,ms/hulu/airbnb認可度也很高。

拿周圍同學舉例,即使是小廠開了一個月8、9k的實習補助,也不要因此放棄BAT的實習機會。

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

雖然近幾年百度已經明顯掉隊於BAT,但是就我個人體驗來看,外界對於百度的技術還是很認可的。對於實習來說,鍍金的效果完全夠用。【2020.5 update:目前應屆不滿一年沒有晉陞和普調資格,除非部門極好,否則不推薦轉正】

在2019.1月,托研二直系學長的福,內推經過三面通過了百度智能雲計算部的後端實習,面經:https://www.nowcoder.com/discuss/151829

到8月結束了實習,半年的實習時光對於工業屆有了更深入的了解。

3. 秋招面試

離開baidu主要有兩個原因:

  1. 工作內容不算滿意:在baidu的工作語言主要是php,大廠目前使用php的很少,不利於職業長期發展;另外實習負責的模塊也不算核心。
  2. 薪資不夠預期:baidu本科白菜價15k*15,每個月的工資扣掉五險一金和稅,只剩下11k,再加上北京動輒3、4k的房租,過於艱難。

因為花了接近一年時間準備,所以在提前批就結束了秋招,9月大部分企業剛開通秋招正式批通道前,我就歇了,真正準備校招的時間不足兩個月。【2020.5 update: offer這種東西當然是越多越好,去年的我太佛系了,騰訊、微軟、快手這些公司甚至沒投簡歷,現在覺得去年還是naive】

AI四小龍面過了兩家,都是sp | ssp,雲從當時沒聽說所以沒投,商湯提前批四道題A了三道解題報告,結果居然沒有不給面試機會Excuse me?

提前批收到了百度、字節跳動等大廠和一些獨角獸的offer,互聯網薪資保密,已經簽約的就不談具體待遇了。

拿個去年8月已經拒掉的獨角獸offer舉例:

0x2 其他經驗

1. 書籍推薦

國內csdn以及各種類似菜鳥教程的博客的知識系統往往支離破碎,不能深入學習。

看書能夠系統化學習知識,下面推薦我看過的一些書

  • 算法:

    • 《劍指Offer》
  • Java:

    • 《Java核心技術》,當字典看,不需要整遍過

    • 《Java併發編程實戰》 ,熟悉多線程

    • 《深入理解Java 虛擬機》,熟悉JVM必備

    • 《圖解Java多線程設計模式》,日本人寫的書,直觀易懂,強力推薦

    • 《Java 8實戰》

    • 《Effective Java》

  • Spring:

    • 《Spring實戰》
  • MySQL:

    • 《高性能MySQL》,主要是第五六章
    • 《Redis實戰》
  • 網絡:

    • 《圖解HTTP》
    • 《計算機網絡自頂向下方法》
  • 代碼規範:

    • 《重構》

2. 準備項目

對於在校學生來說,準備項目可能會面臨着無法下手的問題。

首先項目一般不是自己憑空想出來的,可以通過實驗室等方式接手比較成熟的方案。

我的操作是直接使用網課,比如慕課

選擇一個感興趣的,從0到1復現一個項目,完善自己的技術棧。

當然,imooc上大部分乾貨需要付費,學生黨如果沒錢可以考慮下面的方式:

蒲公英論壇:ipv6,好處是下載視頻不走校園網流量,有部分從imooc搬運的資源。

當然對於買不起資料的窮孩子,還是有灰色路徑可以『曲徑通幽』的,比如【已刪除】:盜版的論壇,基本上涵蓋所有imooc的視頻代碼資料,僅供參考。作為程序員,這裏盡量推薦支持正版。

3. 算法能力

面試必備:《劍指offer》 + LeetCode

LeetCode題目比較多,參考Hot/Top 100:

到秋招結束,刷題量不多,總共100左右,但是對每道題使用思維導圖進行總結,盡可能舉一反三:

4. 實習投遞

投遞實習的方式一般有:

  • [x] 熟人內推:最靠譜最快的方式,投遞簡歷到百度實習面試邀約只花了不到半天。

  • [x] 牛客網:響應時間一般,1~7天

  • [x] 實習僧app :響應時間一般,一般一周

  • [x] 官方通道:比如騰訊的暑期實習官方在線投遞。

4. 面試技巧

分公司分崗位的面經可參考:www.nowcoder.com

我的個人面經總結:https://github.com/mio4/learn-java/blob/master/Note/interview.md ,總共250頁的PDF,綜合個人經歷的所有面試,題目覆蓋率至少有50%。根據研二轉專業的同學反饋來看,作用很大。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

12萬起這些合資SUV品質都這麼好!居然還能降價達2.2萬?_網頁設計公司

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

4T渦輪增壓發動機,最大功率為140匹,最大扭矩為200牛米,匹配了一台6速手自一體變速箱,加速時初段反映還是不錯的,不過據市場反映,其運用的普利司通輪胎質量容易開裂。北京現代ix25指導價:11。98-18。68萬最大優惠價1。6萬元北京現代ix25自上市以來還沒有經歷過改款,在市場的受歡迎程度也僅次於本田XR-V車型,月銷量維持在1萬輛左右,現在全國市場普遍優惠1萬元左右,最優惠的是成都,達到了1。

年底買車優惠大,確實是很多消費者所想的,眼看着金九銀十這個最佳買車時期快過去了,可能接下來買車的時機還有下個月的廣州車展,以及元旦前經銷商為了沖銷量榜這幾個優惠期間了,但這一期介紹的這幾款10萬級小型SUV,常年的優惠也很大,最大達到了2.2萬元,如果與銷售多磨一磨價格,確實能夠在你的預算中再狠狠地砍個幾千塊,話不多說,趕緊往下看。

東風標緻2008

指導價:9.97-16.37萬

最大優惠價2.2萬元

標緻2008在全國各地具有1.5萬元以上的優惠,其中在上海地區的優惠最大,全系優惠達到了2-2.2萬元。

作為東風標緻的一款小型SUV產品標緻2008,車身最大的亮點就是擁有着全景天幕玻璃頂和超大視野的前擋風玻璃,透視感極佳,配合高品質的遮陽簾可以隔絕八成以上的紅外線,以及99%的紫外線,外觀設計上與208有些相似,都採用了極具運動風格的前臉設計,再加上車身的越野套件,整體形象十分硬朗。

動力上全新標緻2008搭載1.2T三缸的渦輪增壓發動機,以及1.6L自然吸氣發動機和1.6T渦輪增壓發動機,如今小排量渦輪發動機已經是發展趨勢,如果只是想用來代步,稍微兼容起家用空間的朋友們,可以入手1.2T發動機版本,百公里油耗為7L,安全配置齊全,帶ESp車身穩定和發動機啟停系統,經濟油耗低同時動力又跟得上,但後排空間稍微比較小,喜歡這款車的朋友可以去實測一下。

上汽通用 雪佛蘭創酷

指導價:10.99-14.99萬

最大優惠價2.0萬元

新款雪佛蘭創酷上市時,廠家主要是針對該車型進行一些配置的升級,創酷在全國平均有1萬元以上的優惠,在上海地區的優惠幅度最大,達到了1.6-2.0萬元。

雪佛蘭創酷採用了和別克昂科拉相同的平台打造,到售價卻比昂科拉便宜了挺多,創酷的車身外觀肌肉感比較強,線條簡潔,有着美系車的獨有味道,但整體的外觀還是比較中庸。

內飾風格依然非常地簡約,

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

中控台按鈕劃分清晰,比較容易上手,儀錶盤為單轉速表,右側則有行車電腦显示屏和數顯時速表。配置上全系標配ESp車身穩定系統,電動天窗,上坡輔助等等,而頂配還多了真皮座椅,倒車雷達,中控大屏等等。

動力上則搭載了1.4T渦輪增壓發動機,最大功率為140匹,最大扭矩為200牛米,匹配了一台6速手自一體變速箱,加速時初段反映還是不錯的,不過據市場反映,其運用的普利司通輪胎質量容易開裂。

北京現代ix25

指導價:11.98-18.68萬

最大優惠價1.6萬元

北京現代ix25自上市以來還沒有經歷過改款,在市場的受歡迎程度也僅次於本田XR-V車型,月銷量維持在1萬輛左右,現在全國市場普遍優惠1萬元左右,最優惠的是成都,達到了1.6萬元的優惠。

外觀設計上,北京現代ix25依舊延續了現代家族“流體雕塑2.0”的設計語言,相對於其他的現代車型,現代ix25顯得更加硬朗帥氣,非常符合國人的審美觀。

內飾同樣沒有太多花哨的設計,比較實用,硬塑料比較多,沒有其他點綴之物。新車標配了車身穩定系統,定速巡航,坡道輔助,倒車雷達,全景天窗,多功能方向盤,藍牙和一鍵啟動功能,確實性價比非常之高,動力則有1.6L和2.0L自然吸氣發動機和1.6T渦輪增壓發動機,能夠滿足對於動力不同需求的消費者,變速箱則涵括了6擋手動,6擋自動和6速雙離合變速器。

上汽大眾斯柯達Yeti

指導價:12.98-20.98萬

最大優惠價1.8萬元

新款斯柯達Yeti車型,取消了以前的老名字“野帝”,斯柯達Yeti在全國各地均為1萬元左右的優惠,其中上海地區的優惠還是最大,幅度達到了1.2-1.8萬元。

新款Yeti在外觀上並不會和老款野帝相差太多,只是在翼子板上多了些許裝飾件,更換了全新的輪圈,整體外觀看起來比較方正。

儀錶盤採用大眾傳統的雙錶盤+中央單色液晶显示屏,沒有什麼新意,後排座椅能夠前後移動,在空間靈活多變,比較實用。配置上大部分版本都配備了ESp車身穩定系統,全系標配電動天窗,動力上則搭配了1.6L自然吸氣發動機,1.4T和1.8T渦輪增壓發動機,變速箱則分別搭載5擋手動,7速雙離合和6速雙離合變速器。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

有人喜歡開手動擋嗎?手動擋車型也可以很高級你知道嗎?_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

另一方面,很多低端的車子,都會全系標配手動變速箱來壓低售價,像寶駿310、長安歐尚就是這樣的角色。所以就目前的經濟狀況來看,手動擋汽車的存在無論對於消費者還是廠商來說,還是有很大意義。假如不管好壞,只要最好玩呢。

據國外媒體消息,法拉利首席技術官Michael HugoLeiters接受採訪時表示,法拉利未來將不會再推出手動擋車型,因此,California成為了法拉利最後一款可以選裝手動變速箱的車型。

聽到了這個消息,作為法拉利車迷的不禁為之震驚,而震驚以後,更多的惋惜。在這個微涼的傍晚,不禁在思考,連跑車都開始放棄手動變速箱了,那這個變速箱還有存在的意義嗎?

雙離合真的比手動擋要快

要評價一個變速箱的性能,主要看它的換擋邏輯、平順性和換擋速度。從換擋邏輯和平順性來看,雙離合的表現主要跟廠商的調校有關,實際表現也是有好有壞的,而手動變速箱則主要跟個人的操作有關,新老司機的差別也很大。所以最具可比性的應該是兩種變速箱的換擋速度了,不說保時捷的pDK了,光大眾的DSG,官方就膽敢給出200毫秒的換擋速度,而手動擋呢?踩離合、從原擋位拔出、再推進新的擋位、松離合,即使是老司機,完成這一套動作的時間也不可能少於200毫秒吧?所以你要是單論換擋速度,雙離合真的完勝手動擋。

但手動擋有一個無敵的優點

手動變速箱造價低,手動擋車也一般比自動擋要便宜差不多一萬軟妹幣,所以對於部分消費者來說,想購得心儀的車輛又想省錢的話,手動擋就是最佳的選擇。對於廠商來說,手動擋則是一個市場工具,用來拉低新車的整體售價,博取眼球,

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

而實際上,很多手動擋的最低配車型,壓根就很難買到。像新思域的手動版本,都幾乎成了珍稀物品。另一方面,很多低端的車子,都會全系標配手動變速箱來壓低售價,像寶駿310、長安歐尚就是這樣的角色。所以就目前的經濟狀況來看,手動擋汽車的存在無論對於消費者還是廠商來說,還是有很大意義。

假如不管好壞,只要最好玩呢?

新手開手動擋一天下來,左腳都是要報廢的節奏,市區擁堵路段的頻繁換擋更是考驗司機手臂耐力和換擋技巧,手動擋的各種麻煩相信都是不言而喻的。那還有沒有在購車預算充足的情況下還願意購買手動擋汽車的人?答案是肯定的,就有一位這樣的朋友,他是手動版昂克賽拉的車主。還記得那天和他開車出去吃飯,隔壁車道也來了一輛昂克賽拉,右側車道的我們眼看前面修路必須要併入左側車道,但隔壁的車子絲毫沒有避讓的意思,緊急時候,豈能認慫?只見他踩下離合,一個之字型的手勢,馬上從5擋換下4擋,小昂轉速馬上飈至3500,緊跟一腳油門和及時的打方向,順利搶在鄰車前面進入左車道,揚長而去。

當然,並不是呼籲大家要怎樣的暴力駕駛,只是想讓大家知道,有些樂趣,只有手動擋能給你。它不是最好的,但一定是最好玩的。

總結:就像汽車會取代馬車那樣,舊事物總是會被更符合人們需求的新事物所取代,自動擋逐漸取代手動擋,這是個必然的趨勢。車企會繼續生產手動擋汽車,但也絕對不是因為部分人的情懷,相信手動擋並不會消失,就像在這個汽車工業發達的年代,在旅遊景點里不是還有人坐馬車嗎?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

當老傳統成為新時尚_包裝設計

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

北宋年間,家家戶戶貼年畫已是一種風尚,也由此,木版年畫在歷史的長河中始終留存着時尚的記憶。當代,手工木版年畫被以非物質文化遺產的形態加以保護,同時,傳承和創新也成為木版年畫傳承人肩負的兩個重任。有這樣一群人,他們視木版年畫為中國民間藝術寶庫中的一顆璀璨明珠,一直走在挖掘、創新、變革的路上。又是新年將至,今年的木版年畫可以為人們帶來哪些新意?這些心心念念的新時代“年畫家”都在忙些什麼?

 設計師王全傑 :年畫新可能

楊柳青年畫周曆簽

新年將至,北京順義區的一所小院里,設計師王全傑剛剛忙完楊柳青年畫周曆簽——2020年“世象新語”周曆的預售,5000套周曆幾乎都已被單位預定出去。初次嘗試與楊柳青木版年畫合作,並能夠得到市場的認可,王全傑不斷回味。

2019年7月,王全傑在清華美院校友召喚下加入了年畫日新創作營,本着對於年畫的關注與喜愛,帶着輕鬆自在的心情,王全傑輕裝上陣了。

剛入營,兩件事讓王全傑心情無法平靜。首先創作營的主辦方將組織結構安排得相當縝密,全國11個木版年畫產地會自發組成一個個創作小組(注:最終實際組成了9個創作小組),年畫傳承人+插畫師+設計師+導師(清華美院和中央美院的教授和副教授)作為一個小組組合,王全傑成了楊柳青組組長。主辦方的安排是,經過一個階段的年畫知識學習,再經歷1個月的創作,然後做出創新作品和產品進行展覽,最後對社會公開創作成果。

在學習了年畫知識、深入年畫創作地區了解了年畫歷史后,王全傑原先以為會憑藉著豐富設計經驗輕鬆完成這份任務,但卻被厚重的傳統文化攪動了內心。“真是無處下手,年畫傳承人深陷其中很難改變,我們作為外來者,出於對老祖宗留下的這些寶物的敬畏,似乎也不敢隨便說、隨便做了。”王全傑說。

一個月的迷茫期過後,王全傑總結出年畫轉化的兩個難點:第一,作為傳承人,雖然擁有老祖宗留下的年畫模板、掌握着傳統的刻畫技藝,但是缺少產品轉化能力;第二,作為外來者——設計人員,雖有很強的設計能力,但究竟轉化成什麼是最大的難點。

王全傑在腦海中翻轉:做什麼樣的產品,怎麼能體現楊柳青風格,怎麼能有新意,怎麼能增加使用的體驗感、情景感,還得製造點小驚喜。

經歷了無數個靈感的沉浮,一次次被自己或小組成員否定,王全傑關於周曆的創意橫空出世。機緣巧合的是,這樣一個創意很快得到北京一家企業的認可,並有意出資支持該創意。王全傑信心倍增。

“創意有了,落實到產品時,第一版就被出資方否定了。”王全傑再次回到原點。利用楊柳青年畫元素設計的第一版周曆美觀,但缺少生活黏性、很難達到與使用者的互動。

年畫日新創作營楊柳青組設計師們再次前往楊柳青,找到楊柳青木版年畫國家級非遺傳承人霍慶順,聽霍慶順細數年畫民俗,並對其所擁有的與老百姓生活相關的藏品做了仔細研究。霍慶順老人一句話點亮了王全傑內心。

“你看踩高蹺,大多數人都只看到表面上的熱鬧,但很少有人知道這其中最主要的用意是祭拜藥王、祈福安康。進入年關,每一天都有特殊的意義……”這句話就像一把開啟創意之門的鑰匙,為迷途中的王全傑和設計師們打開了門:“周曆的基礎上加入日曆,在臘月的最後一周開始日曆倒計時。”

創意有了,楊柳青創作小組成員連續两天兩夜的頭腦風暴,將創意再次細化落實在產品上:在周曆基礎之上特別策劃的“過年日曆”,篇篇辣詞趣語,以當下語境直入現代生活,內置如意轉盤每天跟你靈犀互動。這些新意讓2020年“世象新語”周曆產品順利出版落地。

“我們設計師想給楊柳青木版年畫傳承者做出一個示範,讓他們看到傳統年畫與當代流行文化融合的更多可能性。為大眾提供一個日常使用載體,讓人們了解楊柳青木版年畫的豐富性和多樣性。”王全傑說。

故事記錄者恭弘=叶 恭弘萌 :回歸當代時尚

穀雨平台上的金華木板年畫

對傳統手工藝有着濃厚興趣的恭弘=叶 恭弘萌,探索傳統與當代文化融合的路徑用了5年時間,為了讓更多人了解傳統手工藝他組建了一個傳統手工藝視頻傳播平台——穀雨,這個平台免費為手工藝人們拍攝宣傳片。

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

“中國上下五千年,從來不缺故事,但卻沒有人會講這些中國故事,有太多我們為之自豪的傳統手工藝散落於四野。如果不能讓人看見,又何談傳承呢?”恭弘=叶 恭弘萌說。

恭弘=叶 恭弘萌畢業於中國美術學院視覺傳達系。他做過設計師,參与設計的項目有中國國際西湖博覽會、第21屆金雞百花電影節標誌等;在電視台工作了十幾年。為了追逐心中的夢想,放棄穩定的工作,恭弘=叶 恭弘萌創業成立了自己的文化公司。服務了幾百個商業客戶后,2015年他突然感覺工作生涯中所有的積累都應該為了達成他內心中一個夢想——用各種方式構築傳統手工藝從再現到再生的體系,讓傳統手工藝能夠回歸當代生活,成為新的時尚,至此,穀雨——傳統手工藝內容轉化平台宣告問世。

2019年,穀雨的木版年畫產品轉化項目“傳統節中國禮”主旨就是讓傳統節更中國、更時尚。“穀雨打造的內容轉化平台是要做傳統手工藝時尚品牌孵化池。”恭弘=叶 恭弘萌說。

穀雨的平台上金華木版年畫製作者黃菁菁就是一位不甘於讓傳統成為傳說的女性。

金華木版年畫,孕育於漢唐,形成於宋元,鼎盛於明清。浙江在五代吳越時期就是木版畫比較發達的地區,至宋代,金華已經是全國木版年畫的中心之一。

黃菁菁出身木版年畫世家,長大后並沒有從事年畫製作,而成為一名商人,在杭州開了一家文化公司,經營得紅紅火火。雖然她在商界收穫了成功,但內心深處無數次回憶起兒時父輩們製作木版年畫的場景、父親抱着她給她講《五子登科》年畫故事的場景……她對記憶中的年畫魂牽夢繞。

那段日子里,似乎總有一個聲音,在向她不停地召喚。她決定:回家,做木版年畫。

回鄉之時正是金華木版年畫最蕭條的時期,曾經有專家對金華木版年畫的萎縮倍感痛心,黃菁菁的回歸讓金華木板年畫再現生機。

說來容易,做起來才知道苦辣辛酸。首先需搜集老版與老畫,“尋回金華年畫的根”。剛開始的那幾年,黃菁菁一直奔波在路上,從南疆到北國。後來甚至還走出了國門,無論是老版還是老畫,黃菁菁的用語都是“請回來”。 就這樣,黃菁菁一點一滴地打造起金華木版年畫博物館和年畫製作體驗館。如今,她的年畫博物館中已收藏了60餘套老版、2000多幅老年畫,其中六成都是孤版。而她的年畫製作體驗基地成了中國第六個年畫製作基地,也是浙江唯一的年畫基地。

傳承人邰立平:讓年畫“火”起來

邰立平作品《方弼》

年關將至,鳳翔木版年畫代表性傳承人邰立平忙得不亦樂乎。

鳳翔年畫“始於唐宋,盛於明清”,早在600多年前的明初洪武年間,世代耕居於此的邰氏家族就已經開始從事年畫的生產了。邰立平是鳳翔木版年畫第20代傳人。他創辦了鳳怡年畫社,致力於對流散民間的古樣進行挖掘、整理、研究和複製,使這一古老民間傳統藝術得以傳承。

邰立平這一代傳承人經歷了年畫從興到衰整個過程。“隨着時代的轉變和人們生活方式的變化,之前人們張貼年畫的習俗正在慢慢地改變。雖然春節時張貼年畫的習俗還未消失,但也從張貼傳統手工印製的年畫轉變為張貼機器印刷品,這就對傳統的非遺手工年畫形成了很大衝擊。傳統的木版年畫就是反映人民生活的一部百科全書。近年來,隨着國家對傳統文化的重視,社會上對這項古老的傳承技藝越來越推崇和認可,出現了很多國潮風格的文創作品,這都是傳統非遺技藝的文化土壤。”邰立平說。

邰立平認為,當下是鳳翔木版年畫發展的最佳時機。“把木版年畫繼承下去,讓民間美術發揚光大”這是邰立平最大的心愿。為了能夠儘早實現自己的心愿,他分步驟推動自己的計劃。

首先需要找回失散的木刻版。從1978年改革開放到上世紀90年代的年畫,邰立平共復刻了400多套版,大概2000塊木版,把散落民間的老畫樣基本全部恢復。他還分別在1994年出版了手工裝訂的《鳳翔木版年畫選》第一卷、1997年出版了第二卷。目前第三卷的出版工作也在緊張進行中。

其次,在創作上,鳳翔年畫風格緊貼時代脈搏。1999年巴黎中國文化周,邰立平創作了活動吉祥物獅子滾繡球版畫,2008年奧運會時創作了福娃主題作品。鳳翔年畫的名氣越來越大。邰立平先後應邀在澳大利亞、德國、法國等多個國家以及國內各大美院和美術館參展,其作品也陸續被中國國家博物館和國內外200多家藝術學院與機構收藏。

再次,從經營的角度,邰立平也不斷進行着適應市場的改變。傳統年畫的尺寸和包裝都不適應現代都市人的需求,他們專門推出了適合張貼在城市單扇門上的小門神年畫,對年畫的包裝進行變革,更能使現代人喜歡和接受。

這些年邰立平一直在探索如何讓年畫走進千家萬戶,使老百姓能真正用起來。他參与非遺進校園活動;他和國內知名設計師合作,推出了雕刻時光日曆;和中國手藝網和雅昌合作,推出了鳳翔木版年畫年曆;和騰訊、京東合作,跨界進入手游領域,讓年輕人在玩遊戲時也能體驗國潮風。(鄭芋)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

【匯總】 為園友寫的皮膚製作工具 awescnb_網頁設計

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

Awescnb, awesome cnblog.

簡介

可能許多初來乍到的新手會被博客園經典的風格勸退,或者您是一個老園友,需要為您的博客定製一些功能(例如宣傳公眾號,文章目錄、或者插入幾個捐助二維碼等等)而不想浪費太多時間。我製作這個小項目的目的是園友能夠輕易地構建一個博客園皮膚或完善您的博客頁面功能。它可以用來做以下三件事:

  1. 安裝: 在您的博客園安裝這個項目中已經集成的皮膚.安裝之後,可以快速切換其他皮膚.
  2. 創建: 快速創建一個的博客園皮膚,通過打包生成文件,供您使用.
  3. 分享: 快速創建一個博客園皮膚並將它貢獻給項目,園友就能夠切換到您的皮膚了.

視頻教程

今天周末錄製了一個簡單視頻教程,雖然我在搭建的文檔里有寫,希望它能幫您更容易上手。視頻從博客皮膚的安裝、切換、從零開始製作三個方面簡單展開。

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

您可能是非 web 前端從業者,視頻中用到的命令您可能會有疑惑,只需要下載 node並安裝到您的電腦就擁有了一個叫做 npm 的東西(node 包管理器),而視頻中使用的 npm xx 命令正是來源於此。

鏈接

  • 博客皮膚性能優化
  • 構建一個簡約博皮的過程
  • 用 webpack 玩轉博客園
  • 當前集成的 30+ 插件介紹
  • 手寫一個兼容博客園多編輯器的文章目錄插件

配置一覽

{
    // 基本配置
    theme: {
        name: 'reacg',
        color: '#FFB3CC',
        title: '',
        contentSize: 'mid',
        headerBackground: '',
        avatar: 'https://pic.cnblogs.com/face/sample_face.gif',
        favicon: '',
    },
    // 代碼高亮
    highLight: {
        type: 'atomOneDark',
        inDarkMode: 'atomOneDark',
    },
    // 代碼行號
    lineNumbers: {
        enable: true,
    },
    // github圖標
    github: {
        enable: true,
        color: '#ffb3cc',
        url: 'https://github.com/guangzan/awescnb',
    },
    // 碼雲圖標
    gitee: {
        enable: true,
        color: '#C71D23',
        url: 'https://gitee.com/guangzan/awescnb',
    },
    // 圖片燈箱
    imagebox: {
        enable: true,
    },
    // 文章目錄
    catalog: {
        enable: true,
        position: 'left',
    },
    // 返回頂部按鈕
    back2top: {
        enable: true,
        type: 'complex',
    },
    // 右下角按鈕列表
    tools: {
        enable: true,
        initialOpen: true,
        draggable: false,
    },
    // live2d模型
    live2d: {
        enable: true,
        page: 'all',
        agent: 'pc',
        model: 'haru-01',
        width: 150,
        height: 200,
        position: 'left',
        gap: 'default',
    },
    // 點擊特效
    click: {
        enable: true,
        auto: false,
        colors: ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C'],
        size: 30,
        maxCount: 15,
    },
    // 評論輸入框表情
    emoji: {
        enable: true,
        showRecents: true,
        recentsCount: 20,
        showPreview: true,
        showSearch: true,
    },
    // 暗色模式
    darkMode: {
        enable: true,
        autoDark: false,
        autoLight: false,
    },
    // 音樂播放器
    musicPlayer: {
        enable: true,
        page: 'all',
        agent: 'pc',
        autoplay: false,
        volume: 0.4,
        lrc: {
            enable: false, // 啟用歌詞
            type: 1, // 1 -> 字符串歌詞 3 -> url 歌詞
            color: '', // 顏色
        },
        audio: [
            {
                name: '404 not found',
                artist: 'REOL',
                url:
                    'http://music.163.com/song/media/outer/url?id=436016480.mp3',
                cover:
                    'http://p2.music.126.net/cu1sEIDxXOJm5huZ3Wjs0Q==/18833534672880379.jpg?param=300x300',
                lrc: ``,
            },
        ],
    },
    // 隨筆頭圖
    postTopimage: {
        enable: true,
        // position: 'top', // position api 已經廢棄,使用 postbottomimage 代替
        fixed: false,
        imgs: [],
    },
    // 隨筆尾圖
    postBottomimage: {
        enable: false,
        img: '',
        height: '',
    },
    // 打賞
    donation: {
        enable: false,
        qrcodes: [],
    },
    // 個性簽名
    signature: {
        enable: false,
        contents: [],
    },
    // 二維碼
    qrcode: {
        enable: false,
        img: '',
        desc: '',
    },
    // 彈出公告
    notice: {
        enable: false,
        text: [],
    },
    // 首頁列表圖
    indexListImg: {
        enable: false,
        imgs: [],
    },
    // 頂部加載進度條
    topProgress: {
        enable: false,
        page: 'all',
        agent: 'pc',
        background: '#FFB3CC',
        height: '5px',
    },
    indexTimeline: {
        enable: false,
    },
    // 隨筆頁尾部簽名
    postSignature: {
        enable: false,
        content: [],
        licenseLink: '',
    },
    // 背景圖片或顏色
    bodyBackground: {
        enable: false,
        type: 'color',
        value: '',
        opacity: 1,
        repeat: false,
    },
    // 彈幕
    barrage: {
        enable: false,
        opacity: 0.6,
        colors: [
            '#FE0302',
            '#FF7204',
            '#FFAA02',
            '#FFD302',
            '#FFFF00',
            '#A0EE00',
            '#00CD00',
            '#019899',
            '#4266BE',
            '#89D5FF',
            '#CC0273',
            '#CC0273',
        ],
        barrages: [],
        indexBarrages: [],
        postPageBarrages: [],
    },
    // 圖表
    charts: {
        enable: false,
        pie: {
            title: 'My skills',
            data: {
                labels: ['JavaScript', 'css', 'Vue', 'React', 'wechat'],
                values: [40, 30, 20, 10, 20],
            },
        },
    },
    // 鎖屏
    lock: {
        enable: true,
        background: '',
        strings: [
            '<i>Powered by</i> webpack.',
            '&amp; Theme in awescnb',
            '快去自定義你的個性簽名吧~',
        ],
    },
    // footer鏈接
    links: [
        {
            name: 'awescnb',
            link: 'https://gitee.com/guangzan/awescnb',
        },
    ],
}

文檔

  • 配置皮膚
  • 配置皮膚
  • 創建新皮膚
  • 更新日誌

最後

起初我只是用 gulp(前端構建工具) 簡單製作了一個博客園皮膚供自己使用,後來越來越多的園友使用,我索性用 webpack 將它重構並和交流群里的小夥伴一起完善。現在它已經能夠完全勝任當前的工作了,enjoy!今後我也不再發布關於它的介紹隨筆,會花費精力寫其他前端相關內容。

有希望捐助的小夥伴不要再問我了,這個小項目非我一人所為,它不接受任何捐助。有任何建議或者問題都可以到交流群(541802647)里交流或者在項目倉庫提個 issue。再次感謝所有提供建議的小夥伴。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

透過 NestedScrollView 源碼解析嵌套滑動原理,Android View 的事件分發原理解析_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

NestedScrollView 是用於替代 ScrollView 來解決嵌套滑動過程中的滑動事件的衝突。作為開發者,你會發現很多地方會用到嵌套滑動的邏輯,比如下拉刷新頁面,京東或者淘寶的各種商品頁面。

那為什麼要去了解 NestedScrollView 的源碼呢?那是因為 NestedScrollView 是嵌套滑動實現的模板範例,通過研讀它的源碼,能夠讓你知道如何實現嵌套滑動,然後如果需求上 NestedScrollView 無法滿足的時候,你可以自定義。

嵌套滑動

說到嵌套滑動,就得說說這兩個類了:NestedScrollingParent3 和 NestedScrollingChild3 ,當然同時也存在後面不帶数字的類。之所以後面帶数字了,是為了解決之前的版本遺留的問題:fling 的時候涉及嵌套滑動,無法透傳到另一個View 上繼續 fling,導致滑動效果大打折扣 。

其實 NestedScrollingParent2 相比 NestedScrollingParent 在方法調用上多了一個參數 type,用於標記這個滑動是如何產生的。type 的取值如下:

    /**
     * Indicates that the input type for the gesture is from a user touching the screen. 觸摸產生的滑動
     */
    public static final int TYPE_TOUCH = 0;

    /**
     * Indicates that the input type for the gesture is caused by something which is not a user
     * touching a screen. This is usually from a fling which is settling.  簡單理解就是fling
     */
    public static final int TYPE_NON_TOUCH = 1;

嵌套滑動,說得通俗點就是子 view 和 父 view 在滑動過程中,互相通信決定某個滑動是子view 處理合適,還是 父view 來處理。所以, Parent 和 Child 之間存在相互調用,遵循下面的調用關係:

上圖可以這麼理解:

  • ACTION_DOWN 的時候子 view 就要調用 startNestedScroll( ) 方法來告訴父 view 自己要開始滑動了(實質上是尋找能夠配合 child 進行嵌套滾動的 parent),parent 也會繼續向上尋找能夠配合自己滑動的 parent,可以理解為在做一些準備工作 。
  • 父 view 會收到 onStartNestedScroll 回調從而決定是不是要配合子 view 做出響應。如果需要配合,此方法會返回 true。繼而 onStartNestedScroll()回調會被調用。
  • 在滑動事件產生但是子 view 還沒處理前可以調用 dispatchNestedPreScroll(0,dy,consumed,offsetInWindow) 這個方法把事件傳給父 view,這樣父 view 就能在onNestedPreScroll 方法裏面收到子 view 的滑動信息,然後做出相應的處理把處理完后的結果通過 consumed 傳給子 view。

  • dispatchNestedPreScroll()之後,child可以進行自己的滾動操作。

  • 如果父 view 需要在子 view 滑動后處理相關事件的話可以在子 view 的事件處理完成之後調用 dispatchNestedScroll 然後父 view 會在 onNestedScroll 收到回調。

  • 最後,滑動結束,調用 onStopNestedScroll() 表示本次處理結束。

  • 但是,如果滑動速度比較大,會觸發 fling, fling 也分為 preFling 和 fling 兩個階段,處理過程和 scroll 基本差不多。 

NestedScrollView

首先是看類的名字

 class NestedScrollView extends FrameLayout implements NestedScrollingParent3,
 NestedScrollingChild3, ScrollingView {

可以發現它繼承了 FrameLayout,相當於它就是一個 ViewGroup,可以添加子 view , 但是需要注意的事,它只接受一個子 view,否則會報錯。

    @Override
    public void addView(View child) {
        if (getChildCount() > 0) {
            throw new IllegalStateException("ScrollView can host only one direct child");
        }

        super.addView(child);
    }

    @Override
    public void addView(View child, int index) {
        if (getChildCount() > 0) {
            throw new IllegalStateException("ScrollView can host only one direct child");
        }

        super.addView(child, index);
    }

    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        if (getChildCount() > 0) {
            throw new IllegalStateException("ScrollView can host only one direct child");
        }

        super.addView(child, params);
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (getChildCount() > 0) {
            throw new IllegalStateException("ScrollView can host only one direct child");
        }

        super.addView(child, index, params);
    }

add view

對於 NestedScrollingParent3,NestedScrollingChild3 的作用,前文已經說了,如果還是不理解,後面再對源碼的分析過程中也會分析到。

其實這裏還可以提一下 RecyclerView:

public class RecyclerView extends ViewGroup implements ScrollingView,
        NestedScrollingChild2, NestedScrollingChild3 {

這裏沒有繼承 NestedScrollingParent3 是因為開發者覺得 RecyclerView 適合做一個子類。並且它的功能作為一個列表去展示,也就是不適合再 RecyclerView 內部去做一些複雜的嵌套滑動之類的。這樣 RecycylerView 外層就可以再嵌套一個 NestedScrollView 進行嵌套滑動了。後面再分析嵌套滑動的時候,也會把 RecycylerView 當作子類來進行分析,這樣能更好的理解源碼。

內部有個接口,使用者需要對滑動變化進行監聽的,可以添加這個回調:

    public interface OnScrollChangeListener {
        /**
         * Called when the scroll position of a view changes.
         *
         * @param v The view whose scroll position has changed.
         * @param scrollX Current horizontal scroll origin.
         * @param scrollY Current vertical scroll origin.
         * @param oldScrollX Previous horizontal scroll origin.
         * @param oldScrollY Previous vertical scroll origin.
         */
        void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
                int oldScrollX, int oldScrollY);
    }

構造函數

下面來看下構造函數:

    public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initScrollView();

        final TypedArray a = context.obtainStyledAttributes(
                attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
        // 是否要鋪滿全屏
        setFillViewport(a.getBoolean(0, false));

        a.recycle();
        // 即是子類,又是父類
        mParentHelper = new NestedScrollingParentHelper(this);
        mChildHelper = new NestedScrollingChildHelper(this);

        // ...because why else would you be using this widget? 默認是滾動,不然你使用它就沒有意義了
        setNestedScrollingEnabled(true);

        ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
    }    

這裏我們用了兩個輔助類來幫忙處理嵌套滾動時候的一些邏輯處理,NestedScrollingParentHelper,NestedScrollingChildHelper。這個是和前面的你實現的接口 NestedScrollingParent3,NestedScrollingChild3 相對應的。

下面看下  initScrollView 方法里的具體邏輯:

    private void initScrollView() {
        mScroller = new OverScroller(getContext());
        setFocusable(true);
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
     // 會調用 ViewGroup 的 onDraw setWillNotDraw(
false); // 獲取 ViewConfiguration 中一些配置,包括滑動距離,最大最小速率等等 final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); }

setFillViewport

在構造函數中,有這麼一個設定:

setFillViewport(a.getBoolean(0, false));

與 setFillViewport 對應的屬性是 android:fillViewport=”true”。如果不設置這個屬性為 true,可能會出現如下圖一樣的問題:

xml 布局:

<?xml version="1.0" encoding="utf-8"?>
<NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#fff000">
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</NestedScrollView>

效果:

可以發現這個沒有鋪滿全屏,可是 xml 明明已經設置了 match_parent 了。這是什麼原因呢?

那為啥設置 true 就可以了呢?下面來看下它的 onMeasure 方法:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // false 直接返回
        if (!mFillViewport) {
            return;
        }

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode == MeasureSpec.UNSPECIFIED) {
            return;
        }

        if (getChildCount() > 0) {
            View child = getChildAt(0);
            final NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();

            int childSize = child.getMeasuredHeight();
            int parentSpace = getMeasuredHeight()
                    - getPaddingTop()
                    - getPaddingBottom()
                    - lp.topMargin
                    - lp.bottomMargin;
            // 如果子 view 高度小於 父 view 高度,那麼需要重新設定高度
            if (childSize < parentSpace) {
                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin,
                        lp.width);
                // 這裏生成 MeasureSpec 傳入的是 parentSpace,並且用的是 MeasureSpec.EXACTLY 
                int childHeightMeasureSpec =
                        MeasureSpec.makeMeasureSpec(parentSpace, MeasureSpec.EXACTLY);
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

當你將 mFillViewport 設置為 true 后,就會把父 View 高度給予子 view 。可是這個解釋了設置 mFillViewport 可以解決不能鋪滿屏幕的問題,可是沒有解決為啥 match_parent 無效的問題。

在回到類的繼承關係上,NestedScrollView 繼承的是 FrameLayout,也就是說,FrameLayout 應該和 NestedScrollView 擁有一樣的問題。可是當你把 xml 中的布局換成 FrameLayout 后,你發現竟然沒有問題。那麼這是為啥呢?

原因是 NestedScrollView 又重寫了 measureChildWithMargins 。子view 的 childHeightMeasureSpec 中的 mode 是 MeasureSpec.UNSPECIFIED 。當被設置為這個以後,子 view 的高度就完全是由自身的高度決定了。

    @Override
    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        // 在生成子 view 的 MeasureSpec 時候,傳入的是 MeasureSpec.UNSPECIFIED
        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

比如子 view 是 LinearLayout ,這時候,它的高度就是子 view 的高度之和。而且,這個 MeasureSpec.UNSPECIFIED 會一直影響着後面的子子孫孫 view 。

我猜這麼設計的目的是因為你既然使用了 NestedScrollView,就沒必要在把子 View  搞得跟屏幕一樣大了,它該多大就多大,不然你滑動的時候,看見一大片空白體驗也不好啊。

而 ViewGroup 中,measureChildWithMargins 的方法是這樣的:

    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

由於一般使用 NestedScrollView 的時候,都是會超過屏幕高度的,所以不設置這個屬性為 true 也沒有關係。

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

繪製

既然前面已經把 onMeasure 講完了,那索引把繪製這塊都講了把。下面是 draw 方法,這裏主要是繪製邊界的陰影:

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (mEdgeGlowTop != null) {
            final int scrollY = getScrollY();
       // 上邊界陰影繪製
if (!mEdgeGlowTop.isFinished()) { final int restoreCount = canvas.save(); int width = getWidth(); int height = getHeight(); int xTranslation = 0; int yTranslation = Math.min(0, scrollY); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) { width -= getPaddingLeft() + getPaddingRight(); xTranslation += getPaddingLeft(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) { height -= getPaddingTop() + getPaddingBottom(); yTranslation += getPaddingTop(); } canvas.translate(xTranslation, yTranslation); mEdgeGlowTop.setSize(width, height); if (mEdgeGlowTop.draw(canvas)) { ViewCompat.postInvalidateOnAnimation(this); } canvas.restoreToCount(restoreCount); }
       // 底部邊界陰影繪製
if (!mEdgeGlowBottom.isFinished()) { final int restoreCount = canvas.save(); int width = getWidth(); int height = getHeight(); int xTranslation = 0; int yTranslation = Math.max(getScrollRange(), scrollY) + height; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) { width -= getPaddingLeft() + getPaddingRight(); xTranslation += getPaddingLeft(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) { height -= getPaddingTop() + getPaddingBottom(); yTranslation -= getPaddingBottom(); } canvas.translate(xTranslation - width, yTranslation); canvas.rotate(180, width, 0); mEdgeGlowBottom.setSize(width, height); if (mEdgeGlowBottom.draw(canvas)) { ViewCompat.postInvalidateOnAnimation(this); } canvas.restoreToCount(restoreCount); } } }

onDraw 是直接用了父類的,這個沒啥好講的,下面看看 onLayout:

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mIsLayoutDirty = false;
        // Give a child focus if it needs it
        if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
            scrollToChild(mChildToScrollTo);
        }
        mChildToScrollTo = null;

        if (!mIsLaidOut) { // 是否是第一次調用onLayout // If there is a saved state, scroll to the position saved in that state.
            if (mSavedState != null) {
                scrollTo(getScrollX(), mSavedState.scrollPosition);
                mSavedState = null;
            } // mScrollY default value is "0"

            // Make sure current scrollY position falls into the scroll range.  If it doesn't,
            // scroll such that it does.
            int childSize = 0;
            if (getChildCount() > 0) {
                View child = getChildAt(0);
                NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
                childSize = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            }
            int parentSpace = b - t - getPaddingTop() - getPaddingBottom();
            int currentScrollY = getScrollY();
            int newScrollY = clamp(currentScrollY, parentSpace, childSize);
            if (newScrollY != currentScrollY) {
                scrollTo(getScrollX(), newScrollY);
            }
        }

        // Calling this with the present values causes it to re-claim them
        scrollTo(getScrollX(), getScrollY());
        mIsLaidOut = true;
    }

onLayout 方法也沒什麼說的,基本上是用了父類 FrameLayout 的布局方法,加入了一些 scrollTo 操作滑動到指定位置。

嵌套滑動分析

如果對滑動事件不是很清楚的小夥伴可以先看看這篇文章:Android View 的事件分發原理解析。

在分析之前,先做一個假設,比如 RecyclerView 就是 NestedScrollView 的子類,這樣去分析嵌套滑動更容易理解。這時候,用戶點擊 RecyclerView 觸發滑動。需要分析整個滑動過程的事件傳遞。

dispatchTouchEvent

這裏,NestedScrollView 用的是父類的處理,並沒有添加自己的邏輯。

onInterceptTouchEvent

當事件進行分發前,ViewGroup 首先會調用 onInterceptTouchEvent 詢問自己要不要進行攔截,不攔截,就會分發傳遞給子 view。一般來說,對於 ACTION_DOWN 都不會攔截,這樣子類有機會獲取事件,只有子類不處理,才會再次傳給父 View 來處理。下面來看看其具體代碼邏輯:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onMotionEvent will be called and we do the actual
         * scrolling there.
         */

        /*
        * Shortcut the most recurring case: the user is in the dragging
        * state and he is moving his finger.  We want to intercept this
        * motion.
        */
        final int action = ev.getAction();
     // 如果已經在拖動了,說明已經在滑動了,直接返回 true
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { return true; } switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check * whether the user has moved far enough from his original down touch. */ /* * Locally do absolute value. mLastMotionY is set to the y value * of the down event. */ final int activePointerId = mActivePointerId; if (activePointerId == INVALID_POINTER) { // If we don't have a valid id, the touch down wasn't on content. 不是一個有效的id break; } final int pointerIndex = ev.findPointerIndex(activePointerId); if (pointerIndex == -1) { Log.e(TAG, "Invalid pointerId=" + activePointerId + " in onInterceptTouchEvent"); break; } final int y = (int) ev.getY(pointerIndex);
          // 計算垂直方向上滑動的距離
final int yDiff = Math.abs(y - mLastMotionY);
          // 確定可以產生滾動了
if (yDiff > mTouchSlop && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) { mIsBeingDragged = true; mLastMotionY = y; initVelocityTrackerIfNotExists();
            // 可以獲取滑動速率 mVelocityTracker.addMovement(ev); mNestedYOffset
= 0; final ViewParent parent = getParent(); if (parent != null) {
               // 讓父 view 不要攔截,這裏應該是為了保險起見,因為既然已經走進來了,只要你返回 true,父 view 就不會攔截了。 parent.requestDisallowInterceptTouchEvent(
true); } } break; } case MotionEvent.ACTION_DOWN: { final int y = (int) ev.getY();
          // 如果點擊的範圍不在子 view 上,直接break,比如自己設置了很大的 margin,此時用戶點擊這裏,這個範圍理論上是不參与滑動的
if (!inChild((int) ev.getX(), y)) { mIsBeingDragged = false; recycleVelocityTracker(); break; } /* * Remember location of down touch. * ACTION_DOWN always refers to pointer index 0. */ mLastMotionY = y; mActivePointerId = ev.getPointerId(0);           // 在收到 DOWN 事件的時候,做一些初始化的工作 initOrResetVelocityTracker(); mVelocityTracker.addMovement(ev); /* * If being flinged and user touches the screen, initiate drag; * otherwise don't. mScroller.isFinished should be false when * being flinged. We need to call computeScrollOffset() first so that * isFinished() is correct. */ mScroller.computeScrollOffset();
          // 如果此時正在fling, isFinished 會返回 flase mIsBeingDragged
= !mScroller.isFinished();
          // 開始滑動 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: /* Release the drag */ mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; recycleVelocityTracker(); if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) { ViewCompat.postInvalidateOnAnimation(this); }
          // 手抬起后,停止滑動 stopNestedScroll(ViewCompat.TYPE_TOUCH);
break; case MotionEvent.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; } /* * The only time we want to intercept motion events is if we are in the * drag mode. */ return mIsBeingDragged; }

onInterceptTouchEvent 事件就是做一件事,決定事件是不是要繼續交給自己的 onTouchEvent 處理。這裏需要注意的一點是,如果子 view 在 dispatchTouchEvent 中調用了:

parent.requestDisallowInterceptTouchEvent(true)

那麼,其實就不會再調用 onInterceptTouchEvent 方法。也就是說上面的邏輯就不會走了。但是可以發現,down 事件,一般是不會攔截的。但是如果正在 fling,此時就會返回 true,直接把事件全部攔截。

那看下 RecyclerView 的 dispatchTouchEvent 是父類的,沒啥好分析的。而且它的 onInterceptTouchEvent 也是做了一些初始化的一些工作,和 NestedScrollView 一樣沒啥可說的。

onTouchEvent

再說 NestedScrollView 的 onTouchEvent。

對於 onTouchEvent 得分兩類進行討論,如果其子 view 不是 ViewGroup ,且是不可點擊的,就會把事件直接交給 NestedScrollView 來處理。

但是如果點擊的子 view 是 RecyclerView 的 ViewGroup 。當 down 事件來的時候,ViewGroup 的子 view 沒有處理,那麼就會交給 ViewGroup 來處理,你會發現ViewGroup 的 onTouchEvent 是默認返回 true 的。也就是說事件都是由  RecyclerView 來處理的。

這時候來看下 NestedScrollView 的 onTouchEvent 代碼:

 public boolean onTouchEvent(MotionEvent ev) {
        initVelocityTrackerIfNotExists();

        MotionEvent vtev = MotionEvent.obtain(ev);

        final int actionMasked = ev.getActionMasked();

        if (actionMasked == MotionEvent.ACTION_DOWN) {
            mNestedYOffset = 0;
        }
        vtev.offsetLocation(0, mNestedYOffset);

        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN: {
          // 需要有一個子類才可以進行滑動
if (getChildCount() == 0) { return false; }
          // 前面提到如果用戶在 fling 的時候,觸碰,此時是直接攔截返回 true,自己來處理事件。
if ((mIsBeingDragged = !mScroller.isFinished())) { final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } } /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged.處理結果就是停止 fling */ if (!mScroller.isFinished()) { mScroller.abortAnimation(); } // Remember where the motion event started mLastMotionY = (int) ev.getY(); mActivePointerId = ev.getPointerId(0);
         // 尋找嵌套父View,告訴它準備在垂直方向上進行 TOUCH 類型的滑動 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
break; } case MotionEvent.ACTION_MOVE: final int activePointerIndex = ev.findPointerIndex(mActivePointerId); if (activePointerIndex == -1) { Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); break; } final int y = (int) ev.getY(activePointerIndex); int deltaY = mLastMotionY - y;
          // 滑動前先把移動距離告訴嵌套父View,看看它要不要消耗,返回 true 代表消耗了部分距離
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset, ViewCompat.TYPE_TOUCH)) { deltaY -= mScrollConsumed[1]; vtev.offsetLocation(0, mScrollOffset[1]); mNestedYOffset += mScrollOffset[1]; }
          // 滑動距離大於最大最小觸發距離
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); }
            // 觸發滑動 mIsBeingDragged
= true; if (deltaY > 0) { deltaY -= mTouchSlop; } else { deltaY += mTouchSlop; } } if (mIsBeingDragged) { // Scroll to follow the motion event mLastMotionY = y - mScrollOffset[1]; final int oldY = getScrollY(); final int range = getScrollRange(); final int overscrollMode = getOverScrollMode(); boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); // Calling overScrollByCompat will call onOverScrolled, which // calls onScrollChanged if applicable.
            // 該方法會觸發自身內容的滾動
if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0, 0, true) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) { // Break our velocity if we hit a scroll barrier. mVelocityTracker.clear(); } final int scrolledDeltaY = getScrollY() - oldY; final int unconsumedY = deltaY - scrolledDeltaY;
            // 通知嵌套的父 View 我已經處理完滾動了,該你來處理了
if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset, ViewCompat.TYPE_TOUCH)) {
              // 如果嵌套父View 消耗了滑動,那麼需要更新 mLastMotionY
-= mScrollOffset[1]; vtev.offsetLocation(0, mScrollOffset[1]); mNestedYOffset += mScrollOffset[1]; } else if (canOverscroll) { ensureGlows(); final int pulledToY = oldY + deltaY;
               // 觸發邊緣的陰影效果
if (pulledToY < 0) { EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(), ev.getX(activePointerIndex) / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } } else if (pulledToY > range) { EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(), 1.f - ev.getX(activePointerIndex) / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } } if (mEdgeGlowTop != null && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) { ViewCompat.postInvalidateOnAnimation(this); } } } break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
          // 計算滑動速率
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
          // 大於最小的設定的速率,觸發fling
if ((Math.abs(initialVelocity) > mMinimumVelocity)) { flingWithNestedDispatch(-initialVelocity); } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) { ViewCompat.postInvalidateOnAnimation(this); } mActivePointerId = INVALID_POINTER; endDrag(); break; case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged && getChildCount() > 0) { if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) { ViewCompat.postInvalidateOnAnimation(this); } } mActivePointerId = INVALID_POINTER; endDrag(); break; case MotionEvent.ACTION_POINTER_DOWN: { final int index = ev.getActionIndex(); mLastMotionY = (int) ev.getY(index); mActivePointerId = ev.getPointerId(index); break; } case MotionEvent.ACTION_POINTER_UP: onSecondaryPointerUp(ev); mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId)); break; } if (mVelocityTracker != null) { mVelocityTracker.addMovement(vtev); } vtev.recycle(); return true; }

ACTION_DOWN

先看 down 事件,如果處於 fling 期間,那麼直接停止 fling, 接着會調用 startNestedScroll,會讓 NestedScrollView 作為子 view 去 通知嵌套父 view,那麼就需要找到有沒有可以嵌套滑動的父 view 。

    public boolean startNestedScroll(int axes, int type) {
        // 交給 mChildHelper 代理來處理相關邏輯
        return mChildHelper.startNestedScroll(axes, type);
    }


    public boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type) {
        // 找到嵌套父 view 了,就直接返回
        if (hasNestedScrollingParent(type)) {
            // Already in progress
            return true;
        }
        // 是否支持嵌套滾動
        if (isNestedScrollingEnabled()) {
            ViewParent p = mView.getParent();
            View child = mView;
            while (p != null) {  // while 循環,將支持嵌套滑動的父 View 找出來。
                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {
                    // 把父 view 設置進去
                    setNestedScrollingParentForType(type, p);
                    // 找到后,通過該方法可以做一些初始化操作
                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);
                    return true;
                }
                if (p instanceof View) {
                    child = (View) p;
                }
                p = p.getParent();
            }
        }
        return false;
    }            

可以看到,這時候主要就是為了找到嵌套父 view。當 ViewParentCompat.onStartNestedScroll 返回 true,就表示已經找到嵌套滾動的父 View 了 。下面來看下這個方法的具體邏輯:

    // ViewParentCompat  
    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
            int nestedScrollAxes, int type) {
        if (parent instanceof NestedScrollingParent2) {
            // First try the NestedScrollingParent2 API
            return ((NestedScrollingParent2) parent).onStartNestedScroll(child, target,
                    nestedScrollAxes, type);
        } else if (type == ViewCompat.TYPE_TOUCH) {
            // Else if the type is the default (touch), try the NestedScrollingParent API
            if (Build.VERSION.SDK_INT >= 21) {
                try {
                    return parent.onStartNestedScroll(child, target, nestedScrollAxes);
                } catch (AbstractMethodError e) {
                    Log.e(TAG, "ViewParent " + parent + " does not implement interface "
                            + "method onStartNestedScroll", e);
                }
            } else if (parent instanceof NestedScrollingParent) {
                return ((NestedScrollingParent) parent).onStartNestedScroll(child, target,
                        nestedScrollAxes);
            }
        }
        return false;
    }

這裏其實沒啥好分析,就是告訴父類當前是什麼類型的滾動,以及滾動方向。其實這裏可以直接看下 NestedScrollView 的 onStartNestedScroll 的邏輯。

//  NestedScrollView
    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,
            int type) {
     // 確保觸發的是垂直方向的滾動
return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; }

當確定了嵌套父 View 以後,又會調用父 view 的  onNestedScrollAccepted 方法,在這裏可以做一些準備工作和配置。下面我們看到的 是 Ns 裏面的方法,注意不是父 view 的,只是當作參考。

public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
    mParentHelper.onNestedScrollAccepted(child, target, axes, type);
   // 這裏 Ns 作為子 view 調用 該方法去尋找嵌套父 view。注意這個方法會被調用是 NS 作為父 view 收到的。這樣就 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, type); }

到這裏,down 的作用就講完了。

ACTION_MOVE 

首先是會調用 dispatchNestedPreScroll,講當前的滑動距離告訴嵌套父 View。

  public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
            int type) {
     // Ns 作為子 view 去通知父View
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type); } 

下面看下 mChildHelper 的代碼邏輯:

    /**
     * Dispatch one step of a nested pre-scrolling operation to the current nested scrolling parent.
     *
     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
     * method/{@link androidx.core.view.NestedScrollingChild2} interface method with the same
     * signature to implement the standard policy.</p>
     *
     * @return true if the parent consumed any of the nested scroll
     */
    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
            @Nullable int[] offsetInWindow, @NestedScrollType int type) {
        if (isNestedScrollingEnabled()) {
       // 獲取之前找到的嵌套滾動的父 View
final ViewParent parent = getNestedScrollingParentForType(type); if (parent == null) { return false; }        // 滑動距離肯定不為0 才有意義 if (dx != 0 || dy != 0) { int startX = 0; int startY = 0; if (offsetInWindow != null) { mView.getLocationInWindow(offsetInWindow); startX = offsetInWindow[0]; startY = offsetInWindow[1]; } if (consumed == null) { if (mTempNestedScrollConsumed == null) { mTempNestedScrollConsumed = new int[2]; } consumed = mTempNestedScrollConsumed; } consumed[0] = 0; consumed[1] = 0;
          // 調用嵌套父 View 的對應的回調 ViewParentCompat.onNestedPreScroll(parent, mView, dx, dy, consumed, type);
if (offsetInWindow != null) { mView.getLocationInWindow(offsetInWindow); offsetInWindow[0] -= startX; offsetInWindow[1] -= startY; } return consumed[0] != 0 || consumed[1] != 0; } else if (offsetInWindow != null) { offsetInWindow[0] = 0; offsetInWindow[1] = 0; } } return false; }

這裏主要是將滑動距離告訴 父 view,有消耗就會返回 true 。

    // ViewParentCompat
    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
            int[] consumed) {
        onNestedPreScroll(parent, target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
    }

其實下面的 onNestedPreScroll 跟前面的 onStartNestedScroll 邏輯很像,就是層層傳遞。

    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
            int[] consumed, int type) {
        if (parent instanceof NestedScrollingParent2) {
            // First try the NestedScrollingParent2 API
            ((NestedScrollingParent2) parent).onNestedPreScroll(target, dx, dy, consumed, type);
        } else if (type == ViewCompat.TYPE_TOUCH) {
            // Else if the type is the default (touch), try the NestedScrollingParent API
            if (Build.VERSION.SDK_INT >= 21) {
                try {
                    parent.onNestedPreScroll(target, dx, dy, consumed);
                } catch (AbstractMethodError e) {
                    Log.e(TAG, "ViewParent " + parent + " does not implement interface "
                            + "method onNestedPreScroll", e);
                }
            } else if (parent instanceof NestedScrollingParent) {
                ((NestedScrollingParent) parent).onNestedPreScroll(target, dx, dy, consumed);
            }
        }
    }

下面為了方便,沒法查看 NS 的嵌套父 View 的邏輯。直接看 Ns 中對應的方法。

    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
            int type) {
     // 最終也是 Ns 再傳給其嵌套父 View dispatchNestedPreScroll(dx, dy, consumed,
null, type); }

傳遞完了之後,就會調用  overScrollByCompat 來實現滾動。

    boolean overScrollByCompat(int deltaX, int deltaY,
            int scrollX, int scrollY,
            int scrollRangeX, int scrollRangeY,
            int maxOverScrollX, int maxOverScrollY,
            boolean isTouchEvent) {
        final int overScrollMode = getOverScrollMode();
        final boolean canScrollHorizontal =
                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
        final boolean canScrollVertical =
                computeVerticalScrollRange() > computeVerticalScrollExtent();
        final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS
                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
        final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS
                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);

        int newScrollX = scrollX + deltaX;
        if (!overScrollHorizontal) {
            maxOverScrollX = 0;
        }

        int newScrollY = scrollY + deltaY;
        if (!overScrollVertical) {
            maxOverScrollY = 0;
        }

        // Clamp values if at the limits and record
        final int left = -maxOverScrollX;
        final int right = maxOverScrollX + scrollRangeX;
        final int top = -maxOverScrollY;
        final int bottom = maxOverScrollY + scrollRangeY;

        boolean clampedX = false;
        if (newScrollX > right) {
            newScrollX = right;
            clampedX = true;
        } else if (newScrollX < left) {
            newScrollX = left;
            clampedX = true;
        }

        boolean clampedY = false;
        if (newScrollY > bottom) {
            newScrollY = bottom;
            clampedY = true;
        } else if (newScrollY < top) {
            newScrollY = top;
            clampedY = true;
        }

        if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
            mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
        }
     
        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);

        return clampedX || clampedY;
    }

整塊邏輯其實沒啥好說的,然後主要是看 onOverScrolled 這個方法:

   protected void onOverScrolled(int scrollX, int scrollY,
            boolean clampedX, boolean clampedY) {
        super.scrollTo(scrollX, scrollY);
    }

最終是調用 scrollTo 方法來實現了滾動。

當滾動完了后,會調用 dispatchNestedScroll 告訴父 view 當前還剩多少沒消耗,如果是 0,那麼就不會上傳,如果沒消耗完,就會傳給父 View 。

如果是子 View 傳給 NS 的,是會通過 scrollBy 來進行消耗的,然後繼續向上層傳遞。

    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
            int dyUnconsumed, int type) {
        final int oldScrollY = getScrollY();
        scrollBy(0, dyUnconsumed);
        final int myConsumed = getScrollY() - oldScrollY;
        final int myUnconsumed = dyUnconsumed - myConsumed;
        dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null,
                type);
    }

假設當前已經滑動到頂部了,此時繼續滑動的話,就會觸發邊緣的陰影效果。

ACTION_UP

當用戶手指離開后,如果滑動速率超過最小的滑動速率,就會調用 flingWithNestedDispatch(-initialVelocity) ,下面來看看這個方法的具體邏輯:

    private void flingWithNestedDispatch(int velocityY) {
        final int scrollY = getScrollY();
        final boolean canFling = (scrollY > 0 || velocityY > 0)
                && (scrollY < getScrollRange() || velocityY < 0);
     // fling 前問問父View 要不要 fling, 一般是返回 false
if (!dispatchNestedPreFling(0, velocityY)) {
       // 這裏主要是告訴父類打算自己消耗了 dispatchNestedFling(
0, velocityY, canFling);
       // 自己處理 fling(velocityY); } }

下面繼續看 fling 的實現。

    public void fling(int velocityY) {
        if (getChildCount() > 0) {

            mScroller.fling(getScrollX(), getScrollY(), // start
                    0, velocityY, // velocities
                    0, 0, // x
                    Integer.MIN_VALUE, Integer.MAX_VALUE, // y
                    0, 0); // overscroll
            runAnimatedScroll(true);
        }
    }

    private void runAnimatedScroll(boolean participateInNestedScrolling) {
        if (participateInNestedScrolling) {
            // fling 其實也是一種滾動,只不過是非接觸的
            startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
        } else {
            stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
        }
        mLastScrollerY = getScrollY();
        ViewCompat.postInvalidateOnAnimation(this);
    }

最終會觸發重繪操作,重繪過程中會調用 computeScroll,下面看下其內部的代碼邏輯。

    @Override
    public void computeScroll() {

        if (mScroller.isFinished()) {
            return;
        }

        mScroller.computeScrollOffset();
        final int y = mScroller.getCurrY();
        int unconsumed = y - mLastScrollerY;
        mLastScrollerY = y;

        // Nested Scrolling Pre Pass
        mScrollConsumed[1] = 0;
     // 滾動的時候,依然會把當前的未消耗的滾動距離傳給嵌套父View dispatchNestedPreScroll(
0, unconsumed, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH); unconsumed -= mScrollConsumed[1]; final int range = getScrollRange(); if (unconsumed != 0) { // Internal Scroll final int oldScrollY = getScrollY();
       // 自己消耗 overScrollByCompat(
0, unconsumed, getScrollX(), oldScrollY, 0, range, 0, 0, false); final int scrolledByMe = getScrollY() - oldScrollY; unconsumed -= scrolledByMe; // Nested Scrolling Post Pass mScrollConsumed[1] = 0;
        // 繼續上傳給父View dispatchNestedScroll(
0, scrolledByMe, 0, unconsumed, mScrollOffset, ViewCompat.TYPE_NON_TOUCH, mScrollConsumed); unconsumed -= mScrollConsumed[1]; }      // 如果到這裡有未消耗的,說明已經滾動到邊緣了 if (unconsumed != 0) { final int mode = getOverScrollMode(); final boolean canOverscroll = mode == OVER_SCROLL_ALWAYS || (mode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); if (canOverscroll) { ensureGlows(); if (unconsumed < 0) { if (mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity()); } } else { if (mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity()); } } }
       // 停止滾動   abortAnimatedScroll(); }      // 如果此時滾動還未結束,並且當前的滑動距離都被消耗了,那麼繼續刷新滾動,直到停止為止
if (!mScroller.isFinished()) { ViewCompat.postInvalidateOnAnimation(this); } }

到這裏,關於 Ns 的嵌套滑動就講完了。希望大家能夠對嵌套滑動有個理解。

閱讀 Ns 的源碼,可以讓你更好的理解嵌套滑動,以及事件分發的邏輯。

  本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

這台飛度飛起來了!超強颱風殺到,要注意什麼_網頁設計公司

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

太恐怖了趕緊躲進房間里吧可是我今天約了老漢吃飯答謝他上次推車的恩情呢那麼也就是說必須開車出去咯。好的,那就讓小編美美支招颱風天用車要注意什麼。首先如果你的車是飛度的話為了不讓它飛起來請拉上你的親朋好友坐車增加車重開玩笑颱風天汽車受損主要有2種情況第一,水淹颱風往往伴隨着強降雨目前的城市排水能力有限所以在很多地勢低洼的地方容易積水颱風天一旦把車子停在那些地方分分鐘變成落湯雞鐺鐺鐺鐺,一台泡水車誕生了第二,落物砸壞颱風一來各種亂七八糟的東西吹得滿天都是尤其是人家窗檯放的花花草草啊分分鐘砸下來車頂變形玻璃破碎第

據線人報道

颱風“薩瓦迪卡”就要抵達海南

sorry是颱風“莎莉嘉”

莎莉嘉是什麼來頭呢?

我們可以看看這個魔鬼的生平

10月13日莎莉嘉颱風生成

經過2天的卧薪嘗膽

10月15日莎莉嘉成長為颱風級

一天之內又晉陞為強颱風級

10月16日

這個魔鬼就殺入菲律賓呂宋半島

10月17日

莎莉嘉來到海南省萬寧市東偏南方大約570公里的南海中部

最強風力13級(38米/秒)

12級的大風就可以把列車吹翻

可以把20噸重的汽油罐拋到80米的高空

按照這個速度

10月19日將正式殺入廣東

沒錯,

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

也就是今天!

太恐怖了

趕緊躲進房間里吧

可是我今天約了老漢吃飯

答謝他上次推車的恩情呢

那麼也就是說必須開車出去咯?

好的,那就讓小編美美支招

颱風天用車

要注意什麼?

首先如果你的車是飛度的話

為了不讓它飛起來

請拉上你的親朋好友坐車

增加車重

開玩笑

颱風天汽車受損

主要有2種情況

第一,水淹

颱風往往伴隨着強降雨

目前的城市排水能力有限

所以在很多地勢低洼的地方容易積水

颱風天一旦把車子停在那些地方

分分鐘變成落湯雞

鐺鐺鐺鐺,一台泡水車誕生了

第二,落物砸壞

颱風一來

各種亂七八糟的東西吹得滿天都是

尤其是人家窗檯放的花花草草啊

分分鐘砸下來

車頂變形

玻璃破碎

第三,車被吹走了

如果一大早醒來

車子被吹走了

請立即抱緊你的男/女朋友

此時風力肯定非常大

當然有的人就能巋然不動

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

終於帶T了!這款SUV換上1.5T能否干贏H6和博越?_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

接着換2擋,即使轉速回落至1000轉的上下,但隨着充足扭矩的緊接而來,車輛的提速依然爽快利落。站在大部分手動擋駕駛者的角度,在擁堵、頻發紅燈起步的情況下,發動機的低扭不足是最為影響體驗的一點(沒有之一)。在經過伊寧縣道的一個又一個路口以後,基本可以確認這台1。

“如果CS75能夠搭載小排量渦輪增壓發動機,那我一定會把它列入我的購車名單當中。”在某次乘搭滴滴專車的過程中,一位長安CS75的車主如是告訴我。

更符合消費需求的1.5T

這位車主的心聲道出了長安CS75在產品布局上存在的不足—消費者需要排量更小、售價更低的渦輪增壓車型以面對日常家用代步的用車需求,而這正是CS75所缺乏的。確實,CS75的2.0L車型由於排量的原因,無法享受到國家的小排量優惠政策。而1.8T車型相對同級競爭車型,12.28萬的起售價又缺乏一定的吸引力。在目前10-15萬級別自主品牌SUV的消費者最看重的兩點核心—價格、油耗上,CS75的產品定位並沒有拿捏到一個較好的平衡。

客觀反映在銷量上,長安CS75在近年的4月、6月、7月的同比銷量均出現了一定程度的下挫。對於長安來說,亟需一個解決方案,以幫助CS75重新站回自主品牌SUV第一梯隊的位置。於是,售價10.58-12.38萬元的CS75 1.5T車型(手動擋)正式被推出了市場。

賽里木湖,既是試車也是賞景

當然,能夠幫助CS75重回第一梯隊,相比起更全面的產品布局,產品的質量或許顯得更為重要。“CS75新車型的動力總成表現如何?”是包括我在內的眾多汽車媒體所關注的重點。於是,在緊接着新車型上市的當天,長安便在新疆的伊寧市為CS75舉辦了媒體試駕活動。因蘋果而出名的伊寧,在10月較其他城市更早地進入冬季。試駕當天的氣度只有6℃,並且從酒店向賽里木湖進發的試駕路程,是逐漸往2000+海拔爬升的過程。不過就試驗一款1.5T發動機的動力性能而言,是一個很理想的環境。

(風景實在太美,容我亂入顯擺兩張)

一台全新設計的發動機

在小排量的政策引導下,目前大多數的緊湊型SUV的渦輪排量都設計為1.4T、1.5T。譬如1.4T的大眾途觀、1.5T的傳祺GS4、1.5T的哈弗H6。而受技術、成本的限制,不少的自主品牌會直接採用三菱的代號4A91T的1.5T發動機,雖然在穩定性上佔據優勢,但使得像缸內直噴等更先進的發動機技術無法在發動機上運用。也因此,CS75並沒有直接採購三菱的發動機方案,而是基於長安的H平台進行研發。當然,後來的試駕證明,這是一個正確的選擇。隨行的長安工程師告訴我:“早在4年前,長安便開始了這款1.5T發動機的研發工作,主要的研發工作由長安位於英國伯明翰的發動機研發中心所完成。整個發動機結構均為全新設計,與1.8T以及2.0L發動機都不盡相同”

這台發動機運用了不少如今主流的發動機技術,相對於不少的自主品牌又是先進的。譬如缸內直噴技術、可變排量機油泵、自動啟停等,在這些技術的結合下,這台1.5T發動機擁有了125kW的最大功率以及230N.m/1950-4500rpm的最大扭矩。動力數據並非1.5T發動機里最強的,但與合資品牌對標也未嘗不可。

這能勝任全面的城市路況

在中高海拔的情況下,預想的低扭不足的情況並沒有出現,1擋起步發動機轉速迅速拉升至1500轉的上下,CS75已經具備了一定明顯的提速感,在紅燈路口的多次試驗我更加肯定這一點。當然實際的提速速度比我感受到的更快,原因在後面再說。接着換2擋,即使轉速回落至1000轉的上下,但隨着充足扭矩的緊接而來,

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

車輛的提速依然爽快利落。站在大部分手動擋駕駛者的角度,在擁堵、頻發紅燈起步的情況下,發動機的低扭不足是最為影響體驗的一點(沒有之一)。在經過伊寧縣道的一個又一個路口以後,基本可以確認這台1.5T的小排量發動機能夠很好地勝任城市擁堵的使用環境。我與長安的工程師分享這一點,其表示,這台1.5T發動機的渦輪增壓部件,由博格華納所提供,由於其可靠性以及對小排量渦輪增壓發動機的適配度較高,像大眾等一線的合資車企亦同樣從其採購。

(上海某知名媒體人士)

由於近年來國家對西部的政策扶持,使得伊寧的高速公路網得到了充分的發展,連接伊寧市區與賽里木湖的連霍高速就是其中的一條暫新的高速公路。這條將近200公里距離的路程,過去或許需要開上半天甚至更久,而當天我們頂着120公里的限速前行,花費的時間也只有一個半小時。

(頗為壯觀的果子溝大橋)

頂着高速限速前行,很自然地把擋位掛上6擋。考慮到CS75的SUV定位,6擋變速箱的變速齒比並沒有被設計得過小。120公里/小時巡航時,轉速指針的位置大致在2700rpm的範圍。要是加深油門的開度,發動機也樂於把轉速往3000rpm以上突破以換取更好的加速能力,但是受制於小排量的天生性結構,,車速突破100公里/小時以後,腳下油門的积極程度便已經很難與提速感相協調。

讓人歡喜的靜音以及振動抑制

不過發動機這種長時間處於高轉速運轉轉態的情況,也讓我發現這台1.5T發動機在靜音性以及震動抑制方面有着讓人歡喜的表現。針對小排量發動機特有的高速運轉震動、噪音的短板,工程師為這台1.5T發動機配置了低噪靜音皮帶、低噪噴油器等相對應的靜音、降噪措施。震動抑制方面的效果是出色的,和2.0L發動機的感官感受相差不多,來自上海某知名媒體老濕和我持同樣的意見。

因為私人座駕比較爛的緣故,我一直對汽車的靜音性比較在意。而CS75的靜音表現則可以形容為同級1.5T車型的前列。試駕過程中,偷懶加減檔操作的我,經常把轉速拉升至3000rpm的水平,但此時的發動機噪音確實要比我座駕的發動機處於2000rpm時的表現還要出色。這也解釋了前面CS75在中低速時的加速能力要比人體感官感受要來得更強,很大程度上這台1.5T的暴躁被掩蓋了起來。如果這一點要對標,不少主流合資品牌的緊湊型SUV能夠成為目標對象。

槽點:讓新手犯難的起步熄火

從動力、靜音、震動這三個方面上,這台1.5T發動機的表現都打消了我預期的擔心。得以輕鬆、愜意地在賽里木湖遊玩,很大部分要歸結於CS75的功勞。但儘管如此,手動版的CS75仍然有一個表較明顯的槽點—起步容易熄火。整個的試駕過程中,我在起步階段熄火了2次。其餘試駕的媒體或多或少也存在這樣的情況。對於老司機而言,可以通過快速的熟悉去解決這一問題。而對於剛拿駕照的新手司機而言,則需要更為合理的自動補油程序。

一份目前自主品牌所缺少的實在

其餘升級,像30公里可開啟的360全景影像、自動駐車等新增配置不加以贅敘。但有一點我認為必要着重提及,那便是位於車內后視鏡上方的行車記錄儀USB預留接口。“許多消費者加裝行車記錄儀都必須拆開中控台,從保險盒取電,這個新增的USB借口能夠避免了這些麻煩。”長安的工程師表示,在原有車型的基礎上,新款車型在細節上做了更多合理性的改進。儘管問題細小,但當中卻能夠透露出一份目前自主品牌所缺少的實在。

比以往的CS75都更值得入手

接近2.0L車型的售價,與1.8T車型相差無幾的動力表現,CS75的1.5T車型比以往的車型都更能滿足自主品牌SUV消費者日常用車的消費需求。在過往,CS75是一款受到認可的好車。如今,這款好車又再進一步完善了自己。如果那位兼職滴滴快車的車主尚未換車,我想CS75的1.5T車型會在他的購車名單之內的。文末最後,也要感謝一路隨行的長安汽車工程師,其客觀公正的講解屬於自主品牌當中少見的一位。

(從長安工程師身上,能夠看到長安汽車的工科男性格)本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊