13萬落地 你可以買到質量好又省心的合資家轎有哪些?_貨運

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

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

雖然新款寶來相比老款寶來的長度有所增長,但是和同級別比起來,還是沒有什麼優勢。寶來的動力系統和朗逸比起來要精簡很多,發動機為1。6L 110馬力和1。4T 131馬力,匹配5擋手動、6擋手自一體和7擋雙離合變速箱。和桑塔納速騰一樣,寶來的後排地板中部凸起挺高的,影響舒適性。

今天給大家推薦三個高質量低油耗的合資緊湊型轎車,性價比都是很高的。

北京現代-領動

領動的車身尺寸為4610*1800*1450mm,軸距為2700mm,領動目前是現代集團最新的緊湊型轎車,也就是朗動的換代車型,這也是現代最喜歡的的多代同堂銷售。因為是最新款,所以領動的也是採用了最新的設計語言,同時加上自己是韓國車,所以顏值也是力壓全場。但從外觀上,領動已經可以征服很大一部分消費者了。

領動的動力系統為1.6L 128馬力 L、1.6L 130馬力 L4和1.4T 130馬力 L4,匹配6擋手動、6擋手自一體變速箱和7擋雙離合。其中領動的自動擋車型都採用了直噴發動機,動力輸出會更暢快,油耗也會相應的降低一點。其中1.6L自動擋車型的百公里油耗為7L多一點,表現很不錯。

不過遺憾的是領動的最低配依然沒有配備ESp車身穩定系統。前幾天一個朋友剛提了朗動,我說為啥不買領動呢?他說,朗動可以優惠2.5萬,領動可以么?突然感覺他說的好有道理,

※回頭車貨運收費標準

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

我竟無言以對。所以希望領動的優惠也能大一點。

一汽大眾-寶來

寶來的車身尺寸為4562*1793*1468mm,軸距為2614mm。雖然新款寶來相比老款寶來的長度有所增長,但是和同級別比起來,還是沒有什麼優勢。

寶來的動力系統和朗逸比起來要精簡很多,發動機為1.6L 110馬力和1.4T 131馬力,匹配5擋手動、6擋手自一體和7擋雙離合變速箱。和桑塔納速騰一樣,寶來的後排地板中部凸起挺高的,影響舒適性。不過寶來的口碑也是路人皆知的,耐用省油一直是大家對它的印象。小編一直覺得,如果寶來不是掛着大眾的標,最起碼還能再優惠一萬。

廣汽本田-凌派

凌派車身尺寸為4664*1750*1505mm,軸距是2650mm。雖然軸距沒達到2.7米,但是本田的偷空間技術也是赫赫有名的,同時作為中國特供車的身份,兩個因素加起來使得凌派的後排空間十分巨大,另外後排地板也比較平整,就算後排坐三個人也不會感到不舒服。

凌派的發動機為1.8L 136馬力 L4,變速箱為5擋手動和CVT變速箱。雖然1.8L不能參与購置稅減半的政策優惠,但是這套動力系統也是久經考驗,成熟耐用,另外凌派有換上了一套CVT變速箱,雖然是1.8L的排量,但是自動擋的百公里油耗只有7.7L左右。另外這套動力系統也是非常的平順。

最後,凌派全系標配剎車輔助、牽引力控制、ESp。作為日系車,這點還是比較實在的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

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

漢蘭達都要哭了 力帆新SUV連眾泰都嚇了一驚!_貨運

※回頭車貨運收費標準

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

4L自然吸氣發動機搭配手動變速器的動力組合,從網上渠道獲得2。4L車型的價格應該在8。68-12。98萬元,2。0T車型的價格為9。98-13。98萬元,還是很吸引人的。結語:力帆在自主品牌中不算出色,但是也算积極向上型,不過由於技術和製作工藝等限制,其產品質量和性能都很難擠進主流自主品牌當中,既然在外觀上下了那麼大工夫,何不走好質量和性能這個關卡呢,山寨之路還能走多遠,還是踏踏實實做好自己,不管怎樣,這款年底上市的力帆X80還是挺令人期待的。

力帆一家子從做摩托車辛辛苦苦發展到現在的汽車產業,也是不容易啊,大家印象最深的應該就是那款和MINI長得很像的力帆320了,這幾年都流行SUV了,二胎政策也開放了,咱們直接出一輛大7座SUV吧,於是漢蘭達躺槍了。

聽說還是力帆大公子親自參与設計的一輛車,如果拋開品牌不和豐田漢蘭達對比的話,這款X80的外觀看上去還是挺前衛時尚的,但國人的眼睛是雪亮的,這明明就是山寨了上一代漢蘭達,六邊形的進氣格柵與前大燈相互呼應,抄襲永無止境啊。

內飾從曝光圖片上看做得還算精緻吧,

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

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

傳統的T形布局中控台搭配着中央显示屏,整體給你比較規整的感覺,鍍鉻飾條和仿木飾板的增加營造了不錯的豪華氛圍,但是中控台仍保留了許多實體功能按鍵,用起來會不會有着眼花繚亂的感覺呢!

車身尺寸方面根據某網站的曝光是長寬高為4820*1930*1760mm,軸距2790mm,屬於標準身材,稍微比漢蘭達短了一點點,預計將提供5門5座和7門7座的版本可供消費者選擇,所以空間上面基本是無需太多顧慮了。

力帆X80主推是2.0T渦輪增壓發動機,最大功率141千瓦,搭配着6速自動變速器,與目前市場上不同品牌同排量的車型相比,差距不是很大,此外還有個2.4L自然吸氣發動機搭配手動變速器的動力組合,從網上渠道獲得2.4L車型的價格應該在8.68-12.98萬元,2.0T車型的價格為9.98-13.98萬元,還是很吸引人的。

結語:力帆在自主品牌中不算出色,但是也算积極向上型,不過由於技術和製作工藝等限制,其產品質量和性能都很難擠進主流自主品牌當中,既然在外觀上下了那麼大工夫,何不走好質量和性能這個關卡呢,山寨之路還能走多遠,還是踏踏實實做好自己,不管怎樣,這款年底上市的力帆X80還是挺令人期待的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

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

澳洲學生遇上重慶_貨運

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

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

兩年前我帶了一批澳洲墨爾本的大學生,到重慶大學參加中澳大學生論壇,同時學生們還進修短期的中文課程。論壇主題是兩國青年如何詮釋彼此的文化。幾個人都喜歡上了重慶。

一次路經一個廣場,音樂喧鬧,原來是大媽們在跳廣場舞。我向澳洲學生解釋,此乃體育鍛煉的一種方式。學生回應說:不僅僅是鍛煉,也是熱愛生活。這裏的人都爭取身心快樂。為什麼不呢?

重慶號稱霧都,當地只有三分之一的日子天氣晴好,一般都霧蒙蒙,所以柳宗元說,狗會吠日,因為總也見不到太陽,不認識。“蜀犬吠日”出自柳宗元的《答韋中立論師道書》。

一年三分之二的時間見天不見日,可是人民仍然安居樂業。外人不解甚至納悶:不見天日多鬱悶。重慶的朋友聞言大笑。天府之國,人們都懷着淡淡的喜悅。不僅重慶本地人熱愛家鄉,外鄉人,甚至外國人也喜歡這裏。

學生們都認真準備論壇發言。

重慶大學外語系的學生認為只有掌握了中國古典文化精華,古為今用,方能在國際交流中真正認識到自己的長項,同時了解自己的不足,培養自我反省和獨立思考的精神。這個觀點在論壇上得到澳洲學生的共鳴。他們認為中國悠久的歷史和哲學很有意思!

對此我深有體會。平時講課時就可以看出一些端倪:越來越多的澳洲學生選修中文,他們渴求了解中國文化,最初的好奇已經躍升為迫切的求知欲。由於中國持續增強的影響力,不僅中文成為墨爾本機場等公共場所的標示語言之一,而且澳洲也開始慶祝中國的農曆新年。

一位男生臉色蒼白來上課。聽我講情緒和健康的關係。講到思傷脾。有人問,脾是什麼?在哪裡?然後低頭查電腦或手機。我解釋說,脾是中醫的後天之本,和胃一起主消化收納運行。如果你思念太甚,思慮過多,脾就疲憊,慢慢處於懶惰狀態,不好好工作了。那男生眼睛一亮,說,怪不得!我最近沒有什麼食慾,可能太想念自己的女朋友了。教室里爆發出一陣鬨笑。

他還專門給我看買來的書法配圖片的書,显示出對漢字的濃厚興趣。他尤其喜歡漢字中隱藏的抽象圖形之美。

講到悲傷肺,祖籍伊朗的馬克略有所悟,說,澳洲很多哮喘病人,肺不好,他們中一些人看起來就抑鬱不樂。也許讓他們高興起來,病情就可以緩解。大家對他的大膽假設抱支持態度。馬克的專業是經濟,由於會說一些中文,畢業后他順利得到澳洲第一大銀行的工作,可以接待來自中國的客戶。

※回頭車貨運收費標準

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

還有幾個學生最喜歡漢字中蘊藏的歷史。讓他們畫車,我說,如果是古代華麗的馬車,就要有華蓋。然後我把“車”字的演變寫出來。學生明白了“車”乃截取了最有代表性的部分——輪子和連接輪子的橫輻。

我講解“明天”:為什麼從字面上看,“明天”是光明的一天?我的理解是:因為今天我們還要經歷夜晚,之後天亮起來,就是明天。中文奇妙,比起一個技術上的時間,它充滿了光與暗對比的哲學,充滿了形象,充滿了生命要素,充滿感恩和希望。

講到道家,問學生小草和大樹誰更剛強。當然是大樹,眾人說。那麼風暴來了呢?一位女生略加思索后回答,風暴中大樹可能被連根拔起,小草卻安然無恙。立即有男生反駁:那如果大樹倒下時砸爛了小草呢?說完后又覺得只是小概率,自己先笑了。

給我留下深刻印象的是兩國大學生都對文化交流抱持開放的態度,認為需要更新對彼此的認識,打破常規。

湯姆是一位工程專業的學生。他的中國之行包括訪問許多重要工程項目,比如世界上最大的水壩三峽。這位未來的工程師也是頗感性的人,他很喜歡在樹木上掛滿紅布條的許願林,對那些同心鎖也情有獨鍾,因為他相信每一枚同心鎖背後都藏着一個感人的故事。他還攜帶了一些母親的骨灰,把它撒在張家界天門山的最高峰。根據傳說,天門山是生界和靈界的門關。他在自己的微信中介紹了天門山。

坐上重慶火鍋的宴席,幾個學生面對桌子中央的鮮花和滿桌豐富的食材興高采烈,感慨這裏的宴席很有意思,兼有娛樂和與朋友交流的功能。有學生對微信支付相當喜歡,打聽怎樣才能把錢放入微信錢包。

坐高鐵也是神奇的體驗。時速達到300公里以上的火車目前澳洲還沒有,當然感覺不一樣,好像貼着地面飛。我的學生說,關鍵是很穩,簡直感覺不到火車在高速運行。我回憶起20多年前的綠皮普快列車,時速不過120公里左右,由於車次少,站票多,上車時擁擠不堪,還有小朋友被從窗口塞進去。現在的高鐵提速接近3倍,大大方便了出行。“春風得意馬蹄疾,一日看盡長安花”不在話下。

秩序來自於資源的合理配置。我記得十幾年前公共汽車車少人擠,保證準時上班是件讓人頭疼的事。現在車次增多,準點率也大大提升。退休人士可以免費乘坐公交車,這是值得稱讚的一大福利,即使在澳洲這樣人均資源配置很高的國家,退休人士也僅僅是享受半價票而已。

每次回國探親,我都對祖國的活力和建設速度感到驚訝。當然,對老城區的保護也相當重要,因為那裡承載了我們兒時的記憶。我的家鄉有泉城之美譽,每次回去我都要到泉水旁、到大明湖畔徜徉,療愈自己在海外的思鄉之苦。

連續兩周陰雨天氣,周末放晴,天藍得深邃通透。重慶人紛紛外出曬太陽。我注意了一下,沒有看到蜀犬吠日的情況。看來古人也是喜歡誇張的。

對於中國哲學中的天人合一境界,幾個學生開始慢慢領悟。比如課堂上講到“留得殘荷聽雨聲”,一位女生髮出讚歎:哇,這不是很美嗎?

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

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

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

方錦龍:我做國樂一定要好玩_貨運

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

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

元旦剛過,各大電視台的跨年晚會相繼落幕,琵琶演奏家方錦龍又在網上火了。他在bilibili視頻網站(網友昵稱B站)舉辦的跨年晚會上的國樂跨界串燒表演令人大開眼界,“壯哉我大國樂”“雞皮疙瘩掉一地”“老爺子太厲害了”等引網友好評不斷。方錦龍接受記者的獨家專訪,講述了他這個節目的創意和幕後故事。

現場:古今中外樂器玩了個遍

方錦龍是一位“網紅”琵琶演奏家,此前他悠閑彈奏《琵琶語》的視頻給網友們留下深刻印象,隨後網友們又發現,他能演奏琵琶、笛、簫、二胡、骨笛等上百種樂器。這次參加B站的跨年晚會,他和當晚演出總監趙兆指揮的管弦樂團合作了一個十分多鐘的節目,再一次燃爆網絡。

在節目中,他一口氣彈奏了琵琶、高音琵琶、尺八、沖繩三味線等多種樂器,甚至還拿出一個像“鋸”的一樣的琴,左手拿着“鋸”,右手像拿着二胡弓子一樣演奏。“那是鋸琴,它來源於幾百年前意大利的伐木工人。”方錦龍介紹,B站邀請他登台這場晚會時,他知道B站的受眾都是年輕人,就想把古今中外的樂器向觀眾展示。

拿着古今中外的樂器,方錦龍和樂團演奏曲目都很跨界。《滄海一聲笑》《男兒當自強》兩首歌曲和中國古曲《將軍令》“嫁接”在一起,經過全新的編曲,來自不同曲目的旋律彷彿一問一答。演奏到高潮,方錦龍瘋狂掃弦,舞台上有電吉他迎合,氣勢恢宏又毫不違和。“琵琶本身就是有俠客之風的樂器,俠肝義膽是中國的民族精神,它和國樂的傳統精神是一致的,看得出來網友們也都很喜歡。”方錦龍有些得意。

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

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

讓網友們更驚訝的是,這個節目的表演是帶劇情的。當美國鄉村民謠《哦,蘇珊娜》的旋律在樂隊中響起,方錦龍就在手邊的多件樂器中反覆挑選,彷彿沒一件樂器趁手,他乾脆不演奏任何樂器,而是用手在自己臉上“波兒”“波兒”地彈出聲音,每一聲的音高都不同。而當指揮說要演奏印度的樂器,方錦龍說:“印度的?那得有咖喱味,我來給你試試這個樂器,你看是不是有咖喱味。”說著,他用艾斯拉吉、西塔爾琴這兩種樂器演奏起來,隨後一大片彈幕飄過,幾乎都寫着:“果然有咖喱味!”

幕後:音樂的“樂”=快樂的“樂”

“這是我故意這麼設計的,我演奏樂器光演不行,還得說,這是我的‘方式脫口秀’。”方錦龍說,節目在策劃階段,他就想在其中加一點“小品”的表演成分,“從頭到尾地演,觀眾會疲憊,不知道我們在彈什麼,那我就輕鬆地講出來,這樣觀眾既能了解很多知識,還會覺得有趣。”

“一定要好玩”是方錦龍從事音樂一向秉持的原則,這是他深受觀眾喜愛的原因。對此他有自己的思考:“我們的音樂已經走出國門,但還未必走進國人的心裏。”方錦龍說,我們有那麼多大師級別的演奏家,在國際上打出中國的名號,給中國爭光。“但是在不少人心裏,很多音樂還停留在殿堂上,國人覺得它高雅,但可能不覺得這些音樂能走進生活。”方錦龍偏偏用傳統樂器演奏時尚的歌曲,甚至把自己當“人型樂器”,在自己臉上彈出旋律,就是為了讓大家覺得,“音樂的‘樂’和快樂的‘樂’其實是一回事”。

不過,用古典音樂或國樂演奏流行歌曲,一直被部分業內人士認為太簡單、太俗,甚至有些“掉價”,但方錦龍不這麼認為。“不要用這種觀念看待老百姓,這是老百姓喜歡的東西,尤其受年輕人喜歡,如果國樂永遠曲高和寡,最後肯定就沒有人傳承了。”方錦龍常常感慨,他自己從事音樂已經42年,但出名是在近10年。“前30年我也做傳統的協奏曲、組曲,這很重要,幫我打下了基礎,但近10年我開始跨界,這才走了出來。”

也就是在近10年中,他發覺越來越多的年輕人其實深愛着國樂,每次看到國樂的跨界演出都非常興奮,被傳統樂器的表現力折服。他這個被網友們稱為“老爺子”的人,也漸漸熟悉了年輕人在彈幕上的習慣用語:“收下我的膝蓋!”“這段演出,建議上春晚。”感受到觀眾的喜愛,方錦龍也跟着興奮起來:“一方水土養一方人,在中國長大的年輕人就有喜歡國樂的基礎,國樂的市場很大,但是需要開發。我們就是要找准年輕人喜歡什麼,把他們吸引過來!”(記者 韓軒)

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

※回頭車貨運收費標準

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

不到10萬合資車配6AT!顏值逆天年輕人都想買?_貨運

※回頭車貨運收費標準

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

什麼。黑內飾是為與外觀相呼應了凸顯運動感。得了吧,這套內飾在設計也太平庸了,完全沒有外觀那種驚艷的感覺。配置歷來的韓系車的拿手好戲,悅納也不例外。主打年輕定位的悅納將蘋果的Carplay及百度Carlife手機互聯功能以及最新的人機交互系統集成在中控的七寸大屏內,不僅如此它還搭載了諸如智能后尾箱、H-band智能手環鑰匙等高科技裝備,這無疑是給像我這樣的年輕人提供了極大的便利。

近一兩年現代品牌似乎在國內市場的表現都不太盡如人意,自瑞納推出后,現代在國內好長時間一直沒能找到更好的換代車型。或許是因為主打家用市場的競爭對手太多,現代經過了一段“漫長”的考慮終於將一款主打年輕運動的小型車——VERNA“悅納”推向了市場。

細心的朋友肯定留意到了“VERNA”不就是北京現代瑞納的英文名怎麼變悅納了?其實這很好理解,就像當時的“Elantra”伊蘭特一樣,改款車型中文名改為悅動一樣,這就可以名正言順兩代同堂新老車型一同搶佔市場了。

車型定位什麼的我們可不關心,“VERNA”悅納駕駛起來到底怎樣才是最重要的,是騾子是馬,還得拉出來遛遛。多說無益,趕緊上車。此次我們試駕的目的地位於美麗的濱海城市—煙台,藉著這座城市的陽光與海灘我們將在此與悅納來一回浪漫邂逅。

外觀設計與原型車瑞納相比,悅納沒有了那種圓潤可愛、平易近人的感覺,換之而來的是更為凌厲的線條,前臉微微上揚的車燈造型搭配上鍍鉻裝飾件,顯得格外精緻。不得不承認的是悅納在外觀設計上,比起同期的致炫、威馳等車都要好看太多了。

我是一個喜歡揪細節的人,悅納設計前衛的可遠不止是前臉。來到車身側面,從翼子板一直延伸到車尾的腰線配合上溜背的造型,讓它看起來頗具COOpER車型的運動氣息。

如果你覺得悅納既然擁有如此靚麗的外觀,內飾的設計理應也十分出彩,那我想說這可能要讓你失望了。悅納的內飾走的是居家的風格,上黑下灰的內飾配色也顯得較為沉悶。什麼?黑內飾是為與外觀相呼應了凸顯運動感?得了吧,

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

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

這套內飾在設計也太平庸了,完全沒有外觀那種驚艷的感覺。

配置歷來的韓系車的拿手好戲,悅納也不例外。主打年輕定位的悅納將蘋果的Carplay及百度Carlife手機互聯功能以及最新的人機交互系統集成在中控的七寸大屏內,不僅如此它還搭載了諸如智能后尾箱、H-band智能手環鑰匙等高科技裝備,這無疑是給像我這樣的年輕人提供了極大的便利。眾多的特色配置還並非是頂配車型專屬,這點更為難得。

如果只是替代鑰匙或許H—Band手環對於部分消費來講還不夠吸引力,但若是配合上智能尾箱的使用那絕對是“逼格滿滿”,試想一下你和女友去完超市手裡捧着採購的物品只需站在車尾處3秒尾箱就會自動打開,免去了掏鑰匙按車門的動作,簡直就是為您掙足了面子有木有!

說了這麼多,悅納的配置是夠豐富了,但開起來又是怎樣呢?抱着滿懷期待的心情,我們把悅納開上了濱海大道。本次我們試駕的車為1.6L排量的車型,悅納保持了韓系車一貫的舒適性風格,動力輸出線性自然,值得一提的是本次悅納所搭載的變速箱為6擋手自一體變速箱,這可比同級別使用4AT/CVT的車型有了不小的優勢。

日常駕駛,悅納發動機的噪音可以說是微乎其微,如果不下車在車上你甚至會懷疑它到底有沒有打着火,這真是要給它點個大大的贊!

【總結】悅納可以說定位非常準確,年輕帥氣的外觀,豐富的配置,在試駕完悅納后,編者可以說是被悅納深深所吸引,它不僅是一輛車,更是年輕人的潮流玩物。雖說如此,但要知道在小型車這個級別,致炫、飛度等車可都不是善茬,悅納要想在從中突圍一個合理的定價還是關鍵。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

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

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%。根據研二轉專業的同學反饋來看,作用很大。

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

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

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

透過 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 的源碼,可以讓你更好的理解嵌套滑動,以及事件分發的邏輯。

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

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

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

文物里的唐都長安人生活_貨運

※回頭車貨運收費標準

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

  展出的文官俑。

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

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

  参觀者在觀展。

  近日,陝西西安博物院“樂居長安—唐都長安人的生活”展開展。本次展覽從西安博物院11萬餘件館藏文物中,特別遴選出280餘件/組唐代精品文物,分別圍繞唐長安的“城、人、衣、食、行、娛”等內容,對唐都長安人的生活進行了全面的再現和闡釋。新華社記者 劉瀟攝

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

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

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

被纏上了,小王問我怎麼在 Spring Boot 中使用 JDBC 連接 MySQL_貨運

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

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

上次幫小王入了 Spring Boot 的門后,他覺得我這個人和藹可親、平易近人,於是隔天小王又微信我說:“二哥,快教教我,怎麼在 Spring Boot 項目中使用 JDBC 連接 MySQL 啊?”

收到問題的時候,我有點頭大,難道以後就要被小王纏上了?

沒等我發牢騷,小王就緊接着說:“二哥,你先別生氣,上次你幫了我的忙后,我在心裏感激了你一晚上,想着第一次遇到這麼親切的大佬,一定要抱緊大腿。。。。。”

馬屁拍到這份上,我的氣自然也就消了。隨後,我花了五分鐘的時間幫他解決了苦惱,沒成想,他又發給我了一個小紅包,表示對我的感謝。並建議我再寫一篇文章出來,因為他覺得像他這樣的小白還有很多。沒辦法,我關上門,開了燈,開始了今天這篇文章的創作。

01、初始化 MySQL 數據庫

既然要連接 MySQL,那麼就需要先在電腦上安裝 MySQL 服務(本文暫且跳過),並且創建數據庫和表。

CREATE DATABASE `springbootdemo`;
DROP TABLE IF EXISTS `mysql_datasource`;
CREATE TABLE `mysql_datasource` (
  `id` varchar(64NOT NULL,
  PRIMARY KEY (`id`)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

02、使用 Spring Initlallzr 創建 Spring Boot 項目

創建一個 Spring Boot 項目非常簡單,通過 Spring Initlallzr(https://start.spring.io/)就可以了。

勾選 Lombok、Web、MySQL Driver、Actuator、JDBC 等五個依賴。

1)Lombok 是一種 Java 實用工具,可用來幫助開發人員消除 Java 的一些冗餘代碼,比如說可以通過註解生成 getter/setter。使用之前需要先在 IDE 中安裝插件。

2)Web 表明該項目是一個 Web 項目,便於我們直接通過 URL 來實操。

3)MySQL Driver:連接 MySQL 服務器的驅動器。

4)Actuator 是 Spring Boot 提供的對應用系統的自省和監控的集成功能,可以查看應用配置的詳細信息,例如自動化配置信息、創建的 Spring beans 以及一些環境屬性等。

5)JDBC:本篇文章我們通過 JDBC 來連接和操作數據庫。

選項選擇完后,就可以點擊【Generate】按鈕生成一個初始化的 Spring Boot 項目了。生成的是一個壓縮包,導入到 IDE 的時候需要先解壓。

03、編輯 application.properties 文件

項目導入成功后,等待 Maven 下載依賴,完成后編輯 application.properties 文件,配置 MySQL 數據源信息。

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springbootdemo
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

1)spring.datasource. 為固定格式。

2)URL 為 MySQL 的連接地址。

3)username 為數據庫的訪問用戶名。

4)password 為數據庫的訪問密碼。

5)driver-class-name 用來指定數據庫的驅動器。也可以不指定,Spring Boot 會根據 URL(有 mysql 關鍵字) 自動匹配驅動器。

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

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

04、編輯 Spring Boot 項目

為了便於我們操作,我們對 SpringBootMysqlApplication 類進行編輯,增加以下內容。

@SpringBootApplication
@RestController
public class SpringBootMysqlApplication {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @RequestMapping("insert")
    public String insert() {
        String id = UUID.randomUUID().toString();
        String sql = "insert into mysql_datasource (id,name) values ('"+id+"','沉默王二')";
        jdbcTemplate.execute(sql);
        return "插入完畢";
    }

}

1)@SpringBootApplication、@RestController、@RequestMapping 註解在[之前的文章]()中已經介紹過了,這裏不再贅述。

2)@Autowired:顧名思義,用於自動裝配 Java Bean。

3)JdbcTemplate:Spring 對數據庫的操作在 jdbc 上做了深層次的封裝,利用 Spring 的注入功能可以把 DataSource 註冊到 JdbcTemplate 之中。JdbcTemplate 提供了四個常用的方法。

①、execute() 方法:用於執行任何 SQL 語句。

②、update() 方法:用於執行新增、修改、刪除等 SQL 語句。

③、query() 方法:用於執行查詢相關 SQL 語句。

④、call() 方法:用於執行存儲過程、函數相關 SQL 語句。

本例中我們使用 execute() 方法向 mysql_datasource 表中插入一行數據 {id:uuid, name:'沉默王二'}

05、運行 Spring Boot 項目

接下來,我們直接運行 SpringBootMysqlApplication 類,這樣一個 Spring Boot 項目就啟動成功了。

這時候,我們可以直接瀏覽器的 URL 中鍵入 http://localhost:8080/insert 測試 MySQL 的插入語句是否執行成功。很遺憾,竟然出錯了。

該怎麼辦呢?這需要我們在連接字符串中顯式指定時區,修改 spring.datasource.url 為以下內容。

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springbootdemo?serverTimezone=UTC

重新運行該項目后再次訪問,發現數據插入成功了。

為了確保數據是否真的插入成功了,我們通過 Navicat(一款強大的數據庫管理和設計工具)來查看一下。

情況不妙,中文亂碼了。該怎麼辦呢?需要我們在連接字符串中顯式指定字符集,修改 spring.datasource.url 為以下內容。

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springbootdemo?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC

重新運行該項目后再次訪問,發現中文不再亂碼了。

快給自己點個贊。

06、鳴謝

我是沉默王二,一枚有趣的程序員。如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回復【666】更有我為你精心準備的 500G 高清教學視頻(已分門別類)。

本文 GitHub 已經收錄,有大廠面試完整考點,歡迎 Star。

原創不易,莫要白票,請你為本文點個贊吧,這將是我寫作更多優質文章的最強動力。

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

※回頭車貨運收費標準

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

年畫“圈粉”路在何方?_貨運

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

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

“如果年畫進行創新,你會喜歡什麼樣的年畫?為什麼會喜歡它?”

“我覺得你一定不是年輕人。”“現在的年輕人多喜歡‘傳統文化’。”……

這是一次有意思的採訪。當記者對遠在廣東佛山的一位“90后”孫璐璐拋出這樣的問題時,就被“呼之欲出”的代溝感震得“花枝凌亂”。自認為通過工作接觸,已經潛入“00后”的審美世界,而又得以在眾“90后”同事的熏陶中了解了年輕人的審美趣味,但這次的採訪才讓記者發覺,過往一切認知都只是皮毛。

孫璐璐說:“現在的年輕人多喜歡‘傳統文化’,覺得李寧酷斃了,漢服超級美,大白兔奶糖的香水、六神花露水的雞尾酒就是流行元素符號……”

從一番激動的言辭中,記者依稀看到當下年輕人對傳統文化那份堅定的認同。這些年,漢服熱、懷舊復古風等潮流風行,讓越來越多的人重新感受到傳統的別樣韻味。傳統文化復蘇、文化自信增強,在這樣的大背景下,作為與傳統文化息息相關的非遺,應該抓住時機,重新綻放新顏。比如,曾在春節年俗中不可或缺的年畫,如何在保留美好文化內涵的前提下,另闢蹊徑,重振春節傳統年俗?

嫁接新平台興起來

在年畫的發展歷史中,也曾起起落落。年畫內容不斷更迭、不斷充盈,跟進時代的步伐。1949年後,年畫曾迎來小陽春。那時,許多專業畫家加入年畫的創作隊伍,打破了舊年畫的一些固定模式,大膽借鑒其他畫種的表現手法,使年畫的面貌煥然一新。

時代在變,人們的審美觀念在變。傳統文化雖好,卻不能囿於過去的框架止步不前,在萬象更新的時代進程中,傳統文化需要年輕化的表達。基於這樣的理念,清華大學等高校與傳承人結對子,走出了一條年畫與時代接軌的新路子。

2019年,在文化和旅遊部非物質文化遺產司的指導與支持下,由傳統工藝與材料研究文化和旅遊部重點實驗室(清華大學)主辦,清華大學美術學院視覺傳達設計系和繪畫系共同承辦的清華大學年畫日新創作營,精選了天津楊柳青、蘇州桃花塢、山東高密、山東楊家埠、陝西鳳翔、河北武強等11個代表性年畫產地的年畫人與設計師、美術創作者組成團隊,在10周的時間里創作出作品48件(套),後期成果將投入商業渠道。

創作營的成果首先在北京國際設計周上展示發布,經過3個月的不懈努力,最終落地京東年貨節。佛山、開封、楊柳青、武強、綿竹、灘頭、高密七大年畫項目帶着濃郁的地域特色,將祥和喜慶的產品帶進電商平台。除了藉助京東非遺頻道銷售年畫和年畫衍生品,“萌萌噠”的年神當起了京東數碼產品的“導購”,一些年畫項目還與戴爾、Kindle、華為等知名品牌深度合作,推出了富有中國風味的春節禮盒。

清華大學美術學院副教授、藝術史論系主任陳岸瑛說,這次嘗試的成功說明古老的年畫並非註定落伍於時代,而是具有強大的發展潛力,特別是在新媒體時代,只要把年畫中的美好寓意挖掘出來,與人民群眾對美好生活的嚮往結合起來,年畫就不愁沒有粉絲。

清華大學年畫日新創作營營員石彥敏創作的楊柳青年畫《連年有餘》

助力老手藝活起來

有一件事曾讓楊柳青木版年畫國家級代表性傳承人霍慶有很有感觸,2015年他在天津圖書館舉辦個人年畫藝術展,來看展覽的人不少,但普遍是中老年人,幾乎沒有年輕人,更別提小朋友了。“這給我一個提示,要想讓年畫藝術傳承發展下去,就應該思考如何把孩子們吸引過來。讓年畫走到孩子身邊,陪伴下一代成長。”霍慶有說。

年畫的遠離,帶來的是年味的變淡,不僅是孩子、年輕人對它越來越陌生,曾經充盈民間的紅火年味也不知所蹤。在過去,過年貼年畫,圖的是喜慶吉利,蘊含的是祈福祝願。在那個穿新衣戴新帽、鞭炮聲聲的年節里,我們用一張張年畫集結普天下的喜慶顏色,把一段叫作“年”的時光裝扮起來。如今,物質生活越來越豐富,傳統的年俗年味卻越來越淡了。

時代的車輪滾滾向前,現代印刷術的出現,令傳統印刷的木版年畫受到了衝擊,有的地區年畫一度失傳。2002年,還是一位生意人的張榮強,因緣際會接到了數十年來投身文化遺產搶救的文化學者馮驥才的電話。那時,馮驥才想去尋找四川夾江年畫,卻發現已經沒有可以製作年畫的師傅了。一直酷愛美術的他,決定將精力投入到拯救夾江年畫上。

2010年,他拜夾江年畫老字號作坊“董大興榮”傳人董貴中為師,希望盡最大努力讓這項傳統文化繼續傳承下去。但這條路走起來比想象中更難。起初,擺在張榮強面前的難題是幾乎沒有可利用的資源,沒有夾江年畫製作流程的文字記載,也沒有會實際操作的師傅,甚至連照片都寥寥無幾。此後,張榮強走訪了大量民間手藝人,並不斷尋找相關資料,經過反覆推敲、多次修改,終於成功恢復了夾江年畫的木版套色印刷技藝,並復刻出《鯉魚跳龍門》《福祿宮》《榮華富貴》《財源湧進》《陳姑趕潘》等近30張經典年畫作品。

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

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

福美祥作坊創作的灘頭年畫《鼠慶豐年》

轉換新語境潮起來

中國人民大學美學與現代藝術研究所研究員張成源在研究東方美學與年畫的關聯時發現,年畫蘊含的東方美學內涵非常豐富,它包含祈禱、健康、豐收、忠孝等。而這些寓意是古今相通的,留存於每一代國人的思想中。因此,年畫重回春節、重回現代生活無需刻意,只要將這一內涵重新挖掘出來,轉換新的話語形式,年畫依然會受到人們的青睞。

在新一代年畫傳承人中,年畫女俠kk的網名似乎比她劉鍾萍的本名更具傳播效應。她當旅遊講解員時與佛山木版年畫結緣。原先的師兄師姐都各尋出路去了,給師父打下手的劉鍾萍卻留了下來。師父馮炳棠是佛山唯一一位掌握木版年畫全套工序的老藝術家,“唯一”二字讓劉鍾萍在不知不覺中對這份工作有了擔當。師父故去后,她挑起了傳承佛山木版年畫的重任。

年畫需要傳承,也需要創新。為了將傳統年畫與現代需求嫁接,她把傳統技藝與現代文化相結合,創作出“諸神復活”系列年畫,受到了市場的歡迎,而傳統年畫與新潮包裝的結合,也迅速俘獲了年輕人的心。起初有人來求購姻緣年畫,受到啟發,她將喜神和合二仙賦予“脫單神器”的新闡釋,頓時使傳統年畫穿越時空與現代接軌。之後,“一個億小目標的財神”“考神狀元及第逢考必過”“天姬送子兒女雙全的二胎神器”“新房入夥鎮宅神器紫薇大神”等系列相繼面世。

劉鍾萍說:“過去的傳統年畫很難吸引到年輕人,我就讓年畫跟他們的生活發生聯繫,他們帶着願望而來,我就負責把他們的願望通過年畫的寓意傳達出去。”

章昉創作的綿竹年畫《老鼠嫁女》

清華大學美術學院教授唐薇創作的佛山年畫《子開鴻蒙》

傳統文化並不缺市場,缺少的是我們重新打扮它的用心。一個鄉村女孩李子柒,將恬靜、本真的鄉村生活用鏡頭表現出來,在互聯網上贏得了國內外網友廣泛的共鳴。因此,承載着人們對美好未來憧憬和嚮往的年畫也可以迎來複興,只要更多力量聚集於此,並賦予年畫一些創新發展的思路和內容,年畫“圈粉”時機來了。

傳統文化並不缺市場,缺少的是我們重新打扮它的用心。一個鄉村女孩李子柒,將恬靜、本真的鄉村生活用鏡頭表現出來,在互聯網上贏得了國內外網友廣泛的共鳴。因此,承載着人們對美好未來憧憬和嚮往的年畫也可以迎來複興,只要更多力量聚集於此,並賦予年畫一些創新發展的思路和內容,年畫“圈粉”時機來了。

專家觀點

中國人民大學美學與現代藝術研究所研究員 張成源

科技的日新月異不會停下前進的腳步,傳統文化的熏陶也應注重其形式。我們要將其中的倫理道德、家風家訓、行為底線、人格境界提煉出來,用新的形式讓它重新被人們接受。在現代社會中,不能強求年畫的量化,因為傳統技藝無法規模生產,但是年畫衍生品可以。年畫也許是小眾文化,年畫衍生品卻可以是大眾文化。因此,當我們在極力調動眾多力量加入年畫的創新中時,欠缺的不僅是年畫衍生品的創作人才,更重要是的消費人群對它的認識。

中國傳媒大學文化產業管理學院副教授 楊 紅

自2019年開始,圍繞“年畫重回春節”這一主題,非遺傳承人與各類互聯網企業、文化創意團隊合作,開始藉助新媒體開發年畫拼圖微信小遊戲、年畫體驗類H5、年畫賀歲動漫、年畫音頻故事課、年畫微信表情包等多種形態的年畫主題創新應用,傳統年畫成為了春節里熱門的網絡傳播內容。

這些嘗試說明,儘管不少與節日相關的非物質文化遺產在當代的應用場景逐漸消失,但它們卻可以藉助網絡與創意,與我們再次相遇,攜帶着不可或缺的文化意義重回節日場景。在這個過程中,給傳統文化適當“減負”,為文化普及適當“減壓”,讓年畫等越來越多傳統IP在當代實現全民認知與共享,是新時代弘揚中華優秀傳統文化的重要路徑。(杜潔芳 王學思)

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

※回頭車貨運收費標準

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