From 40f29055071637a4232bea6237ba6e2cde9d01e9 Mon Sep 17 00:00:00 2001 From: Seungkeun Lee Date: Thu, 23 May 2019 09:21:38 +0900 Subject: [PATCH] [Tizen] Enhancement CollectionView implementation on Tizen (#6181) * Implement ItemsView.ItemSizingStrategy property * CollectionView - Optimization Item size calculation with cache * Implement ItemsView.ItemSizingStrategy property on GridItemsLayout - refactoring CollectionView * CollectionView - Support DataTemplateSelector * Fix FrameRenderer - Inherit from LayoutRenderer because Frame is subclass of Layout * Fix minor layout issue on CollectionView * Allow CollectionView items to resize with their content * Fix miss typo and remove comment --- .../ControlGallery.Tizen.cs | 1 + .../res/FlowerBuds.jpg | Bin 0 -> 5008 bytes Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg | Bin 0 -> 10315 bytes Xamarin.Forms.ControlGallery.Tizen/res/Legumes.jpg | Bin 0 -> 6210 bytes .../res/Vegetables.jpg | Bin 0 -> 5349 bytes .../Native/CollectionView/CollectionView.cs | 145 +++++++++- .../Native/CollectionView/EmptyItemAdaptor.cs | 2 +- .../Native/CollectionView/GridLayoutManager.cs | 319 +++++++++++++++++++-- .../CollectionView/ICollectionViewLayoutManager.cs | 4 + .../Native/CollectionView/ItemAdaptor.cs | 11 + .../Native/CollectionView/ItemTemplateAdaptor.cs | 129 +++++++-- .../Native/CollectionView/LinearLayoutManager.cs | 245 ++++++++++++++-- .../Native/CollectionView/RecyclerPool.cs | 10 +- .../Native/CollectionView/ViewHolder.cs | 1 + .../Renderers/FrameRenderer.cs | 10 +- .../Renderers/ItemsViewRenderer.cs | 18 +- 16 files changed, 790 insertions(+), 105 deletions(-) create mode 100644 Xamarin.Forms.ControlGallery.Tizen/res/FlowerBuds.jpg create mode 100644 Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg create mode 100644 Xamarin.Forms.ControlGallery.Tizen/res/Legumes.jpg create mode 100644 Xamarin.Forms.ControlGallery.Tizen/res/Vegetables.jpg diff --git a/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs b/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs index 4a28a1b..53b658f 100644 --- a/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs +++ b/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs @@ -21,6 +21,7 @@ namespace Xamarin.Forms.ControlGallery.Tizen { var app = new MainApplication(); FormsMaps.Init("HERE", "write-your-API-key-here"); + global::Xamarin.Forms.Platform.Tizen.Forms.SetFlags("CollectionView_Experimental", "Shell_Experimental"); global::Xamarin.Forms.Platform.Tizen.Forms.Init(app); FormsMaterial.Init(); app.Run(args); diff --git a/Xamarin.Forms.ControlGallery.Tizen/res/FlowerBuds.jpg b/Xamarin.Forms.ControlGallery.Tizen/res/FlowerBuds.jpg new file mode 100644 index 0000000000000000000000000000000000000000..023797cc106124e0aadbfb14a87babcb501819b2 GIT binary patch literal 5008 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 y*YTehv9G&aIS=b_CtN#tx%4kdCQAVT literal 0 HcmV?d00001 diff --git a/Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg b/Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6c4ea3d9ff97e7e4e4ebb34f7b64a7114054ed86 GIT binary patch literal 10315 zcmdUUXIN8BxAr6i2)zl?K?%J#sZvC`ih_a^>4He_Ef7&bL8S^P(i9LB0qIhuR}}%J zqY%VGj|fHsg^ z3LwrhvFV1mxd3qdI1mE>AOQ`G9>5{Q57lHC!!9O=a0QHU7b7A348{paeK4rDz_@(TD{q=TDRfYb@y)BHj5Qi@WtGC)p7MoCpxMpaIhUq)6{ zK~_~x9su6U8Na-TJ73QFjXka({M*ax!h8560EuANON5M^oNOZ5uc+VwiHP6$dJ^m} z9T=PpB*A~%%uGfA>c4!1=O>f@?thX@xo01m(`4ixh7^;j_An!asekKNC8J69>52e= zH|GBGM^;8A7q+Jl@sbPwHM2;qT(V!72IT#v4<>)w z2mZs){9k^O#!~;Sv%mwWpe~a6UjoICz(Ngus8atEBmdL~>5{VUmmSg^k_d-p1!zK* znv@CrP$KX{je`L2Q~p-ve+u7q!2`-Nt*2za`?dEl1vKKw9wwRB`9tUEA6WMftoH}j z-@~M_22YW{?Hm4~WAq0${sSL-O1`U)hI~8zNB@aGFf5T0+=gslg>>K$?tw5nlqF<) zcm~1<2w#Hwi=bf0Ashx_5hy<>AiM?P2N0&*)6a(H5xIw5NZjNic71@d;Swwtwu=Rz zZulN%fiSpF3G$(n6g2NJ2#-O*4v{#340k{SAJalO54y>o!2ZA>|L=aY00%E;2S4Xu zdi)R|nSn9*3_~|=0@F~#12RUcP!}X|P;J~j=jJ*PVPFh^V|_~-fIaZa0v7>ytu0rdIbix%(zAOb|hLGb~E4=j!lNAThD;ev2tT=XydKE82Re&PT8 z%tfO`S{MIAsfB%n93|g%mmYvh0E~6hD548fF7bRyH(F9KFZoqdw{eI|odG zDzKR|u0M@EianZidLhx#@c`@rR+v;e^H+rh7-y7cl(9!)0DE-zp+)*+!zdkConhvw zFHu@p_Ox@14oUwIF3E*|kzJjssYW|SBn;VsoZBtR$lY@B9}N6US1M2e7;qf?IzyAu z(FTxnrBF?Us=|M56NyAasJcMa8mbk0*5*lPBx$NhV!M0%b%62_60Y4N>H1&wFHJ2# zDqyT$UYXU)BtXCR#aR^@w1N1wJpke|(BlVQU#6khWgzAu;GE$ns3m3G6AT8&NjVJICM}R^#L#nJsUO9^RqAnz^A^oRX7~J zy0&*(h3m)b!=dBmqfLD{JpPfmUYwsE~1xTVGi7$RDz|QTL82b{A1tl&1|QynLydFW{edU_o_ER zU0Guk7{FSu3D=h=nawbs-_)+hT5z_DYd>+QwGKGpkhJz_ZQ9N`CTXTqYmeZs;|Bt*r&us6Z7cuZ&}eJ)l{VA06^tHI zBTo2fDJ3a(p;}&LAEeDV0LqY7z-!SAwcUFGbtmUO`Owb_c+kr^iMuBrD{c0(i5P?{i0C|}ETuKqB>v*_B?pCE zMlB`!CZF2iR<8?X6X>A>sh-?V8A3Xqb(xzX3DUgjRLn}%Xbume1KZ@|G&wcqCMntx zeWf-^_kQ;mfLzzlAUKKLTCk%O^0C1sy%_RQC&&~vbi3R+u~vtCI%4NYu>L)XCYEre zow;vfQkbI-m-_d!5|VK!Y8g=)mw7!ZDw22yktD~U26&qK39K63P5zAqcj z(ImxUDkd%P$X$Fw>>a43riYI|Dnv;8D##I%CH5ye-wgbQ8Hc z9iT9^@d`CH`i%YP9J8`;=OkN4WI5_m?LC>oJAsVP8#RS=hLaD5)EEn8>!!;_h*WX- zXA3g&@HB~2Puw)1?L~WIxK`+rm;D z)W7hUOaFSAOltWGef5VynS7xRQIF3&f`j72G|5ixthNdxvYuzT;fh*@)YTYQkW8#* ztxqP(am#rg{MeQvLncVcRvr_;wp!E3P0f)i6oq2rtF0Un$I@*v7GtQyLQbbrZ|#ee zWPo@j<(~a)>QPeeWhlOK{xkOsf~=g!Zr7o1L%fbeY{Jp{DIO$VH|Dl33Sy2Oh=O<} zrr}U9nFGeIQ0_fRj*}g_D-7}~$=LOWbzr6!o+_Yl%BV7b9ZJz)UulR}#0}h>)BwaQ z;;0^Poo4_0qYrIFqtH^i&gBy4&K;x$tp*4VYj>&`2Q%qfUh>>jUy5U({5ulM$N9ZMEd zCh>YAUFGnfyuu+~wf^t(I)^y7|BHPm81Ii0r@Zt)=osz`UqAtjzk7s9suzeja3YF$-@>ETKF3dV3Cu;_k+>)d#buhm;?7<6b*;7VRV&A#~IN8XE z(v`fOWVi`-YK!QG!`oE%%u8g)R7ZN>EC~ zs2tgppf!}fM@RL|k!?llp#Z*0-2HYX8tmP+QXb$4K)bmfs69GzJk>0uV=_yT7HUurkNJHd^c7I z_ZZG2|KR0;AI#l?O=0iYjAO)@jbBp1h05ufY=o7?sc5-mCVNL!EDTi;szrGD?NjLz zh8&MrhJq3~*03WyvCi)D{&$;%Z@$-+(FpBguVUfdmzsW4smbKAz|UTDgpde)pQ+-ZLz^`)GBP;iEI9%JU5y}ZMj)8TnGeWnQ?Q(JpgiciPcACy zK2qRFc`K{w;2KKa(eE-9HQRo64o*QKVG&U=1;s;3$||ZlM|Jh|4GfLUPMR;D_5gqu3e9fOHR3+nwEa&ZpMSW{DOytMUNg=R902j)IO`L zZ)@-9?CS36?Hd|?^LAu(Y_$#d6k^Psr zn4q}e5C;fKQd}^2Fez{*1o;733TEw7ln%Zu2j!xW`;H{tFK?w1kT+dpb@UsgW4qpG4N9TnDC^@Tjj)HmmdrmAdY!rTKG@y{mpCpU zOs4pwS?U(I@yExdgq+g(&>DF%ULy>HOUOtuHp|AC_S6@vl>Q*f+icUZiF2B{{5eL) zm0EcvakhDMPdL#dxV~rB_oAI+<9&i~X|2k0&so0-62Vo+*H!%`M~<7Agn~17A11RW z+>x*C*O|OTg>28I8)*{QG}z8;)Fqg29j#rcWk)q*8fr+MJ4mqi8{vq4_W5R@z;oAPT^VVk@B_9|lq+3r# zWCc^i#(g?`;#k?&lq*q*?p-fJ($2a?*cMQAEt*7YN=lEes>&V9INST=j97VwiFKj3 z#=YVknvmeuTGjda(wqWIlkg&wgYD!cjmJ^RyYbUVGz%EC)6?BkUjU3P;o+umR@HwXuEz(l5lwEwV(T=nJiRi zz{9~6$=CTKNzFQjUh>svAN9^9)%nS}uMEV!FCSw>xD|Tq^6U1kk}UeS$aK(d{4EIokcR8^g?5M`IHW2JbGrW=~43Jt-O@ zC)e9@MVxQo{!}X7A1RIYT9aO~UJD4Zn;pq=&%ZE|E-#UG(qM?P4yH7JCr#>fv2&)q z)6=qB54KCHpEGUQRgWI<=QdBFWO9O^USSVL1hS6a#tF${x!uFxG>i*>mt+u@ooQ5S zBN&ON6}nHOS0ZC__URwc{Blqo-4HC=r98*&6faVi890C!`5x1#SZw0tJNq(pzSe_{ z33Wf}1LI_uHo__Xw#Da*qQ!aZRA!lW;`-gqa&Nw=`3I6`Pb#~l8DFh&zZn|tf4%)m zavXga+()imsBsnA<8UazXZUhITzPAC)?QRxTrM+5>E`*CZkvQVITt@|>0{>`hYnh% zak)A7W*B~G>&Pb?<>B@V`Wp1r*T%LW;-U4IL|eC75uth^_T@amG}@z9Ha#IICPwZ6 z^yW)+7w^r2VvU6YBcDmL@c<&=Z+8S>mg4VvB}@lG4@1H;PUj zWAkb1h@bqv^*A@>vTlUhn&zesj&fOh?qf))l!v;kllpV7HwF^#6)JWX`2Fk65rMP% zC_fm1zJFH#>T?%w%IyT5A&+m@!He}ox)%g}X^CqeBe%+1J|7nUAZxiI)}&c#l=WVZ z2nK#GL>xY5ZkJ-D;>F}Y@Yu?Fbf7=n`}LMF4zt9CbJ82$nUs!F*GfY4!g+>hF;D4N z?k?LFtm$mlyp04qFT>KWt(p>E)#UUUyT#s`q0p|QuH~n1(0^3cxTA#jC?5*2E-`XL z-@$6FxzeES@`Zd-GvgU}s3nlG^#)&w%O0Ie=rkaLYQ&+5i1zKfQXQe)FZ-D(&eE$z zkOzFY`%a0w@JNYb!IRPO<+8wlApNAiEDyc7pWBmLwaN*x^X|8G4?XL>Pv^^U;6R#jT{)-U`?!u;_|nAULAbHGm;`?eD*1>2iKKS5*qhK@@AUF zgv4hpSvk!!6M7@Pv}De3n5I6OYWva-PvjdSfYvFK89uI%+LKwQ%uet3ZAFBgd-ZyN zE9Q*!O(N)*8=uL9i!a~;vgvR7p5fXwU33kc7aYv|R_P%nWZEWpv3&ZR<)&|h*?Rf9 zMTg1t;MyJ8#^iUF9RiL*2VB@spILlgz`7jPy~R@8$thH*SW*^S6|bEd>6kACtW)E{^TpR+1#g=_H|rgq^@_z7S3j+*NgJ8!SiW&27WH{b=|!+u%@AJA z=P(22z6Nz<3lSiR;DQO$=TR#}SZxeiI)7TLKKk(jqk1A+f8DecD&@sgF1aQ9_4p%E zDT_btmcnNe;w%{Pm?I-wYLs#JtB-mrmbo}3&mPh{NhOuZMm}zIi6${XYj?6w~ z^;_yZ-QL+J>6H=r-FOjmj^TCARi$^Fc-Y$T@j=P9g3>JBE_;ek{%OfB-(65S{&t6U z#)*LM$uh6?51a9cUXBY+SwWdGYL=Efl~?PdE~cUvFtYUTF5r$4m%K zn#9iV)z|8sVP=+nku1-O`Zj)Zwl>m#XN<3!?t6TrfyeU0tg;WcC;PC6VkIec%Gr}q z0%WKZ&P%zU{P_-YGrIPEPSIOY{%7(pc^@K#b)Td z)JuQGj>6vR)EngX{-2IfslCrd8d8G+f6S)1ebU#Xt-pNT5ckK9+$S=jaJ1&o6>j(+}2 zqMrC|{1t3c$+@mNF3&4cCe31eRFQ8cMwf7b@}Er{#N2(k#PP1t&g%{+Mlz;4WJJ7- z&oTEztu2}PNBuAmC{%vX?DtSV>gp7? zr>iFs%w%uVcAatSdYt?{852sNguZ}Swr?sj#6WAuEIwf_Z!=+E$aSU7(=1&LRrlEZ zw1v4c4E-A`nUBc9ZZ=2G;(uUixOjx#y)uQb(uvabax+tC-r#nvnHQK`zqW(?L!69G#egtKI{fudcnA$Lx)IE{n&T4VHgmTGtPYcWbhg%(0p5J87xw?lQVE zMqPAO>2{nl^bWDD$&d-H9`7d#+b~qo(cRap4|_a(n$By(>2h!LrYAbpRq8P}{MO|# z#_&;V|F!iFBH-EJaT(PU8Ze9*B^=(cPW7&|5_=%>)VWtD512%%s5#E|VHJXUe*0RiF{DdCo4w7^9Ij@Z`40 z?4t?ArhD-v6PwoKpNGP}ln{7j%OXcI_Ae{b6<*V5Z_7% z>S5@q^SnP*D&O6QVYENQnc8nzx^1MV;av7s{!P?tKD5P@52jU4U3{&7vn8dC3VYlN z!#QcG@z~?j+xwT1C8*F-Y-g@holkvx@(EH$CZdC!EI4839cJwWh6(Sta?Vb7w6o;h z>*o%2$RwMY&33Pw+rh4{E$&{AYyZeryx&M}$9d?vtM^FHrreYl{aIThmd%P)F579V zhSi0Ec$X1O`O1oOd|=u~i-VT_eeo^MPANGfb#}2t!0xp6Dh;~$EYTz8!1t{3kvh$r z8cHM0toRgyfaS1j?OSAvSznFjb)~{*tSz6}zT3*+V9js%d?`QJ_8caUjE?GMF=n!x zeLuPbn{%w}IfA$0tqcp*FMIzpiy`_|UeT*ji;BvZDpF>`7s7U>5yl$ zQjaWqoomZ=%$Hi4nPXjUXPXo6oLp0vusvgRIwsY(5zZtZPpsslG$5EFkqS4d2cJ&)mp zYR*TKHwiiFI&1Jx?b!<182V}B{B!-j+Zhv8?ABR%awl3FJeicq*x#uj9Gj`ObD-6x z@j17GF+l}=yub?It(B@Bk>Oi?r;DB=*`RCclS*H<_7^)Bw7_X?v?T=c_RE~7&tPWqbNH+~Y?bVuHXNsw)rzqXxtm{0 zb%3`uS9@}0RFuFN*}{w3ZrE5{c0W~DH>@t}=3l=sdr(?&3*WB;)V;gSRcPu$R60Xx zs5a{3O7!NrriE6a6@}oz#>J5Cz-pBioqT-aebU#U+O_br+og@(5AorK6-r?%B_TBv zrgdC-$$BlOpH|9*eboWgVQ~{XBDid@6@*vBmIxmoK{Etr>6+VBjrY_BZT2`*HCrSc zpcFf?awHS`{6c{Dl})?y=A6(xv?@M)wn;uB+2c(h&nYV+Fs+{yrY9JlH>(tT0+YZ) z-NU zd(q9WbX$!G&JCo#z`em{YcLs!hT57uoPan1xNBQ1Kide{qZ&xOHMSVThi^ZYb|G-l zqvZZmCgk(5Q_l>pW$gDiFxHr-J~Ur%yowi8y0yif=5G^nul2RRLNY%-oF*i zDK~#B1#kU4JT${O^p@2fO7p_Tw}eOyyQJA2rtLeCybo`Bt_G;_Re$-upN+ZFI{%h{ zwtRbI+PQIWX(_92^vxxF~sU@PE5-@)5 z-x+4fi6ADDVoQ23V3qbltaE9OL!}Ng;^?h8$!DeXXZw>wf83BotQm>N-w41xmKhYO zY4Y~&v9W0?5L5V68CLoe)1q=b==5l$xaHQN%20{G2&Hy2{Iw3%Ho5^{P8kN)=1mug zug_W&XBjhTqil^PSnKiVFOJd`Un12iLacj|ng^kmc>PdjzB?}*BO30;tB;Vu3=sRPHy{_~_psiXHFT}8fM>Bbcp%0X_2ZnFTdi%0f z${W%cd%QAyXBi^WU1CjRjmqh!bTqAxzzUO0W!E20Qm zVn@W&Yp_C9(A$Z(wS_M}=UvHTH*6EVke%Uver#P-L;B+Iaa4*;pJ3|v hlvXjVRh^$6%Z8=La0(By+a_|eY4z2~|2%stQZ%=0;C&Y3&+diHu1K&_>wsRjT7fdGx03vj&# zuv7K1w*dgOwE_G90DuHQ2xI~f-SmJrrT}FAcV7+23n2K{`&&1IVt|`FfcD08ZtSma zvi&ROU(Xj7_D&wcI;yWZyu^higvCSw*9!m@z%3x~Z~HG15E1+pF(DxV5r`NB`fDVl zoa8z|A;- zkcNo%zL*j*ot_1V)Ai27poDCa2g;S5^e@NJU~x;gU{bQX_ZS$NxVU+E`S>46NJ>e| z$f`V5Ra4i{)Oz(=-@x#Vk+IcVYa3fTdk1$9PcLsDU%!yhu<(e;s1J#sl9E$WKYvNf z$<50zDEx*js;aK3t*dWnZ0hRn`QF>t|6^d{=j7D%%1anj5ZLH)}u$`pX5p<$YrWG=xO=#fWK@^gtG_bes=^NbV>n zWLI{QJ`jJ2rnht(C%X%Nw8n+`i}o+F{~cKH|BLM3VE@fE51=3b-Ygyg4L}iabUPxD z43~^y?nFB*>(ne0jpf1m+kU|AS8Adz@?8l1L#)7=GA7aMZ5}vR2J}{=rE?D4cqsFo zbG%wFBj`w8;QZ-jMj)FV8qV>xyUGZnkTUOXEMM0_-8)!QChNg-Qoh}da_+WQVDmX-Y?1rshmE|PurKzw ze5!o59a}m6C_{ndu$CF(%r9zfm~3bRlKHWE+wad#zK+8sSwv6qot2EQ znwQhMW9U(wP+YeV&)VsMFAR~lv!80pH(}nM!oW#%D2)G+>uvg3uo4N57f;j>gl!;L zjiGj?z`9S+iCS$Dl^H@9RnFdqPxbyf6)(A?177gL0t=@7px z^G-duoA^{n*xG_l^ZqS9rn|5Hl+Ez>IGJ2Mh|0p!-eqT{>ta$E9M&{(Y!B*zB)rK^ zwAPK6=GXra)S5(1>?N`nx6gM0@)5wwp=%7Vk}hVur{fP+Ru;Pod>4;=WxD>n&kc&* z2BS_K^mqNW9#%Qm*6jK<4DCwG+8cyz&TpgCyO$*4OY`C%Kj*~U8xnfIKOR(pDkN|{ zUNO_U8fX)3MA!FFN*=I$F0~;_vZc};0I@a4{Ze&6bn1JQQ^2_|8$7{dLPD#RlgG`F zOSSnWwSH%?y-IxU;h}0&Kg%ZpDH%pg7C9a^(Zc91nSH10NF!1dqHbl^j-Yk0n*o=kloJt zATTq4Im=`wCwr9dk&j0?4h2TEHs z6UeMLAn__(RmoPo&kV_W)GHGG%hZR6bu>mq{XgtpG2!2&O`AP5DttC8EJo1T>Xjb; z97PcKJK<;}`f?pkR$D2S`j|Bg5?aAwiqpjktf!wvXu9%e~%w>KBg-8SgW1;Xehrq(=9Q379SrCa0!v^*s7^>L{KW1@V^`47F~gb-jbG z5a>kVPs&(M30~3ZNDpYY($-=XK5_JphXxkPxJ%nDOf`E4=&D}kv=NxtwXrr~3}S3O z*D0AsRvtdGNV`x)wB&_Jr*L)?ih@D!%;N=8arzifrxQwU^862WCzsP+^>zaV^WqNQ zXWUR8Beaq+`OE+C0fA49caBbVA`m zxq*FYRN3q7lsipu^&XA#ykfOx$Bm?lc-QWtFt7PQlWmS*RYqca*^~RJlZIctbZ20f z3;3pR=df_5^kOAra)`%{f50^Wxo=jb+sm<1lNH@!Tdb9mXjU3u9}a1kgQ-%@VbcNX zX_{bLcVZ`UYK011^)-MJYZ334&~yB)_#eEK|D8qnidY3uZ0dR7DIm>4DOvH zy4jz>C2+5hSBqKqYT2n_ZcEy}WzD8$I&<|$RMebBqWxrHuL%#7mpIvzQ=wAb@ zHBM$d2KOADAf9&nT)NBry4&E}bV1#Zb@|^?Q5}OoF6>Y7K+H_IqHS@2W9{0v9qh6& zZExZ{BUj>?R$o#y>rpOJaKT(PZkxhe!l&`2Oy!SEYVPinyGd3aMOGYRl~&%@{`tGlIUb}Fu2OY90?L+Nc8OkkDGm4tA1DKF^QZCHsiv{v8PzN z4fz*cw(@B~p$qtTqXiy_(02MqzZsY9PGyDC&mCK~cuiMcKWh#lnWxx#q{NflkPI3$ z1Oqo?b1NqC_x*Ey>@y@p>zPqBqZIz|oI_+gSKB)Q+J}GgD@Frdhv!ue2cK+h6X4$-5NaF079$6c)NzxBJvtC*4!)o-_kw5Qm8{ckS7?}Hz;2R1beoqHZ)aBuB7cSLr{I1P=yj#2HUw_JawpiJqs#U<2*C;cQSe3knpV7iX zApyDuI5{K^(d(x-;R&UhxCUrz(i=H(su}f8x~*oufo~^_C@Gp{C_00hgEc%f5E)<|rNxz7Zw)?T+>+Y@6V?g4*x#MN02iue9cx@4T^@W#)NnY20h+_pf&U4)tE?(Amzmc4Y;%z3$Ikuer8W21H z4`63FxMH*W5PJ=*RfJ2ArsI zAf+pcRy!AP4WMRm4KYRBY*&t(T$X#|I4LE}y^wJUh#5uAC|7#e%guk%)f)#Z=SNDJ zHV(I4`%T3&RaJ=8WecOy=d~`11?KX-caNn;`PUt+U*?mT>a2K9jgH36IrliPwzDjn zG(YTS5m)Z>B0E%W;8|o_joUe4-^U;&L(r(_mD9GhaR%W7y@yI3eHQgTx$zoG(!`=* zFutWIc$X#&zJmUWb-|d$wn?@}tc`hQC=dup=E{Gseo_~2-kfR+G~6T%9s)}LAV|AZ zLQCxzX2UJe^7Fq;c_t)usqGDHP!|d-+o8R=TQS@er1K;jx8$GfnPp?t?6t`591I6Q zYwN8xd<^;1FY}Gso*4-&FMH!99(O;%#RN)Ntiyz)^dmfnBvZ!T>`7K~ZOy)YC7i?g zau{$qr!iF3u1rHfuVC+L1y<9#1Eg!H@UeG>ws8`Ps*=IDbdXx z%WOEwH0y$xTy4jaKJ3>p$;yMI`W0o8Ukp{N7pFXmJcv$HMaw}DOf@N| z7%_B$lec$$Kg9j6KrrzHY`=W2Tljm0bKAyEJ6+%h~2mD7FQ59+Us6BPFsmI*u{=^pT6k&Dc@Au8k8_a7@NKL zzJo>kRN9;|(BY^&beRN0m5hQCys4fP&YOX&BP33y&t@U34`t4DKq>TJT!^*87={Bf zF4S@U-KHFuDS@0zbBhTG>wiWImjX|DNBN@?>7qBg1)s8r4DA9pi}|M-1zv@nr9`#0 zW)0RK$0^5}>`a{6a+OZBWgN^LwkK@1Q%T_RFXVKd^>Gk|u%wuHFg(5nWcIc#<1~vG z@Go!>q4XGs)0BZ1s)FXAC%NITV#yMzq(=!jEZe2&*2816eeykiAIM8YmmU=@_O;1$ zo}u(dW%lYT`?2LlINJ+tc7Vwnoz#K>iH6&PBLkIvOdEB+qeB3|EzLzg6#bSZdg7I% z0FrI`9vzD+L-D-Ewx=1zEKUHMh@lF5prI4yiG@5fVoVZrMMAQTQKk5m;okc7WvYcC`ojI@DM(5)iFP_+&3WuM#zyunXYkow1BSXUr=bql6e-%tYCiP(INJiDPQW)rvdvbdjD@#*_lqhAWff1~J(WO&4~w+F2j z%XM9NkyjI&6CQ^b%YBoGZwwCCfYxkiuZT5l0mH*4bPWhgF8bxSm0|a2$w^L=c62yl zt3FX??5<%~PE+=4xAE%7=5N2L2^lHUwTK}z*i|Z~g%1LkzOAIDF$=9Pm@8X97#h)Z zWg{Vb;;Vq&7HYm=4*Xf?vS6>kHJvvJf3_xXur9~k$_wc^kBb1ukTcpOvyvKW%JwPjy5d38UrrwQe=AY%-nS(|ZZx;#NGXpc9c>U&1Y22O%#n`$ zvb=Aktaqw^vOEf9`>5MpdVs{;y(iRdvGX%Gs0c+=95VC8Iky=huveQh`UMw&B6%!< z>76Ehq3&o^SQ`40W`d<*OQShGZj zS;Uzd-Xp#COe!T(EI#4>MldXP>PleA2h{8RB8GE88zZz{)w;CQV~Dh3mc$EsZGK1r zi!SSD1@CvfKIa-vI;TSB8`)iPVD9FwbHy(&pSem*ZA9m*?B9NW&vm{ze5Fj=O;#`0 zSfb#<2nyTYIm=SUc=B6Z1LktJ1B+pvlY5G95n)%iaaphuI|c5V`b5`XSQ*Ma}HLX_Hxxcr=1z@CJqw6EB zocCL8pHB+YY1-A24vDUHA&vnl3*ZzM$In+{Q}BV zp>-emo&Q*YwwCFHU;6?Hg*7_{YMp1jO43TYoaQT=c|BU!c&0vA_Q_=7x`NwV(-+djtFx zl>5w}g?xDRH=kZ@E0kC0rzbI1ckVF{>4jZL3l$KOVO&MYqRgf$+L_Kl;E z%>H2`x-416aB{`gJ6j`-2J)HsQoRhN`{g6C9pyZiRS5H8?rXsF)OD%u=B#Stk>*-* z&gL8wVfUb)04sr659yLBlQrHJ1%b5ye?^J8QwVDf=BMY*h4ehcS7O{(Y+mGh9Hq}p zcU>i{u5QK>zyJ~N&wNEKLqWn*?A<3XYhDJyDqkj)D@YC(sURINJ1KQENLnSvq#yU? zsSaAB6eDw5y$P34Me^x7h?F&cS%#vPPmpqd-Nr}oYXFA4E06=Ha`9wNbv^6O?lO47 zR7R*|dVZ}W?{Zh`y#_L@!>FK>m9Q*Nwvv7hM}S?N6mAi6UR!$DF)Du=rSq|%^+N5Q zrWCq4+u*4%=BbKEN?$x^i1bWLU>Bt$o%RAdza(%B^%Hn-mc`v`VB{etb$Z}GXNomd z&-9_wXh>C#(`#vzGEAj-UM;nf+}SO5o{6|rZY5ZY_@1G@mKs@F?y}(vTXUQ|Hvs3x+?oFQXuWsD*OPY&x~yjKCJtZCLKA(YCW9J#Rl8un4_C2C6A$x` z!dKG|Qdde&TV{7~rIJ1A2j$-}tY>mP-?wmNi^huEAbwK2eScRIYHbo4F?>u!33hL*Px65f0i4vXsWq@ST8rY`NH+U7z zlChliWiGITjbHtv={UaNkA;9+q8GnQD4TowH^r!%pENs1+wjLuK8xPrg_n&G?folU I?)BV%01N2u0ssI2 literal 0 HcmV?d00001 diff --git a/Xamarin.Forms.ControlGallery.Tizen/res/Vegetables.jpg b/Xamarin.Forms.ControlGallery.Tizen/res/Vegetables.jpg new file mode 100644 index 0000000000000000000000000000000000000000..297964d72689045c4677598dfc2dd09fd6e9393e GIT binary patch literal 5349 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 _viewHolderIndexTable = new Dictionary(); ViewHolder _lastSelectedViewHolder; @@ -92,12 +94,19 @@ namespace Xamarin.Forms.Platform.Tizen.Native { OnAdaptorChanging(); _adaptor = value; - _adaptor.CollectionView = this; OnAdaptorChanged(); } } - int ICollectionViewController.Count => Adaptor?.Count ?? 0; + int ICollectionViewController.Count + { + get + { + if (Adaptor == null || Adaptor is IEmptyAdaptor) + return 0; + return Adaptor.Count; + } + } EPoint ICollectionViewController.ParentPosition => new EPoint { @@ -189,21 +198,31 @@ namespace Xamarin.Forms.Platform.Tizen.Native ScrollTo(Adaptor.GetItemIndex(item), position, animate); } - void ICollectionViewController.RequestLayoutItems() => RequestLayoutItems(); + public void ItemMeasureInvalidated(int index) + { + LayoutManager?.ItemMeasureInvalidated(index); + } + void ICollectionViewController.RequestLayoutItems() => RequestLayoutItems(); ESize ICollectionViewController.GetItemSize() { + return (this as ICollectionViewController).GetItemSize(LayoutManager.IsHorizontal ? AllocatedSize.Width * 100 : AllocatedSize.Width, LayoutManager.IsHorizontal ? AllocatedSize.Height : AllocatedSize.Height * 100); + } + + ESize ICollectionViewController.GetItemSize(int widthConstraint, int heightConstraint) + { if (Adaptor == null) { return new ESize(0, 0); } + if (_itemSize.Width > 0 && _itemSize.Height > 0) { return _itemSize; } - _itemSize = Adaptor.MeasureItem(AllocatedSize.Width, AllocatedSize.Height); + _itemSize = Adaptor.MeasureItem(widthConstraint, heightConstraint); _itemSize.Width = Math.Max(_itemSize.Width, 10); _itemSize.Height = Math.Max(_itemSize.Height, 10); @@ -214,22 +233,32 @@ namespace Xamarin.Forms.Platform.Tizen.Native return _itemSize; } + ESize ICollectionViewController.GetItemSize(int index, int widthConstraint, int heightConstraint) + { + if (Adaptor == null) + { + return new ESize(0, 0); + } + return Adaptor.MeasureItem(index, widthConstraint, heightConstraint); + } + ViewHolder ICollectionViewController.RealizeView(int index) { if (Adaptor == null) return null; - var holder = _pool.GetRecyclerView(); + var holder = _pool.GetRecyclerView(Adaptor.GetViewCategory(index)); if (holder != null) { holder.Show(); } else { - var content = Adaptor.CreateNativeView(this); + var content = Adaptor.CreateNativeView(index, this); holder = new ViewHolder(this); holder.RequestSelected += OnRequestItemSelection; holder.Content = content; + holder.ViewCategory = Adaptor.GetViewCategory(index); _innerLayout.PackEnd(holder); } @@ -268,9 +297,19 @@ namespace Xamarin.Forms.Platform.Tizen.Native void ICollectionViewController.UnrealizeView(ViewHolder view) { _viewHolderIndexTable.Remove(view); + Adaptor.UnBinding(view.Content); view.ResetState(); view.Hide(); _pool.AddRecyclerView(view); + if (_lastSelectedViewHolder == view) + { + _lastSelectedViewHolder = null; + } + } + + void ICollectionViewController.ContentSizeUpdated() + { + OnInnerLayout(); } protected virtual EScroller CreateScroller(EvasObject parent) @@ -308,7 +347,6 @@ namespace Xamarin.Forms.Platform.Tizen.Native } } - void OnLayoutManagerChanging() { _layoutManager?.Reset(); @@ -319,6 +357,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native if (_layoutManager == null) return; + _itemSize = new ESize(-1, -1); _layoutManager.CollectionView = this; _layoutManager.SizeAllocated(AllocatedSize); RequestLayoutItems(); @@ -326,6 +365,11 @@ namespace Xamarin.Forms.Platform.Tizen.Native void OnAdaptorChanging() { + if (Adaptor is IEmptyAdaptor) + { + RemoveEmptyView(); + } + _layoutManager?.Reset(); if (Adaptor != null) { @@ -340,32 +384,67 @@ namespace Xamarin.Forms.Platform.Tizen.Native return; _itemSize = new ESize(-1, -1); + Adaptor.CollectionView = this; (Adaptor as INotifyCollectionChanged).CollectionChanged += OnCollectionChanged; - + + LayoutManager?.ItemSourceUpdated(); RequestLayoutItems(); - if (LayoutManager != null) + if (Adaptor is IEmptyAdaptor) { - var itemSize = (this as ICollectionViewController).GetItemSize(); + CreateEmptyView(); } } void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { + // CollectionChanged could be called when Apaptor was changed on CollectionChanged event + if (Adaptor is IEmptyAdaptor) + { + return; + } + if (e.Action == NotifyCollectionChangedAction.Add) { int idx = e.NewStartingIndex; + if (idx == -1) + { + idx = Adaptor.Count - e.NewItems.Count; + } foreach (var item in e.NewItems) { + foreach (var viewHolder in _viewHolderIndexTable.Keys.ToList()) + { + if (_viewHolderIndexTable[viewHolder] >= idx) + { + _viewHolderIndexTable[viewHolder]++; + } + } LayoutManager.ItemInserted(idx++); } } else if (e.Action == NotifyCollectionChangedAction.Remove) { int idx = e.OldStartingIndex; - foreach (var item in e.OldItems) + + // Can't tracking remove if there is no data of old index + if (idx == -1) + { + LayoutManager.ItemSourceUpdated(); + } + else { - LayoutManager.ItemRemoved(idx); + foreach (var item in e.OldItems) + { + LayoutManager.ItemRemoved(idx); + foreach (var viewHolder in _viewHolderIndexTable.Keys.ToList()) + { + if (_viewHolderIndexTable[viewHolder] > idx) + { + _viewHolderIndexTable[viewHolder]--; + } + } + } } } else if (e.Action == NotifyCollectionChangedAction.Move) @@ -375,11 +454,20 @@ namespace Xamarin.Forms.Platform.Tizen.Native } else if (e.Action == NotifyCollectionChangedAction.Replace) { - LayoutManager.ItemUpdated(e.NewStartingIndex); + // Can't tracking if there is no information old data + if (e.OldItems.Count > 1 || e.NewStartingIndex == -1) + { + LayoutManager.ItemSourceUpdated(); + } + else + { + LayoutManager.ItemUpdated(e.NewStartingIndex); + } } else if (e.Action == NotifyCollectionChangedAction.Reset) { LayoutManager.Reset(); + LayoutManager.ItemSourceUpdated(); } RequestLayoutItems(); } @@ -424,6 +512,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native void OnInnerLayout() { + // OnInnerLayout was called when child item was added + // so, need to check scroll canvas size var size = _layoutManager.GetScrollCanvasSize(); _innerLayout.MinimumWidth = size.Width; _innerLayout.MinimumHeight = size.Height; @@ -456,6 +546,26 @@ namespace Xamarin.Forms.Platform.Tizen.Native } Scroller.SetPageSize(itemSize.Width, itemSize.Height); } + + void CreateEmptyView() + { + _emptyView = Adaptor.CreateNativeView(this); + _emptyView.Show(); + Adaptor.SetBinding(_emptyView, 0); + _emptyView.Geometry = Geometry; + _emptyView.MinimumHeight = Geometry.Height; + _emptyView.MinimumWidth = Geometry.Width; + Scroller.SetContent(_emptyView, true); + _innerLayout.Hide(); + } + + void RemoveEmptyView() + { + _innerLayout.Show(); + Scroller.SetContent(_innerLayout, true); + Adaptor.RemoveNativeView(_emptyView); + _emptyView = null; + } } public interface ICollectionViewController @@ -471,6 +581,12 @@ namespace Xamarin.Forms.Platform.Tizen.Native int Count { get; } ESize GetItemSize(); + + ESize GetItemSize(int widthConstraint, int heightConstraint); + + ESize GetItemSize(int index, int widthConstraint, int heightConstraint); + + void ContentSizeUpdated(); } public enum CollectionViewSelectionMode @@ -478,5 +594,4 @@ namespace Xamarin.Forms.Platform.Tizen.Native None, Single, } - } diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/EmptyItemAdaptor.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/EmptyItemAdaptor.cs index 5aa6d6d..abee190 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/EmptyItemAdaptor.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/EmptyItemAdaptor.cs @@ -6,7 +6,7 @@ using XLabel = Xamarin.Forms.Label; namespace Xamarin.Forms.Platform.Tizen.Native { - public class EmptyItemAdaptor : ItemTemplateAdaptor + public class EmptyItemAdaptor : ItemTemplateAdaptor, IEmptyAdaptor { static DataTemplate s_defaultEmptyTemplate = new DataTemplate(typeof(EmptyView)); public EmptyItemAdaptor(ItemsView itemsView, IEnumerable items, DataTemplate template) : base(itemsView, items, template) diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs index 56f0090..2ac17e2 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs @@ -14,13 +14,22 @@ namespace Xamarin.Forms.Platform.Tizen.Native Rect _last; Dictionary _realizedItem = new Dictionary(); - public GridLayoutManager(bool isHorizontal, int span = 1) + List _itemSizes; + List _cached; + List _accumulatedItemSizes; + bool _hasUnevenRows; + int _baseItemSize; + + public GridLayoutManager(bool isHorizontal, int span = 1) : this(isHorizontal, span, ItemSizingStrategy.MeasureFirstItem) { } + + public GridLayoutManager(bool isHorizontal, int span, ItemSizingStrategy sizingStrategy) { IsHorizontal = isHorizontal; Span = span; + _hasUnevenRows = sizingStrategy == ItemSizingStrategy.MeasureAllItems; } - public int Span { get; internal set; } + public int Span { get; private set; } public bool IsHorizontal { get; } @@ -30,23 +39,63 @@ namespace Xamarin.Forms.Platform.Tizen.Native { Reset(); _allocatedSize = size; - _scrollCanvasSize = new ESize(0, 0); + InitializeMeasureCache(); } public ESize GetScrollCanvasSize() { + if (CollectionView.Count == 0 || _allocatedSize.Width <= 0 || _allocatedSize.Height <= 0) + return _allocatedSize; + if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0) return _scrollCanvasSize; - var itemCount = CollectionView.Count; - var itemSize = CollectionView.GetItemSize(); + int totalItemSize = 0; + if (_hasUnevenRows) + { + totalItemSize = _accumulatedItemSizes[_accumulatedItemSizes.Count - 1]; + } + else + { + totalItemSize = (int)Math.Ceiling(CollectionView.Count / (double)Span) * BaseItemSize; + } + if (IsHorizontal) { - return _scrollCanvasSize = new ESize((int)Math.Ceiling(itemCount / (double)Span) * itemSize.Width , _allocatedSize.Height); + _scrollCanvasSize = new ESize(totalItemSize , _allocatedSize.Height); } else { - return _scrollCanvasSize = new ESize(_allocatedSize.Width, (int)Math.Ceiling(itemCount / (double)Span) * itemSize.Height); + _scrollCanvasSize = new ESize(_allocatedSize.Width, totalItemSize); + } + + return _scrollCanvasSize; + } + + int BaseItemSize + { + get + { + if (_baseItemSize == 0) + { + if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0) + return 0; + + var itembound = CollectionView.GetItemSize(ItemWidthConstraint, ItemHeightConstraint); + _baseItemSize = IsHorizontal ? itembound.Width : itembound.Height; + } + return _baseItemSize; + } + } + + int ItemWidthConstraint => IsHorizontal ? _allocatedSize.Width * 100 : ColumnSize; + int ItemHeightConstraint => IsHorizontal ? ColumnSize : _allocatedSize.Height * 100; + + int ColumnSize + { + get + { + return IsHorizontal ? _allocatedSize.Height / Span : _allocatedSize.Width / Span; } } @@ -58,8 +107,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native return true; var diff = IsHorizontal ? Math.Abs(_last.X - viewport.X) : Math.Abs(_last.Y - viewport.Y); - var margin = IsHorizontal ? CollectionView.GetItemSize().Width : CollectionView.GetItemSize().Height; - if (diff > margin) + if (diff > BaseItemSize) return true; return false; @@ -74,13 +122,10 @@ namespace Xamarin.Forms.Platform.Tizen.Native } _isLayouting = true; _last = bound; - - var size = CollectionView.GetItemSize(); - var itemSize = IsHorizontal ? size.Width : size.Height; - + int padding = Span * 2; - int startIndex = Math.Max(GetStartIndex(bound, itemSize) - padding, 0); - int endIndex = Math.Min(GetEndIndex(bound, itemSize) + padding, CollectionView.Count - 1); + int startIndex = Math.Max(GetStartIndex(bound) - padding, 0); + int endIndex = Math.Min(GetEndIndex(bound) + padding, CollectionView.Count - 1); foreach (var index in _realizedItem.Keys.ToList()) { @@ -122,7 +167,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native public void UpdateSpan(int span) { Span = span; - _scrollCanvasSize = new ESize(0, 0); + InitializeMeasureCache(); CollectionView.RequestLayoutItems(); } @@ -149,6 +194,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native } } + UpdateInsertedSize(inserted); + _scrollCanvasSize = new ESize(0, 0); } @@ -175,6 +222,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native _realizedItem.Remove(last); } + UpdateRemovedSize(removed); _scrollCanvasSize = new ESize(0, 0); } @@ -192,29 +240,76 @@ namespace Xamarin.Forms.Platform.Tizen.Native public Rect GetItemBound(int index) { - var size = CollectionView.GetItemSize(); - if (IsHorizontal) + int rowIndex = index / Span; + int columnIndex = index % Span; + var columnSize = ColumnSize; + + int rowStartPoint = 0; + int columnStartPoint = 0; + int itemSize = 0; + + if (!_hasUnevenRows) { - size.Height = _allocatedSize.Height / Span; + itemSize = BaseItemSize; + rowStartPoint = rowIndex * BaseItemSize; + columnStartPoint = columnIndex * columnSize; } - else + else if (_cached[index]) { - size.Width = _allocatedSize.Width / Span; + var updatedMaxItemSize = GetMaxItemSize(index); + itemSize = _itemSizes[index]; + rowStartPoint = _accumulatedItemSizes[rowIndex] - updatedMaxItemSize + (updatedMaxItemSize - itemSize) / 2; + columnStartPoint = columnSize * columnIndex; } + else + { + var oldMaxItemSize = GetMaxItemSize(index); - int rowIndex = index / Span; - int colIndex = index % Span; - var colSize = IsHorizontal ? size.Height : size.Width; + var measured = CollectionView.GetItemSize(index, ItemWidthConstraint, ItemHeightConstraint); + itemSize = IsHorizontal ? measured.Width : measured.Height; - return - IsHorizontal ? - new Rect(rowIndex * size.Width, colIndex * size.Height, size.Width, size.Height) : - new Rect(colIndex * size.Width, rowIndex * size.Height, size.Width, size.Height); + if (itemSize != _itemSizes[index]) + { + _itemSizes[index] = itemSize; + } + + var updatedMaxItemSize = GetMaxItemSize(index); + if (oldMaxItemSize != updatedMaxItemSize) + { + UpdateAccumulatedItemSize(rowIndex, updatedMaxItemSize - oldMaxItemSize); + int columnStart = (index / Span) * Span; + for (int toUpdate = columnStart; toUpdate < index; toUpdate++) + { + if (_realizedItem.ContainsKey(toUpdate)) + { + var updated = _realizedItem[toUpdate].View.Geometry; + if (IsHorizontal) + { + updated.X += (updatedMaxItemSize - oldMaxItemSize) / 2; + } + else + { + updated.Y += (updatedMaxItemSize - oldMaxItemSize) / 2; + } + _realizedItem[toUpdate].View.Geometry = updated; + } + } + CollectionView.ContentSizeUpdated(); + } + rowStartPoint = _accumulatedItemSizes[rowIndex] - updatedMaxItemSize + (updatedMaxItemSize - itemSize) / 2; + columnStartPoint = columnSize * columnIndex; + + _cached[index] = true; + } + + return IsHorizontal ? + new Rect(rowStartPoint, columnStartPoint, itemSize, columnSize) : + new Rect(columnStartPoint, rowStartPoint, columnSize, itemSize); } public void Reset() { - foreach (var realizedItem in _realizedItem.Values) + foreach (var realizedItem in _realizedItem.Values.ToList()) { CollectionView.UnrealizeView(realizedItem.View); } @@ -222,16 +317,156 @@ namespace Xamarin.Forms.Platform.Tizen.Native _scrollCanvasSize = new ESize(0, 0); } + public void ItemSourceUpdated() + { + InitializeMeasureCache(); + } + + public void ItemMeasureInvalidated(int index) + { + if (_realizedItem.ContainsKey(index)) + { + CollectionView.RequestLayoutItems(); + } + if (_hasUnevenRows) + { + if (_cached.Count > index) + _cached[index] = false; + } + } + + void InitializeMeasureCache() + { + _baseItemSize = 0; + _scrollCanvasSize = new ESize(0, 0); + _last = new Rect(0, 0, 0, 0); + + if (!_hasUnevenRows) + return; + + if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0) + return; + + int n = CollectionView.Count; + _itemSizes = new List(); + _cached = new List(); + _accumulatedItemSizes = new List(); + + for (int i = 0; i < n; i++) + { + _cached.Add(false); + _itemSizes.Add(BaseItemSize); + if (i % Span == 0) + { + int accIndex = i / Span; + _accumulatedItemSizes.Add((accIndex > 0 ? _accumulatedItemSizes[accIndex - 1] : 0) + _itemSizes[i]); + } + } + } + + void BuildAccumulatedSize() + { + _accumulatedItemSizes = new List(); + int n = _itemSizes.Count; + for (int i = 0; i < n; i++) + { + int accIndex = i / Span; + int prevSize = accIndex > 0 ? _accumulatedItemSizes[accIndex - 1] : 0; + if (i % Span == 0) + { + _accumulatedItemSizes.Add(prevSize); + } + int columnMax = _accumulatedItemSizes[accIndex] - prevSize; + if (columnMax < _itemSizes[i]) + { + _accumulatedItemSizes[accIndex] += (_itemSizes[i] - columnMax); + } + } + } + + void UpdateInsertedSize(int inserted) + { + if (!_hasUnevenRows) + return; + + _cached.Insert(inserted, false); + _itemSizes.Insert(inserted, BaseItemSize); + BuildAccumulatedSize(); + } + + void UpdateRemovedSize(int removed) + { + if (!_hasUnevenRows) + return; + + _itemSizes.RemoveAt(removed); + _cached.RemoveAt(removed); + BuildAccumulatedSize(); + } + + void UpdateAccumulatedItemSize(int index, int diff) + { + for (int i = index; i < _accumulatedItemSizes.Count; i++) + { + _accumulatedItemSizes[i] += diff; + } + + if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0) + { + if (IsHorizontal) + { + _scrollCanvasSize.Width += diff; + } + else + { + _scrollCanvasSize.Height += diff; + } + } + } + + int GetMaxItemSize(int index) + { + int columnStart = (index / Span) * Span; + int columnEnd = columnStart + Span - 1; + int max = 0; + for (int i = columnStart; i <= columnEnd && i < _itemSizes.Count; i++) + { + max = Math.Max(max, _itemSizes[i]); + } + return max; + } + + int GetStartIndex(Rect bound, int itemSize) { return ViewPortStartPoint(bound) / itemSize * Span; } + int GetStartIndex(Rect bound) + { + if (!_hasUnevenRows) + { + return GetStartIndex(bound, BaseItemSize); + } + + return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortStartPoint(bound)) * Span; + } + int GetEndIndex(Rect bound, int itemSize) { return (int)Math.Ceiling(ViewPortEndPoint(bound) / (double)itemSize) * Span; } + int GetEndIndex(Rect bound) + { + if (!_hasUnevenRows) + { + return GetEndIndex(bound, BaseItemSize); + } + + return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortEndPoint(bound)) * Span; + } + int ViewPortStartPoint(Rect viewPort) { return IsHorizontal ? viewPort.X : viewPort.Y; @@ -247,6 +482,32 @@ namespace Xamarin.Forms.Platform.Tizen.Native return IsHorizontal ? viewPort.Width : viewPort.Height; } + static int FindFirstGreaterOrEqualTo(IList data, int value) + { + if (data.Count == 0) + return 0; + + int start = 0; + int end = data.Count - 1; + while (start < end) + { + int mid = (start + end) / 2; + if (data[mid] < value) + { + start = mid + 1; + } + else + { + end = mid - 1; + } + } + if (data[start] < value) + { + start++; + } + return start; + } + class RealizedItem { public ViewHolder View { get; set; } diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs index dc7bf73..4b4c626 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs @@ -23,6 +23,10 @@ namespace Xamarin.Forms.Platform.Tizen.Native void ItemUpdated(int index); + void ItemSourceUpdated(); + void Reset(); + + void ItemMeasureInvalidated(int index); } } diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemAdaptor.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemAdaptor.cs index 80ba766..f29abed 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemAdaptor.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemAdaptor.cs @@ -7,6 +7,7 @@ using ESize = ElmSharp.Size; namespace Xamarin.Forms.Platform.Tizen.Native { + public interface IEmptyAdaptor { } public abstract class ItemAdaptor : INotifyCollectionChanged { @@ -89,12 +90,22 @@ namespace Xamarin.Forms.Platform.Tizen.Native return _itemsSource.IndexOf(item); } + public virtual object GetViewCategory(int index) + { + return this; + } + public abstract EvasObject CreateNativeView(EvasObject parent); + public abstract EvasObject CreateNativeView(int index, EvasObject parent); + public abstract void RemoveNativeView(EvasObject native); public abstract void SetBinding(EvasObject view, int index); + public abstract void UnBinding(EvasObject view); public abstract ESize MeasureItem(int widthConstraint, int heightConstraint); + + public abstract ESize MeasureItem(int index, int widthConstraint, int heightConstraint); } } diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemTemplateAdaptor.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemTemplateAdaptor.cs index 46338e1..a03ea39 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemTemplateAdaptor.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemTemplateAdaptor.cs @@ -1,5 +1,7 @@ +using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using ElmSharp; using ESize = ElmSharp.Size; using XLabel = Xamarin.Forms.Label; @@ -8,43 +10,43 @@ namespace Xamarin.Forms.Platform.Tizen.Native { public class ItemDefaultTemplateAdaptor : ItemTemplateAdaptor { + class ToTextConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value?.ToString() ?? string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException(); + } + public ItemDefaultTemplateAdaptor(ItemsView itemsView) : base(itemsView) { ItemTemplate = new DataTemplate(() => { + var label = new XLabel + { + TextColor = Color.Black, + }; + label.SetBinding(XLabel.TextProperty, new Binding(".", converter: new ToTextConverter())); + return new StackLayout { BackgroundColor = Color.White, Padding = 30, Children = { - new XLabel() + label } }; }); } - public override void SetBinding(EvasObject native, int index) - { - ((GetTemplatedView(native) as StackLayout).Children[0] as XLabel).Text = this[index].ToString(); - } - - public override ESize MeasureItem(int widthConstraint, int heightConstraint) - { - var view = (View)ItemTemplate.CreateContent(); - if (Count > 0) - { - ((view as StackLayout).Children[0] as XLabel).Text = this[0].ToString(); - } - var renderer = Platform.GetOrCreateRenderer(view); - var request = view.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request; - renderer.Dispose(); - return request.ToPixel(); - } } public class ItemTemplateAdaptor : ItemAdaptor { Dictionary _nativeFormsTable = new Dictionary(); + Dictionary _dataBindedViewTable = new Dictionary(); ItemsView _itemsView; public ItemTemplateAdaptor(ItemsView itemsView) : base(itemsView.ItemsSource) @@ -66,9 +68,26 @@ namespace Xamarin.Forms.Platform.Tizen.Native return _nativeFormsTable[evasObject]; } - public override EvasObject CreateNativeView(EvasObject parent) + public override object GetViewCategory(int index) { - var view = ItemTemplate.CreateContent() as View; + if (ItemTemplate is DataTemplateSelector selector) + { + return selector.SelectTemplate(this[index], _itemsView); + } + return base.GetViewCategory(index); + } + + public override EvasObject CreateNativeView(int index, EvasObject parent) + { + View view = null; + if (ItemTemplate is DataTemplateSelector selector) + { + view = selector.SelectTemplate(this[index], _itemsView).CreateContent() as View; + } + else + { + view = ItemTemplate.CreateContent() as View; + } var renderer = Platform.GetOrCreateRenderer(view); var native = Platform.GetOrCreateRenderer(view).NativeView; view.Parent = _itemsView; @@ -78,10 +97,16 @@ namespace Xamarin.Forms.Platform.Tizen.Native return native; } + public override EvasObject CreateNativeView(EvasObject parent) + { + return CreateNativeView(0, parent); + } + public override void RemoveNativeView(EvasObject native) { if (_nativeFormsTable.TryGetValue(native, out View view)) { + ResetBindedView(view); Platform.GetRenderer(view)?.Dispose(); _nativeFormsTable.Remove(native); } @@ -91,22 +116,70 @@ namespace Xamarin.Forms.Platform.Tizen.Native { if (_nativeFormsTable.TryGetValue(native, out View view)) { + ResetBindedView(view); view.BindingContext = this[index]; + _dataBindedViewTable[this[index]] = view; + + view.MeasureInvalidated += OnItemMeasureInvalidated; + } + } + + public override void UnBinding(EvasObject native) + { + if (_nativeFormsTable.TryGetValue(native, out View view)) + { + view.MeasureInvalidated -= OnItemMeasureInvalidated; + ResetBindedView(view); } } public override ESize MeasureItem(int widthConstraint, int heightConstraint) { - var view = ItemTemplate.CreateContent() as View; - var renderer = Platform.GetOrCreateRenderer(view); - view.Parent = _itemsView; - if (Count > 0) - view.BindingContext = this[0]; - var request = view.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request; - renderer.Dispose(); + return MeasureItem(0, widthConstraint, heightConstraint); + } + + public override ESize MeasureItem(int index, int widthConstraint, int heightConstraint) + { + if (_dataBindedViewTable.TryGetValue(this[index], out View createdView) && createdView != null) + { + return createdView.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request.ToPixel(); + } - return request.ToPixel(); + View view = null; + if (ItemTemplate is DataTemplateSelector selector) + { + view = selector.SelectTemplate(this[index], _itemsView).CreateContent() as View; + } + else + { + view = ItemTemplate.CreateContent() as View; + } + using (var renderer = Platform.GetOrCreateRenderer(view)) + { + view.Parent = _itemsView; + if (Count > index) + view.BindingContext = this[index]; + var request = view.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request; + return request.ToPixel(); + } } + void ResetBindedView(View view) + { + if (view.BindingContext != null && _dataBindedViewTable.ContainsKey(view.BindingContext)) + { + _dataBindedViewTable[view.BindingContext] = null; + } + } + + void OnItemMeasureInvalidated(object sender, EventArgs e) + { + var data = (sender as View)?.BindingContext ?? null; + int index = GetItemIndex(data); + if (index != -1) + { + CollectionView.ItemMeasureInvalidated(index); + } + } } } diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs index d5c2fc4..b13834f 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs @@ -13,10 +13,19 @@ namespace Xamarin.Forms.Platform.Tizen.Native bool _isLayouting; Rect _last; Dictionary _realizedItem = new Dictionary(); + List _itemSizes; + List _cached; + List _accumulatedItemSizes; - public LinearLayoutManager(bool isHorizontal) + bool _hasUnevenRows; + int _baseItemSize; + + public LinearLayoutManager(bool isHorizontal) : this(isHorizontal, ItemSizingStrategy.MeasureFirstItem) { } + + public LinearLayoutManager(bool isHorizontal, ItemSizingStrategy sizingStrategy) { IsHorizontal = isHorizontal; + _hasUnevenRows = sizingStrategy == ItemSizingStrategy.MeasureAllItems; } public bool IsHorizontal { get; } @@ -27,28 +36,64 @@ namespace Xamarin.Forms.Platform.Tizen.Native { Reset(); _allocatedSize = size; - _scrollCanvasSize = new ESize(0, 0); + InitializeMeasureCache(); } ESize _scrollCanvasSize; public ESize GetScrollCanvasSize() { + if (CollectionView.Count == 0 || _allocatedSize.Width <= 0 || _allocatedSize.Height <= 0) + { + return _allocatedSize; + } + + if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0) return _scrollCanvasSize; - var itemCount = CollectionView.Count; - var itemSize = CollectionView.GetItemSize(); + int totalItemSize = 0; + + if (_hasUnevenRows) + { + totalItemSize = _accumulatedItemSizes[_accumulatedItemSizes.Count - 1]; + } + else + { + totalItemSize = BaseItemSize * CollectionView.Count; + } + if (IsHorizontal) { - return _scrollCanvasSize = new ESize(itemCount * itemSize.Width, _allocatedSize.Height); + _scrollCanvasSize = new ESize(totalItemSize, _allocatedSize.Height); } else { - return _scrollCanvasSize = new ESize(_allocatedSize.Width, itemCount * itemSize.Height); + _scrollCanvasSize = new ESize(_allocatedSize.Width, totalItemSize); + } + + return _scrollCanvasSize; + } + + int BaseItemSize + { + get + { + if (_baseItemSize == 0) + { + if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0) + return 0; + + var itemBound = CollectionView.GetItemSize(ItemWidthConstraint, ItemHeightConstraint); + _baseItemSize = IsHorizontal ? itemBound.Width : itemBound.Height; + } + return _baseItemSize; } } + int ItemWidthConstraint => IsHorizontal ? _allocatedSize.Width * 100 : _allocatedSize.Width; + int ItemHeightConstraint => IsHorizontal ? _allocatedSize.Height : _allocatedSize.Height * 100; + bool ShouldRearrange(Rect viewport) { if (_isLayouting) @@ -57,8 +102,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native return true; var diff = IsHorizontal ? Math.Abs(_last.X - viewport.X) : Math.Abs(_last.Y - viewport.Y); - var margin = IsHorizontal ? CollectionView.GetItemSize().Width : CollectionView.GetItemSize().Height; - if (diff > margin) + if (diff > BaseItemSize) return true; return false; @@ -71,17 +115,15 @@ namespace Xamarin.Forms.Platform.Tizen.Native { return; } + _isLayouting = true; _last = bound; - var size = CollectionView.GetItemSize(); - var itemSize = IsHorizontal ? size.Width : size.Height; - int startIndex = Math.Max(GetStartIndex(bound, itemSize) - 2, 0); - int endIndex = Math.Min(GetEndIndex(bound, itemSize) + 2, CollectionView.Count - 1); + int startIndex = Math.Max(GetStartIndex(bound) - 2, 0); + int endIndex = Math.Min(GetEndIndex(bound) + 2, CollectionView.Count - 1); foreach (var index in _realizedItem.Keys.ToList()) { - if (index < startIndex || index > endIndex) { CollectionView.UnrealizeView(_realizedItem[index].View); @@ -138,6 +180,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native } } + UpdateInsertedSize(inserted); + _scrollCanvasSize = new ESize(0, 0); } @@ -164,6 +208,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native _realizedItem.Remove(last); } + UpdateRemovedSize(removed); + _scrollCanvasSize = new ESize(0, 0); } @@ -181,24 +227,47 @@ namespace Xamarin.Forms.Platform.Tizen.Native public Rect GetItemBound(int index) { - var size = CollectionView.GetItemSize(); - if (IsHorizontal) + int itemSize = 0; + int startPoint = 0; + + if (!_hasUnevenRows) + { + itemSize = BaseItemSize; + startPoint = itemSize * index; + } + else if (index >= _itemSizes.Count) { - size.Height = _allocatedSize.Height; + return new Rect(0, 0, 0, 0); + } + else if (_cached[index]) + { + itemSize = _itemSizes[index]; + startPoint = _accumulatedItemSizes[index] - itemSize; } else { - size.Width = _allocatedSize.Width; + var measured = CollectionView.GetItemSize(index, ItemWidthConstraint, ItemHeightConstraint); + itemSize = IsHorizontal ? measured.Width : measured.Height; + + if (itemSize != _itemSizes[index]) + { + UpdateAccumulatedItemSize(index, itemSize - _itemSizes[index]); + _itemSizes[index] = itemSize; + + CollectionView.ContentSizeUpdated(); + } + startPoint = _accumulatedItemSizes[index] - itemSize; + _cached[index] = true; } - return - IsHorizontal ? - new Rect(index * size.Width, 0, size.Width, size.Height) : - new Rect(0, index * size.Height, size.Width, size.Height); + + return IsHorizontal ? + new Rect(startPoint, 0, itemSize, _allocatedSize.Height) : + new Rect(0, startPoint, _allocatedSize.Width, itemSize); } public void Reset() { - foreach (var realizedItem in _realizedItem.Values) + foreach (var realizedItem in _realizedItem.Values.ToList()) { CollectionView.UnrealizeView(realizedItem.View); } @@ -206,16 +275,78 @@ namespace Xamarin.Forms.Platform.Tizen.Native _scrollCanvasSize = new ESize(0, 0); } + public void ItemSourceUpdated() + { + InitializeMeasureCache(); + } + + public void ItemMeasureInvalidated(int index) + { + if (_realizedItem.ContainsKey(index)) + { + CollectionView.RequestLayoutItems(); + } + if (_hasUnevenRows) + { + if (_cached.Count > index) + _cached[index] = false; + } + } + + void InitializeMeasureCache() + { + _baseItemSize = 0; + _scrollCanvasSize = new ESize(0, 0); + + if (!_hasUnevenRows) + return; + + if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0) + return; + + int n = CollectionView.Count; + _itemSizes = new List(); + _cached = new List(); + _accumulatedItemSizes = new List(); + + for (int i = 0; i < n; i++) + { + _cached.Add(false); + _itemSizes.Add(BaseItemSize); + _accumulatedItemSizes.Add((i > 0 ? _accumulatedItemSizes[i - 1] : 0) + _itemSizes[i]); + } + } + int GetStartIndex(Rect bound, int itemSize) { return ViewPortStartPoint(bound) / itemSize; } + int GetStartIndex(Rect bound) + { + if (!_hasUnevenRows) + { + return GetStartIndex(bound, BaseItemSize); + } + + return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortStartPoint(bound)); + } + int GetEndIndex(Rect bound, int itemSize) { return (int)Math.Ceiling(ViewPortEndPoint(bound) / (double)itemSize); } + int GetEndIndex(Rect bound) + { + if (!_hasUnevenRows) + { + return GetEndIndex(bound, BaseItemSize); + } + + return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortEndPoint(bound)); + } + int ViewPortStartPoint(Rect viewPort) { return IsHorizontal ? viewPort.X : viewPort.Y; @@ -231,6 +362,76 @@ namespace Xamarin.Forms.Platform.Tizen.Native return IsHorizontal ? viewPort.Width : viewPort.Height; } + void UpdateAccumulatedItemSize(int index, int diff) + { + for (int i = index; i < _accumulatedItemSizes.Count; i++) + { + _accumulatedItemSizes[i] += diff; + } + + if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0) + { + + if (IsHorizontal) + { + _scrollCanvasSize.Width += diff; + } + else + { + _scrollCanvasSize.Height += diff; + } + } + } + + void UpdateRemovedSize(int removed) + { + if (!_hasUnevenRows) + return; + var removedSize = _itemSizes[removed]; + _itemSizes.RemoveAt(removed); + UpdateAccumulatedItemSize(removed, -removedSize); + _accumulatedItemSizes.RemoveAt(removed); + _cached.RemoveAt(removed); + } + + void UpdateInsertedSize(int inserted) + { + if (!_hasUnevenRows) + return; + + _cached.Insert(inserted, false); + _itemSizes.Insert(inserted, BaseItemSize); + _accumulatedItemSizes.Insert(inserted, 0); + _accumulatedItemSizes[inserted] = inserted > 0 ? _accumulatedItemSizes[inserted - 1] : 0; + UpdateAccumulatedItemSize(inserted, BaseItemSize); + } + + static int FindFirstGreaterOrEqualTo(IList data, int value) + { + if (data.Count == 0) + return 0; + + int start = 0; + int end = data.Count - 1; + while (start < end) + { + int mid = (start + end) / 2; + if (data[mid] < value) + { + start = mid + 1; + } + else + { + end = mid - 1; + } + } + if (data[start] < value) + { + start++; + } + return start; + } + class RealizedItem { public ViewHolder View { get; set; } diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/RecyclerPool.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/RecyclerPool.cs index 37f2e4f..d39f64c 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/RecyclerPool.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/RecyclerPool.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using ElmSharp; +using System.Linq; namespace Xamarin.Forms.Platform.Tizen.Native { @@ -21,6 +21,14 @@ namespace Xamarin.Forms.Platform.Tizen.Native _pool.AddLast(view); } + public ViewHolder GetRecyclerView(object category) + { + var holder = _pool.Where(d => d.ViewCategory == category).FirstOrDefault(); + if (holder != null) + _pool.Remove(holder); + return holder; + } + public ViewHolder GetRecyclerView() { if (_pool.First != null) diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ViewHolder.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ViewHolder.cs index 53e6887..00be58f 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ViewHolder.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ViewHolder.cs @@ -28,6 +28,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native Initialize(parent); } + public object ViewCategory { get; set; } public EColor FocusedColor { get; set; } public EColor SelectedColor { get; set; } diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs index 3f17f31..1db24c8 100644 --- a/Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs +++ b/Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs @@ -3,7 +3,7 @@ using EColor = ElmSharp.Color; namespace Xamarin.Forms.Platform.Tizen { - public class FrameRenderer : ViewRenderer + public class FrameRenderer : LayoutRenderer { const int _thickness = 2; const int _shadow_shift = 2; @@ -21,7 +21,7 @@ namespace Xamarin.Forms.Platform.Tizen RegisterPropertyHandler(Frame.HasShadowProperty, UpdateShadowVisibility); } - protected override void OnElementChanged(ElementChangedEventArgs e) + protected override void OnElementChanged(ElementChangedEventArgs e) { if (Control == null) { @@ -105,15 +105,15 @@ namespace Xamarin.Forms.Platform.Tizen void UpdateColor() { - if (Element.BorderColor.IsDefault) + if ((Element as Frame).BorderColor.IsDefault) _frame.Color = s_DefaultColor; else - _frame.Color = Element.BorderColor.ToNative(); + _frame.Color = (Element as Frame).BorderColor.ToNative(); } void UpdateShadowVisibility() { - if (Element.HasShadow) + if ((Element as Frame).HasShadow) _shadow.Show(); else _shadow.Hide(); diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs index 01e8ec7..5dbc27b 100644 --- a/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs +++ b/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs @@ -14,6 +14,7 @@ namespace Xamarin.Forms.Platform.Tizen RegisterPropertyHandler(ItemsView.ItemsSourceProperty, UpdateItemsSource); RegisterPropertyHandler(ItemsView.ItemTemplateProperty, UpdateAdaptor); RegisterPropertyHandler(ItemsView.ItemsLayoutProperty, UpdateItemsLayout); + RegisterPropertyHandler(ItemsView.ItemSizingStrategyProperty, UpdateSizingStrategy); RegisterPropertyHandler(SelectableItemsView.SelectedItemProperty, UpdateSelectedItem); RegisterPropertyHandler(SelectableItemsView.SelectionModeProperty, UpdateSelectionMode); } @@ -143,12 +144,21 @@ namespace Xamarin.Forms.Platform.Tizen { if (Element.ItemsLayout != null) { - Control.LayoutManager = Element.ItemsLayout.ToLayoutManager(); + Control.LayoutManager = Element.ItemsLayout.ToLayoutManager(Element.ItemSizingStrategy); Control.SnapPointsType = (Element.ItemsLayout as ItemsLayout)?.SnapPointsType ?? SnapPointsType.None; Element.ItemsLayout.PropertyChanged += OnLayoutPropertyChanged; } } + void UpdateSizingStrategy(bool initialize) + { + if (initialize) + { + return; + } + Control.LayoutManager = Element.ItemsLayout.ToLayoutManager(Element.ItemSizingStrategy); + } + void OnLayoutPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ItemsLayout.SnapPointsType)) @@ -164,14 +174,14 @@ namespace Xamarin.Forms.Platform.Tizen static class ItemsLayoutExtension { - public static ICollectionViewLayoutManager ToLayoutManager(this IItemsLayout layout) + public static ICollectionViewLayoutManager ToLayoutManager(this IItemsLayout layout, ItemSizingStrategy sizing = ItemSizingStrategy.MeasureFirstItem) { switch (layout) { case ListItemsLayout listItemsLayout: - return new LinearLayoutManager(listItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal); + return new LinearLayoutManager(listItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal, sizing); case GridItemsLayout gridItemsLayout: - return new GridLayoutManager(gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal, gridItemsLayout.Span); + return new GridLayoutManager(gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal, gridItemsLayout.Span, sizing); default: break; } -- 2.7.4