钛合金ListView嵌套模型标签数组

时间:2014-02-12 08:39:46

标签: javascript listview titanium titanium-mobile titanium-alloy

我正在使用Titanium Alloy 3.2版。我在列表视图中有一组帖子。我的数据如下:

  [ 
   { username: 'dude',   imageUrl: 'url', tags: ['tag1','tag2','tag3']        },
   { username: 'wheres', imageUrl: 'url', tags: ['tag1']                      },
   { username: 'my',     imageUrl: 'url', tags: ['tag1','tag2','tag3','tag4'] },
   { username: 'car',    imageUrl: 'url', tags: ['tag1','tag2']               }
  ]

这是xml。这仅适用于用户名和图像。我无法弄清楚如何将标签添加到每个帖子。

   <ListView id="streamListview">

       <Templates>
            <ItemTemplate name="template" id="template">
                <View class="item-container">               
                   <ImageView bindId="pic" class="pic"/>
                   <Label bindId="username" class="username"/>
                </View>
            </ItemTemplate>
        </Templates>

        <ListSection id="section">
            <ListItem template="template" class="list-item"/>
        </ListSection>                  

   </ListView>

我的控制器代码(没有标签)

   var posts = [];
   for (var i=0; i<data.length; i++){
      var post = {
          template : "template",
          pic      : { image : data[i].get("imageUrl") },
          username : { text  : data[i].get("username") }
      };
      posts.push(post);            
   }
   $.section.setItems(posts);

如果我应该事先在模板中声明每个视图,我怎样才能在帖子中添加标签(可点击的)?我的示例中的每个标记数组都需要不同数量的视图,具体取决于数组长度。理想情况下,每个标记都是自己的UI.Label元素。我相信这可以使用TableView完成,但出于性能原因,我更倾向于使用ListView。

2 个答案:

答案 0 :(得分:5)

我想我知道你需要什么,在这种情况下,因为你想动态生成每个项目(例如,你首先打开你的ListView为空的情况,并进行API调用以获取远程数据并填充带有所述数据的ListView)您需要使用在自己的控制器中声明的ItemTemplates。

您只需像普通一样创建一个新控制器,并在视图xml中放置ItemTemplate:

<Alloy>
    <ItemTemplate name="template" id="template">
         <View class="item-container">               
             <ImageView bindId="pic" class="pic"/>
             <Label bindId="username" class="username"/>
         </View>
    </ItemTemplate>
</Alloy>

在你的tss中,你将模板中每个元素引用的所有样式都放在一起,因为你没有提供一个tss例子,我不知道你的样式属性是什么,但在你需要的时候定义模板的样式,例如让我们说:

"#template": // this is the id of your template in your xml
{
    width : Ti.UI.FILL,
    height : '44dp',
    backgroundColor : '#FFFFFF'
}

要动态填充ListView和ListItems,您需要在API的回调中执行以下操作:

function displayListItems(items)
{
    var itemCollection = [];
    for(var i=0; i < items.length; i++)
    {
        var tmp = {
            pic : {
                image : items[i].image
            },
            username : {
                text : items[i].text
            },
            template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
        };
        itemCollection.push(tmp);
    }
    $.ListView.sections[0].items = itemCollection;
}

瞧,您可以动态填充ListView。现在你可以做一些额外的步骤。

在模板控制器中,您可以将其留空,因为ListView可以管理 itemclick 事件,但如果您希望在Listitem中的某个元素触发时发生不同的操作,则需要指定控制器中为每个元素调用的函数。

例如,假设您将名为 dataInfo 的属性传递给ImageView,并将模板中的Label传递给您:

function displayListItems(items)
{
    var itemCollection = [];
    for(var i=0; i < items.length; i++)
    {
        var tmp = {
            pic : {
                image : items[i].image
                dataInfo : items[i].fooA //lets pass to the ImageView the object fooA
            },
            username : {
                text : items[i].text,
                dataInfo : items[i].fooB //lets pass to the Label the object fooB
            },
            template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
        };
        itemCollection.push(tmp);
    }
    $.ListView.sections[0].items = itemCollection;
}

你希望ImageView和Label调用不同的函数,你需要像这样更改你的xml:

<Alloy>
    <ItemTemplate name="template" id="template">
         <View class="item-container">               
             <ImageView bindId="pic" class="pic" onClick="imageFunction"/> <!-- added onClick event -->
             <Label bindId="username" class="username" onClick="labelFunction"/> <!-- added onClick event -->
         </View>
    </ItemTemplate>
</Alloy>

在您的控制器中,您将声明每个功能:

function imageFunction(e)
{
    var dataInfo;
    if(Ti.Platform.osname === 'android')
    {
        var item = e.section.items[e.itemIndex];
        var bindObject = item[e.bindId];
        dataInfo = bindObject.fooA;
    }
    else
    {
        dataInfo = e.source.fooA;
    }

}

function labelFunction(e)
{
    var dataInfo;
    if(Ti.Platform.osname === 'android')
    {
        var item = e.section.items[e.itemIndex];
        var bindObject = item[e.bindId];
        dataInfo = bindObject.fooB;
    }
    else
    {
        dataInfo = e.source.fooB;
    }
}

现在您可能会问,为什么要检查操作系统名称,这是因为即使您使用相同的功能,Android和iOS也会收到不同的 e 对象。在iOS中,您可以使用 e.source.propertyName 直接访问传递给事件源的任何属性,而在Android中,您需要访问 e.section 中的项目使用 e.itemIndex ,然后使用与之关联的 e.bindId 检索项目内的视图。

ListItems的一个最大限制是更新ListItem中的视图,为此你需要更新你想要在视觉上改变的整个项目,并为它分配一个不同的模板,但是你获胜的速度和#39;能够注意到任何滞后,严重的是ListView的性能是另外的,与ScrollView不同,让我们不要谈论可怕的和有缺陷的TableView。

警告,从Titanium SDK 3.2.0.GA那里出现了ItemTemplates中的一个错误,导致模板中子视图内的视图在Android中更改其zIndex而无法控制它,有两个已知这样的实例: 如果您使用不在子视图中设置布局:这可能会导致应该显示在另一个视图下方的视图。

如果在子视图中使用垂直布局:这可能导致每个视图的位置被加扰,这是因为zIndex改变了垂直布局中的显示顺序。

此错误是随机触发的,Appcelerator团队没有做太多工作,请在此查看JIRA票证TIMOB-16704

如果您使用具有固定定位视图的模板并确保没有视图位于另一个视图之上,也可以避免这种情况,还要记住没有垂直布局,还没有使用水平方式对此进行测试我个人尝试避免水平布局,因为在滚动视图,普通视图等中使用时会出现与其相关的其他错误。

修改

您可能想要做的另一件事是为您渲染的项目指定不同的外观,您必须选择:

  • 在声明ListItem时应用样式。
  • 根据一系列条件,为每个ListItem应用不同的布局。

对于第一个选项,您需要省略或覆盖模板中某些属性的声明:

例如,让我们使用不同的背景颜色,其中 fooA 属性存在,如果不存在,则使用另一种颜色:

function displayListItems(items)
{
    var itemCollection = [];
    for(var i=0; i < items.length; i++)
    {
        var properties = {};
        if(typeof items[i].fooA !== 'undefined')
        {
            properties = {
               backgroundColor : 'red'
            };
        }
        else
        {
            properties = {
               backgroundColor : 'blue'
            };
        }
        var tmp = {
            properties : properties, // properties for the template
            pic : {
                image : items[i].image
                dataInfo : items[i].fooA //lets pass to the ImageView the object fooA
            },
            username : {
                text : items[i].text,
                dataInfo : items[i].fooB //lets pass to the Label the object fooB
            },
            template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
        };
        itemCollection.push(tmp);
    }
    $.ListView.sections[0].items = itemCollection;
}

您可以根据需要更改宽度,高度,backgroundColor,布局等。

现在,如果您希望每个项目具有独特的外观(意味着显示不同内容的不同视图)以及可能是不同的行为,则您需要使用不同的模板。

这可能听起来很麻烦,但事实并非如此,一旦你习惯了模板很快就可以创建,而且不需要很长时间,另一个可能是如果你想要11种不同的外观,这可能意味着你&#39 ;这需要11个模板,但这是一个极端情况,如果你正在处理那么多模板,你可能想要重新考虑你的UI。

虽然限制性,项目模板为您提供了多种选择,但想象力是实现所有可能性的唯一必要条件。

编辑2

我终于明白你的问题是什么,如果你需要创建一个内容根据ax变量更改的模板,那么你应该尝试在ListView控制器上声明模板,但这应该在打开窗口之前完成你是否会要显示ListView,因为 templates 属性只能在创建时设置,你应该添加如下内容:

function createTemplate(items)
{
    var template = {};
    for(var i=0; i < items.length; i++)
    {

        template.childTemplates = [];
        for(var j=0; items[i].tags.length; j++)
        {
           var childTemplate = {
                type: 'Ti.UI.Label',
                bindId: 'tag' + j,
                properties : {
                    width : Ti.UI.SIZE, // Here you define how your style
                    height : Ti.UI.SIZE,
                    font : {
                        fontSize : '18dp'
                    },
                    text : items[i].tags[j].text // you can pass the text here or if you want to on the later for
                }
            };
           template.childTemplates.push(childTemplate);
        }
    }
    // After this you should end up with a template with as many childTemplates as tags each item have, so send the template to the controller with your ListView
    Alloy.createController('ListViewWindow', {customTemplate: template});
}

在ListView控制器中,您可以检索模板:

var args = arguments[0] || {};
var template = args.customTemplate;

$.ListView.templates = {'customTemplate' : template}; // before any window open call, do this

这应该将模板添加到ListView中,您也可以在控制器中创建ListView而不是在Alloy xml中声明它,使用更符合您需求的那个。

答案 1 :(得分:0)

如果您在控制器中动态创建模板,则可以使用ListView。您还需要遍历每个“tags”对象并为每个标记项生成Ti.UI.Label“type”。但是,我不确定此方法是否比使用TableView对象更有效,因为您创建的每个ListItem基本上都包含不同的模板。

要生成动态模板,它将类似于以下内容:请记住,您需要迭代“标记”并生成x Ti.UI.Label类型,其中x是“标记”的长度。此外,click事件应该可以使用Titanium SDK 3.2.1。

var plainTemplate = {
childTemplates: [
    {
        type: 'Ti.UI.Label',
        bindId: 'username'
    },
    {
        type: 'Ti.UI.ImageView',  
        bindId: 'pic'       

    },                    
    {
        type: 'Ti.UI.Label',
        bindId: 'tags', 
        events: { click : handleTagClickEvent }  // Binds a callback to click event
    }
]};


function handleTagClickEvent(e) {
    Ti.API.info('You clicked a tag label: ' + e.type);
}


var listView = Ti.UI.createListView({
    templates: { 'plain': plainTemplate },
    defaultItemTemplate: 'plain'           
});

希望这会以某种方式帮助你!