android自定義圖表的手勢(shì)處理 - ViewTransHelper工具類

? ? ? ?本文介紹自己寫的一個(gè)處理手勢(shì)的工具類ViewTransHelper,可以方便的放在自己的圖表中來(lái)處理手勢(shì)诡壁。用內(nèi)置封裝好的InsetTransView或者OutsetTransHelper更加簡(jiǎn)單,解耦涡贱,不像第三方庫(kù)里面圖表的手勢(shì)部分與其它部分耦合巧勤。ViewTransHelper適合自定義圖表,或者查看圖片票顾,支持tap础浮、double tap、多點(diǎn)scale奠骄、drag豆同、fling。后面有效果圖含鳞。

? ? ? ?項(xiàng)目地址:https://github.com/fornana/ViewTransHelper

? ? ? ?開發(fā)中碰到圖表的情況還是很多的影锈,但是一般比較尷尬的是,項(xiàng)目里面只需要折線圖或者柱狀圖蝉绷,需求簡(jiǎn)單鸭廷。如果是餅圖還好,相對(duì)簡(jiǎn)單一些熔吗。折線圖和柱狀圖就不好辦了辆床。用第三方庫(kù)例如MPAndroidChart,稍微大了點(diǎn)桅狠。而且如果細(xì)節(jié)部分比較特別佛吓,需要根據(jù)MPAndroidChart來(lái)定制,也不是那么方便的垂攘。也就是說(shuō),ui設(shè)計(jì)的圖表類型單一淤刃,但是效果上又稍有特色的時(shí)候晒他。直接使用MPAndroidChart,第一是大了點(diǎn)逸贾,第二是細(xì)節(jié)部分不好處理陨仅。

? ? ? ?這個(gè)時(shí)候選擇自定義怎么樣津滞?常見的做法是直接套一層HorizontalScrollView,然后一次性把數(shù)據(jù)全部繪制灼伤。兩個(gè)問(wèn)題:一次性繪制所有數(shù)據(jù)触徐,但是只顯示部分,效率低狐赡;手勢(shì)單一撞鹉,不能縮放。所以颖侄,要想自定義稍微好點(diǎn)鸟雏,就必須自己處理手勢(shì),簡(jiǎn)單點(diǎn)就只處理左右滑動(dòng)览祖。難點(diǎn)就只能使用第三方庫(kù)了孝鹊。第三方庫(kù)里面的手勢(shì)部分往往與其具體圖表關(guān)聯(lián)太深,不好提取出來(lái)放在自己的圖表里使用展蒂。所以又活,寫了ViewTransHelper,還有兩個(gè)對(duì)ViewTransHelper的封裝锰悼,InsetTransView和OutsetTransHelper柳骄。它們都與圖表沒(méi)有關(guān)聯(lián),只處理手勢(shì)部分松捉。

? ? ? ?繼續(xù)說(shuō)自定義圖表夹界,首先要面對(duì)手勢(shì)判斷,然后是圖表繪制隘世,而繪制是需要坐標(biāo)的可柿,特別是計(jì)算滑動(dòng)縮放以后應(yīng)該繪制哪部分?jǐn)?shù)據(jù),剩下的怎么封裝數(shù)據(jù)之類的比較好處理丙者。而且如果只是應(yīng)付一般的小項(xiàng)目里面的圖表复斥,在不用考慮手勢(shì)的情況下處理起來(lái)就更簡(jiǎn)單了。歸納一下械媒,自定義圖表主要難點(diǎn):手勢(shì)判斷目锭、坐標(biāo)計(jì)算。以下對(duì)其進(jìn)行分析纷捞,并給出解決方法痢虹。

一、手勢(shì)判斷

? ? ? 像圖表里面碰到的這種問(wèn)題主儡,我們?cè)趫D片查看的需求里面也會(huì)碰到奖唯。查看圖片需要能夠double tap放大,縮放糜值,拖拽丰捷。和這里是一致的坯墨。這個(gè)已經(jīng)有很好的方法了,例如PhotoView病往,SubsamplingScaleImageView捣染。當(dāng)然,將要提到的ViewTranHelper也可以用來(lái)做查看圖片的功能停巷。

? ? ? 那么耍攘,PhotoView是怎么處理這些手勢(shì)的呢?縮放使用ScaleGestureDetector叠穆,滑動(dòng)少漆、double tap放大使用GestureDetector。github上面開源的圖表部分也是這么做的硼被。最初是想將PhotoView中手勢(shì)部分直接提取出來(lái)放在圖表中使用的示损。但是ScaleGestureDetector、GestureDetector都是通過(guò)設(shè)置callback的方式做的嚷硫,而onTouchEvent是順序型處理检访,這樣ScaleGestureDetector與GestureDetector夾雜在一起很別扭,細(xì)節(jié)部分不好控制仔掸。所以沒(méi)有采用脆贵。

? ? ? ?實(shí)際上,這個(gè)地方不好做的就是tap起暮、double tap卖氨、scale。tap负懦、double tap就交給GestureDetector筒捺,它也只處理這部分。剩下的就是scale比較難纸厉,drag系吭、fling自行處理。至于scale颗品,既然ScaleGestureDetector里面有肯尺,那就提取出來(lái),而不是直接使用躯枢,就避免了callback则吟。ViewTranHelper的scale部分是從ScaleGestureDetector(android4.4)里面提取出來(lái)的。

? ? ? ?之所以從ScaleGestureDetector里面提取锄蹂,是因?yàn)樗С秩我舛帱c(diǎn)的控制逾滥,考慮了很多細(xì)節(jié),官方的更值得信賴。其它的寨昙,例如MPAndroidChart的scale是作者自己寫的,對(duì)于多點(diǎn)的處理不是很完美掀亩。測(cè)試一下多點(diǎn)舔哪,實(shí)際上一次性只有兩個(gè)手指的移動(dòng)對(duì)于縮放是有效的,而且多手指按下再放開以后縮放不能進(jìn)行下去槽棍。ScaleGestureDetector在任意多點(diǎn)的縮放捉蚤,多點(diǎn)按下松開,各種情況下都很好炼七。

? ? ? ?ViewTransHelper是獨(dú)立出來(lái)處理view手勢(shì)的工具類缆巧,在它的基礎(chǔ)上可以很簡(jiǎn)單的就自定義圖表以及圖片查看功能,集合了GestureDetector與ScaleGestureDetector的手勢(shì)部分豌拙。ViewTransHelper的輸入是event陕悬,輸出是dx、dy按傅,就是滑動(dòng)偏移的大小捉超,以及sx、sy唯绍,就是縮放的大小拼岳。

? ? ? ViewTranHelper的使用:

? ? ? ?transHelper = new ViewTransHelper(view,callback);

? ? ? ?transHelper.onTouchEvent(e);

? ? ? ?這里的callback接口方法如下:

? ? ? canDragHorizontal()况芒、canDragVertical()惜纸、canScaleHorizontal()、canScaleVertical()指明是否能夠滑動(dòng)或者縮放绝骚。

? ? ? getScaleLevel()耐版,返回值例如1.2f,這意味著double tap以后皮壁,放大1.2f椭更。

? ? ? onScale(float sx,float sy,float px,float py),手指縮放時(shí)的縮放大小以及縮放中心蛾魄。

? ? ? onDrag(int dx,int dy)虑瀑,手指滑動(dòng)時(shí),滑動(dòng)的距離滴须。

? ? ? onFling(int dx,int dy)舌狗,手指滑動(dòng)然后松開,此時(shí)如果速度達(dá)到要求就移動(dòng)直到速度減為0扔水,或者達(dá)到了邊界點(diǎn)痛侍。

? ? ? 就像ViewDragHelper一樣,很簡(jiǎn)單的。但是看來(lái)還是覺(jué)得稍微麻煩還要設(shè)置callback主届。下面提供兩個(gè)類對(duì)ViewDragHelper進(jìn)行封裝赵哲,然后,就可以不用設(shè)置callback就能處理手勢(shì)了君丁。

? ? ? ?在ViewTransHelper的callback中枫夺,可以想象我們根據(jù)輸出的dx、dy绘闷、sx橡庞、sy去控制canvas上的圖形的繪制。ViewTransHelper只是識(shí)別出手勢(shì)印蔗,怎么移動(dòng)扒最,怎么縮放圖形就不由它控制了。例如华嘹,我去控制某個(gè)方形移動(dòng)吧趣,一般會(huì)給它的移動(dòng)加上一個(gè)邊界。怎么處理呢除呵?InsetTransHelper和OutsetTransHelper就是做這個(gè)工作的再菊,它們對(duì)ViewTransHelper進(jìn)行封裝,使用時(shí)設(shè)置需要變換的圖形颜曾,輸入是從ViewTransHelper傳過(guò)來(lái)的dx纠拔、dy、sx泛豪、sy稠诲,輸出則是變換后的圖形以及一個(gè)matrix。

? ? ? ?這樣如果你要控制canvas中的某個(gè)圖形诡曙,不再需要callback臀叙。只要設(shè)置顯示的viewport,圖形的大小价卤,圖形最大最小寬高劝萤。然后在onDraw()中,獲取當(dāng)前變換后的圖形繪制即可慎璧。詳細(xì)的使用見示例InsetTransView床嫌、OutsetTransView。

InsetTransHelper效果如下:


InsetTransHelper的使用:

? ? ? ?transHelper = new InsetTransHelper(view);

? ? ? ?transHelper.setup(viewport,shapeWidth,shapeHeight,minWidth,minHeight);

? ? ? ?transHelper.onTouchEvent(e);

? ? ? 這里viewport是顯示的窗口為rect厌处,指在view的哪里顯示;shapeWidth岁疼、shapeHeight是希望控制的圖形shape的寬高阔涉,它們起點(diǎn)是viewport的起點(diǎn);minWidth、minHeight是最小寬高瑰排,因?yàn)槟軌蚩s放所以不能為0贯要。所有的點(diǎn)坐標(biāo)都是相對(duì)于canvas而言的。如果需要設(shè)置初始的shape凶伙,調(diào)用transHelper.setCurrentShape(rect)即可郭毕,rect也是相對(duì)于canvas坐標(biāo)系而言的。

? ? ? ?InsetTransHelper保證不管怎么縮放都使得shape在viewport內(nèi)部函荣。

OutsetTransHelper的效果如下:


? ? ? ?OutsetTransHelper的使用類似,它保證任意時(shí)刻shape都是包裹viewport扳肛,任何變換下都如此傻挂。

? ? ? ?在圖表中,我們需要顯示的數(shù)據(jù)如果全部繪制出來(lái)就是一個(gè)長(zhǎng)方形挖息,viewport是顯示的方框金拒,手勢(shì)滑動(dòng),就會(huì)顯示相應(yīng)的部分套腹。任意時(shí)刻這個(gè)長(zhǎng)方形都將viewport包裹在其中绪抛。OutsetTransHelper包裹viewport與圖表中的情形是不是一模一樣匕得。使用OutsetTransHelper以后完全不需要自己處理手勢(shì)紫新,滑動(dòng)縮放,邊界判斷之類的問(wèn)題奏甫。而且它返回一個(gè)matrix尖飞,根據(jù)這個(gè)matrix可以計(jì)算出當(dāng)前顯示圖表哪部分症副。

二、坐標(biāo)計(jì)算

? ? ? 前面提到一次性繪制全部的數(shù)據(jù)政基,但是贞铣,顯示的只有那么幾個(gè)。效率上存在問(wèn)題沮明,而且本身自定義圖表在繪制的時(shí)候坐標(biāo)計(jì)算就不好弄辕坝,現(xiàn)在又要做到顯示多少繪制多少,就更麻煩了荐健。MPAndroidChart是怎么做的呢酱畅?使用matrix。

? ? ? ?計(jì)算機(jī)圖形學(xué)或者3d數(shù)學(xué)里面會(huì)講到怎樣對(duì)局部坐標(biāo)系中的物體進(jìn)行各種變換摧扇,然后將局部坐標(biāo)系的物體變換到全局坐標(biāo)系圣贸,再將其變換到viewport窗口部分,然后剪切顯示扛稽。它就是通過(guò)matrix來(lái)做的(不考慮旋轉(zhuǎn))吁峻。這里所面對(duì)的問(wèn)題是一樣的,只是換成了2d。所以用含,使用matrix是可以計(jì)算出手指各種縮放矮慕、移動(dòng)以后,圖表的哪些數(shù)據(jù)是可見的啄骇。

? ? ? ?matrix來(lái)自于哪里呢痴鳄?就是上面提到的InsetTransHelper、OutsetTransHelper中維護(hù)的matrix缸夹。

? ? ? 所以痪寻,使用ViewTranHelper就可以解決圖表中的兩個(gè)難點(diǎn)。剩下的例如各種樣式虽惭,各種效果橡类,就比較方便了。后續(xù)將會(huì)分析如何繪制曲線圖芽唇,主要是坐標(biāo)計(jì)算以及OutsetTransHelper的使用顾画,并使用OutsetTransHelper實(shí)現(xiàn)簡(jiǎn)單的折線圖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匆笤,一起剝皮案震驚了整個(gè)濱河市研侣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炮捧,老刑警劉巖庶诡,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異寓盗,居然都是意外死亡灌砖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門傀蚌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)基显,“玉大人,你說(shuō)我怎么就攤上這事善炫×糜模” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵箩艺,是天一觀的道長(zhǎng)窜醉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)艺谆,這世上最難降的妖魔是什么榨惰? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮静汤,結(jié)果婚禮上琅催,老公的妹妹穿的比我還像新娘居凶。我一直安慰自己,他們只是感情好藤抡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布侠碧。 她就那樣靜靜地躺著,像睡著了一般缠黍。 火紅的嫁衣襯著肌膚如雪弄兜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天瓷式,我揣著相機(jī)與錄音替饿,去河邊找鬼。 笑死贸典,一個(gè)胖子當(dāng)著我的面吹牛盛垦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓤漏,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼颊埃!你這毒婦竟也來(lái)了蔬充?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤班利,失蹤者是張志新(化名)和其女友劉穎饥漫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罗标,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡庸队,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闯割。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彻消。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宙拉,靈堂內(nèi)的尸體忽然破棺而出宾尚,到底是詐尸還是另有隱情,我是刑警寧澤谢澈,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布煌贴,位于F島的核電站,受9級(jí)特大地震影響锥忿,放射性物質(zhì)發(fā)生泄漏牛郑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一敬鬓、第九天 我趴在偏房一處隱蔽的房頂上張望淹朋。 院中可真熱鬧笙各,春花似錦、人聲如沸瑞你。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)者甲。三九已至春感,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虏缸,已是汗流浹背鲫懒。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刽辙,地道東北人窥岩。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宰缤,于是被迫代替她去往敵國(guó)和親颂翼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容