如何使用Xamarin Forms在标签云中制作按钮?

时间:2017-12-18 03:13:23

标签: xamarin xamarin.forms

我有这样的代码:

<TableSection>
   <ViewCell Height="200">
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>      
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
   </ViewCell>
</TableSection>

我想做的是让按钮看起来像标签云,并扩展ViewCell高度以适应它们。

我没有看到任何关于如何做到这一点的例子,并希望得到一些反馈/建议。

由于

更新1 - 以下是我尝试但不起作用

   <ViewCell Height="200">
      <StackLayout Orientation="Horizontal" IsClippedToBounds="false" Spacing="5">
         <Button Text="ABCDEF1" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF2" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF3" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF4" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF5" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF6" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF7" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF8" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
      </StackLayout>
   </ViewCell>

不幸的是,我看到的只是前四个按钮,之后没有任何内容。我希望看到的是第一行上的按钮,然后是第二行上的另外四个按钮,其中视图单元的高度扩展到适合。

3 个答案:

答案 0 :(得分:3)

正如迭戈建议的那样,我们可以自定义Layout来实现期望风格。

创建源自Layout<View>的新子类,我们只需要

  1. 覆盖OnMeasure以返回此布局的大小。

  2. 覆盖LayoutChildren以确定孩子的位置和大小。

  3. 创建Bindable Properties以个性化每次需要的使用。

  4. 自定义布局:

    public class WrapLayout : Layout<View>
    {
        public static readonly BindableProperty SpacingProperty =
            BindableProperty.Create
            (
                "Spacing",
                typeof(double),
                typeof(WrapLayout),
                10.0,
                propertyChanged: (bindable, oldvalue, newvalue) => ((WrapLayout)bindable).OnSizeChanged()
            );
    
        public double Spacing
        {
            get { return (double)GetValue(SpacingProperty); }
            set { SetValue(SpacingProperty, value); }
        }
    
        private void OnSizeChanged()
        {
            this.ForceLayout();
        }
    
        protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
        {
            if (WidthRequest > 0)
                widthConstraint = Math.Min(widthConstraint, WidthRequest);
            if (HeightRequest > 0)
                heightConstraint = Math.Min(heightConstraint, HeightRequest);
    
            double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
            double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);
    
            return DoHorizontalMeasure(internalWidth, internalHeight);
        }
    
        private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
        {
            int rowCount = 1;
    
            double width = 0;
            double height = 0;
            double minWidth = 0;
            double minHeight = 0;
            double widthUsed = 0;
    
            foreach (var item in Children)
            {
                var size = item.Measure(widthConstraint, heightConstraint);
    
                height = Math.Max(height, size.Request.Height);
    
                var newWidth = width + size.Request.Width + Spacing;
                if (newWidth > widthConstraint)
                {
                    rowCount++;
                    widthUsed = Math.Max(width, widthUsed);
                    width = size.Request.Width;
                }
                else
                    width = newWidth;
    
                minHeight = Math.Max(minHeight, size.Minimum.Height);
                minWidth = Math.Max(minWidth, size.Minimum.Width);
            }
    
            if (rowCount > 1)
            {
                width = Math.Max(width, widthUsed);
                height = (height + Spacing) * rowCount - Spacing; // via MitchMilam 
            }
    
            return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
        }
    
        protected override void LayoutChildren(double x, double y, double width, double height)
        {
            double rowHeight = 0;
            double yPos = y, xPos = x;
    
            foreach (var child in Children.Where(c => c.IsVisible))
            {
                var request = child.Measure(width, height);
    
                double childWidth = request.Request.Width;
                double childHeight = request.Request.Height;
                rowHeight = Math.Max(rowHeight, childHeight);
    
                if (xPos + childWidth > width)
                {
                    xPos = x;
                    yPos += rowHeight + Spacing;
                    rowHeight = 0;
                }
    
                var region = new Rectangle(xPos, yPos, childWidth, childHeight);
                LayoutChildIntoBoundingRegion(child, region);
                xPos += region.Width + Spacing;
            }
        }
    }
    

    在Xmal

    <local:WrapLayout Spacing="5">
        <Button Text="111111111111111" BackgroundColor="Red"/>
        <Button Text="222" BackgroundColor="Green"/>
        <Button Text="33333333333333333333333333" BackgroundColor="Gray"/>
        <Button Text="444444" BackgroundColor="Blue"/>
        <Button Text="5" BackgroundColor="Orange"/>
        <Button Text="6666666666666666" BackgroundColor="Aqua"/>
        <Button Text="77777777" BackgroundColor="Yellow"/>
        <Button Text="888" BackgroundColor="Pink"/>
        <Button Text="9 9 9 9" BackgroundColor="Purple"/>
        <Button Text="10" BackgroundColor="Brown"/>
    </local:WrapLayout>
    

    测试

    enter image description here

答案 1 :(得分:2)

您可以按照xamarin表单开发人员网站https://developer.xamarin.com/guides/xamarin-forms/user-interface/layouts/custom/

提供的指南来实现此目的

您描述的问题与本指南所示的示例代码完全相同。创建自定义布局,您可以在任何需要的地方重用此解决方案。

简洁地说,您需要做的就是创建WrapLayout:

  1. 实施处理儿童安排的方法
  2. 提供可绑定的属性以个性化每个需求的使用
  3. 按照您的意愿消费
  4. 你可能得到这样的结果:

    Sample result

答案 2 :(得分:1)

why not use a StackLayout ?

package org.stackoverflow.jena.customaggregate;

import com.google.common.base.Joiner;
import org.apache.jena.atlas.logging.LogCtl;
import org.apache.jena.graph.Graph;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.sparql.engine.binding.Binding;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.expr.ExprEvalException;
import org.apache.jena.sparql.expr.ExprList;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.expr.aggregate.Accumulator;
import org.apache.jena.sparql.expr.aggregate.AccumulatorFactory;
import org.apache.jena.sparql.expr.aggregate.AggCustom;
import org.apache.jena.sparql.expr.aggregate.AggregateRegistry;
import org.apache.jena.sparql.function.FunctionEnv;
import org.apache.jena.sparql.graph.NodeConst;
import org.apache.jena.sparql.sse.SSE;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public class AggGroupConcatSorted {
    static {
        LogCtl.setCmdLogging();
    }

    static AccumulatorFactory accumulatorFactory = (agg, distinct) -> new AccGroupConcatSorted(agg);

    static class AccGroupConcatSorted implements Accumulator {
        private AggCustom agg;

        private List<String> nodeStrList = new ArrayList<>();

        AccGroupConcatSorted(AggCustom agg) {
            this.agg = agg;
        }

        @Override
        public void accumulate(Binding binding, FunctionEnv functionEnv) {
            ExprList exprList = agg.getExprList();
            for (Expr expr : exprList) {
                try {
                    NodeValue nv = expr.eval(binding, functionEnv);
                    // Evaluation succeeded, add string to list
                    nodeStrList.add(nv.asString());
                } catch (ExprEvalException ex) {
                }
            }
        }

        @Override
        public NodeValue getValue() {
            // sort list
            Collections.sort(nodeStrList);
            // return single node which in fact is the concatenated string of the list elements
            return NodeValue.makeString(Joiner.on(" ").join(nodeStrList));
        }
    }

    public static void main(String[] args) {
        // Example aggregate that concatenates the node values in sorted order
        String aggUri = "http://example.org/group_concat_sorted" ;

        // register the custom aggregate - returns unbound for no rows
        AggregateRegistry.register(aggUri, accumulatorFactory, NodeConst.nodeMinusOne);

        // sample data
        Graph g = SSE.parseGraph("(graph (:s :p \"b\") (:s :p \"bc\") (:s :p \"abc\"))") ;
        String qs = "SELECT (<http://example.org/group_concat_sorted>(?o) AS ?x) {?s ?p ?o}" ;

        // query execution
        Query q = QueryFactory.create(qs) ;
        try ( QueryExecution qexec = QueryExecutionFactory.create(q, ModelFactory.createModelForGraph(g)) ) {
            ResultSet rs = qexec.execSelect() ;
            ResultSetFormatter.out(rs);
        }
    }

}