2016-03-22 31 views
7

È possibile modificare i vincoli di un RelativeLayout dopo che sono stati impostati una volta?Xamarin.Forms: modifica vincoli RelativeLayout in seguito

Nella documentazione vedo metodi come GetBoundsConstraint(BindableObject), ma penso che si possano usare solo se si dispone di un file XAML. Per ora sto cercando di farlo in codice. Ottengo null se provo

RelativeLayout.GetBoundsConstraint(this.label); 

L'unico modo che ho scoperto è quello di togliere i bambini dal layout e aggiungerlo con i nuovi vincoli di nuovo.

Esempio:

public class TestPage : ContentPage 
{ 
    private RelativeLayout relativeLayout; 
    private BoxView view; 
    private bool moreWidth = false; 

    public TestPage() 
    { 
     this.view = new BoxView 
     { 
      BackgroundColor = Color.Yellow, 
     }; 

     Button button = new Button 
     { 
      Text = "change", 
      TextColor = Color.Black, 
     }; 

     button.Clicked += Button_Clicked; 

     this.relativeLayout = new RelativeLayout(); 

     this.relativeLayout.Children.Add(this.view, 
      Constraint.Constant(0), 
      Constraint.Constant(0), 
      Constraint.Constant(80), 
      Constraint.RelativeToParent((parent) => 
      { 
       return parent.Height; 
      })); 

     this.relativeLayout.Children.Add(button, 
      Constraint.RelativeToParent((parent) => 
      { 
       return parent.Width/2; 
      }), 
      Constraint.RelativeToParent((parent) => 
      { 
       return parent.Height/2; 
      })); 

     this.Content = this.relativeLayout; 
    } 

    private void Button_Clicked(object sender, EventArgs e) 
    { 
     double width = 0; 
     if(this.moreWidth) 
     { 
      width = 120; 
     } 
     else 
     { 
      width = 80; 
     } 
     var c= BoundsConstraint.FromExpression((Expression<Func<Rectangle>>)(() => new Rectangle(0, 0, width, this.Content.Height)), new View[0]); 
     RelativeLayout.SetBoundsConstraint(this.view, c); 
     this.relativeLayout.ForceLayout(); 
     this.moreWidth = !this.moreWidth; 
    } 
} 

risposta

11

Essa non ufficialmente possibile con la versione corrente di forme Xamarin. Il contenitore RelativeLayout ricalcola i vincoli solo quando si aggiungono/rimuovono elementi dalla propria raccolta figli (memorizza nella cache i vincoli risolti, presumibilmente per le prestazioni). Anche se i vari vincoli sono implementati come proprietà Bindable, non vengono ancora ricalcolati se modificati.

Suppongo che l'intenzione sia di un giorno rispettare gli aggiornamenti dei vincoli, che sarebbero utili con le animazioni, ad esempio, ma per ora non sembra funzionare in questo modo.

TUTTAVIA, ho preso uno sguardo alla fonte decompilato per RelativeLayout ed è possibile incidere insieme un modo intorno ad esso - ma potrebbe non soddisfare le vostre esigenze, a seconda di quanto la funzionalità desiderata e quanto complesso le tue definizioni di vincoli sono.

Vedere questo codice di esempio (la parte fondamentale è l'impostazione del vincolo utilizzando SetBoundsConstraint, che ignora i limiti calcolati internamente della vista aggiunto - e quindi chiamando ForceLayout()):

public partial class App : Application 
{ 
    public App() 
    { 
     var label = new Label { 
      Text = "Test", 
      HorizontalTextAlignment = TextAlignment.Center, 
      VerticalTextAlignment = TextAlignment.Center, 
      BackgroundColor = Color.Silver 
     }; 
     var layout = new RelativeLayout(); 
     layout.Children.Add (label, 
      Constraint.Constant (50), 
      Constraint.Constant (100), 
      Constraint.Constant (260), 
      Constraint.Constant (30)); 
     MainPage = new ContentPage { 
      Content = layout 
     }; 

     var fwd = true; 
     layout.Animate ("bounce", 
      (delta) => { 
       var d = fwd ? delta : 1.0 - delta; 
       var y = 100.0 + (50.0 * d); 
       var c = BoundsConstraint.FromExpression ((Expression<Func<Rectangle>>)(() => new Rectangle (50, y, 260, 30)), new View [0]); 
       RelativeLayout.SetBoundsConstraint(label, c); 
       layout.ForceLayout(); 
      }, 16, 800, Easing.SinInOut, (f, b) => { 
       // reset direction 
       fwd = !fwd; 
      },() => { 
       // keep bouncing 
       return true; 
      }); 
    } 
} 
+0

Grazie per la tua eccellente risposta! È possibile utilizzare 'BoundsConstraint.FromExpression' insieme a' Constraint.RelativeToParent'? O puoi solo impostare valori per 'Rectangle'? Quello che sto cercando di fare è cambiare la larghezza di un elemento che occupa metà dello schermo. Pertanto devi conoscere l'altezza del genitore. Attualmente sto usando 'this.Content.Height', che sembra funzionare. Anche se devo testarlo più lontano. – testing

+1

Dovrebbe funzionare in questo modo fintanto che tutti i vincoli sull'elemento che stai modificando sono Costanti o RelativeToParent (il BoundsConstraint è una combinazione di tutti e quattro i vincoli x/y/larghezza/altezza, quindi tutti dovrebbero rispettare questo criteri). Se qualcuno di essi utilizza RelativeToView, è necessario passare quelle viste in una matrice come secondo parametro a 'FromExpression()'. È difficile dirlo con certezza se questo hack funzionerebbe per qualsiasi cosa tranne Constant, a meno che non lo provi. E naturalmente dipende dagli interni, quindi è soggetto alla rottura della prossima versione XF. –

+0

Ho provato a lavorare con 'RelativeToParent', ma ho faticato a usare la notazione corretta. Invece di una costante ho provato a usare 'Constraint.RelativeToParent ((parent) => {return parent.Height;})', ma ho ricevuto un messaggio come * troppi parametri *. Hai funzionato? – testing

1

Se ti ho capito bene, mi avrebbe cercato il seguente

Implementare evento SizeChanged per relativeLayout

come: -

var relativeLayout = new RelativeLayout(); 

relativeLayout.SizeChanged += someEventForTesting; 

e invocare l'evento ogni volta che si desidera ridimensionare il layout relativo o un determinato bambino.

public void someEventForTesting(Object sender , EventArgs) 
{ 
var layout = (RelativeLayout)sender ; 
//here you have access to layout and every child , you can add them again with new constraint . 
} 
+0

Se viene attivato l'evento 'SizeChanged', ho accesso ai vincoli? Come posso attivare l'evento 'SizeChanged' con il relativo' RelativeLayout'? L'evento – testing

+0

'SizeChanged' non offre alcun vantaggio a meno che non si tratti di utilizzare una' Grid' per il layout e il posizionamento/ridimensionamento manuale degli elementi di contenuto nel codice. Inoltre, per attivare l'evento 'SizeChanged' è necessario ridimensionare il contenitore, cosa che può essere piuttosto costosa se ci sono molte viste secondarie. –

+0

@ testing- Come ha giustamente sottolineato Keith, può essere un lavoro estensivo quando si hanno molti figli, per attivare l'evento basta aggiungere qualcosa a un particolare layout, si cercherà di cambiare il contenitore e l'evento verrà attivato, in caso, u riscrivi tutto relativo Relay se possibile, o cambia la posizione di quei bambini che saranno interessati. Immagina una situazione (puramente ipotetica): - hai un layout con 4 pulsanti. vuoi cliccare 4 ° e ottenere un nuovo btn che appare proprio al 4 °. in questo scenario. sull'evento click bttn, puoi attivare sizeChange e fare il lavoro lì –