title: How to fix touch events and images disappearing in a TableView using Titanium Alloy in Android?
body: <p>I'm working with Titanium SDK 3.1.3 and deploying an Alloy project to Android.
  I have a Window in a xml file called UserProfile like this:</p>
  
  <pre><code>&lt;Alloy&gt;
      &lt;Window class="container"&gt;
  
          &lt;View id="MainHolder"&gt;
  
              &lt;View id="MainHolderTitleView"&gt;
  
                  &lt;View id="CloseButton"&gt;
                      &lt;Label id="CloseButtonLabel"&gt;&lt;/Label&gt;
                  &lt;/View&gt;
                  &lt;Label id="MainHolderTitleLabel"&gt;&lt;/Label&gt;
                  &lt;View id="CreatePostButton"&gt;
                      &lt;ImageView id="CreatePostButtonImage" image="/images/menu_options.png"&gt;&lt;/ImageView&gt;
                  &lt;/View&gt;
              &lt;/View&gt;
  
              &lt;View id="AppSettings"&gt;
  
                  &lt;View id="AppOptionButtons"&gt;
                      &lt;View id="MenuOption1" class="optionButton"&gt;
                          &lt;Label id="Option1Label" class="optionButtonLabel"&gt;&lt;/Label&gt;
                      &lt;/View&gt;
                      &lt;View id="MenuOption2" class="optionButton"&gt;
                          &lt;Label id="Option2Label" class="optionButtonLabel"&gt;&lt;/Label&gt;
                      &lt;/View&gt;
                      &lt;View id="MenuOption3" class="optionButton"&gt;
                          &lt;Label id="Option3Label" class="optionButtonLabel"&gt;&lt;/Label&gt;
                      &lt;/View&gt;
  
                  &lt;/View&gt;
  
              &lt;/View&gt;
  
              &lt;View id="MainHolderWorkspace"&gt;
                  &lt;TableView id="AppTableView"&gt;&lt;/TableView&gt;
                  &lt;ActivityIndicator id="activityIndicator"/&gt;
              &lt;/View&gt;
  
          &lt;/View&gt;
  
      &lt;/Window&gt;
  &lt;/Alloy&gt;
  </code></pre>
  
  <p>the tss looks like this:</p>
  
  <pre><code>".container" : {
      backgroundColor:"#b1b1b1",
      width:Ti.UI.FILL, 
      height:Ti.UI.FILL, 
      top:0, 
      left:0,
      navBarHidden : true
  }, 
  "#MenuAppsButton":{
      height:'20dp'
  },
  "#MainHolderTitleLabel":{
      // width : '250dp',
      left : '60dp',
      right : '60dp',
      height : '25dp',
      color:"#FFFFFF", 
      textAlign : 'center',
      font:{fontSize:'12dp'},
      ellipsize : true
  },
  "#CreatePostButton":{
      right:0, 
      width:'44dp', 
      height:'44dp', 
      backgroundColor:"#242424"
  },
  "#CreatePostButtonImage":{
      height:'20dp'
  },
  ".optionButton":
  {
      width:Ti.UI.FILL, 
      height:'44dp', 
      backgroundColor:"#f0f0f0",
      top:1
  }, 
  ".optionButtonLabel":
  {
      color:"#363636", 
      font:{
          fontSize:'12dp', 
          fontWeight:"bold"
      }, 
      height:Ti.UI.SIZE, 
      width:Ti.UI.SIZE
  
  },
  "#AppOptionButtons":
  {
      width:Ti.UI.FILL, 
      height:Ti.UI.SIZE,
      backgroundColor:"transparent", 
      layout:"vertical"
  
  },
  "#AppSettings":
  {
      top:'44dp', 
      left:0, 
      backgroundColor:"transparent", 
      width:Ti.UI.FILL, 
      height:Ti.UI.FILL, 
      layout:"vertical"
  },
  "#MenuHolder":
  {
      width:Ti.UI.FILL, 
      height:Ti.UI.FILL,
      top:0, 
      left:"-320dp", 
      zIndex:0, 
      backgroundColor:"#363636"
  },
  "#MainHolder":{
      width:Ti.UI.FILL, 
      height:Ti.UI.FILL,
      top:0, 
      left:0, 
      zIndex:1
  },
  "#MainHolderTitleView":
  {
      left:0, 
      right:0, 
      height:'44dp', 
      top:0,
      backgroundColor:"#363636", 
      zIndex:0
  }, 
  "#MainHolderTitleDraggingView":
  {
      left:0, 
      width:'44dp', 
      height:'44dp', 
      top:0,
      backgroundColor:"transparent", 
      zIndex:2
  },
  "#MainHolderWorkspace":
  {
      top:'44dp', 
      bottom:0, 
      left:0,
      backgroundColor:"#b1b1b1",
      zIndex:2
  }, 
  "#MainHolderTitleViewMenuButton":
  {
      width:'50dp', 
      height:'44dp', 
      left:0, 
      top:0, 
      backgroundColor:"#242424"
  }, 
  "#CloseButton":
  {
      width:'60dp', 
      height:Ti.UI.FILL, 
      backgroundColor:"#232323", 
      left:0
  },
  "#CloseButtonLabel":
  {
      color:"#FFFFFF", 
      font:{fontSize:'13dp'},
      text : L('close_string')
  }, 
  "#AppTableView":
  {
      width:Ti.UI.FILL, 
      height:Ti.UI.SIZE,
      top:0,
      left:0, 
      showVerticalScrollIndicator:true, 
      backgroundGradient : {
              type : 'linear',
              colors :  [
                  {
                      color : '#e7e7e7',
                      position : 0.0
                  },
                  {
                      color : '#d8d8d8',
                      position : 1.0
                  }
              ]
  
      },
      separatorColor : 'transparent'
  },
  "#activityIndicator":
  {
      width:Ti.UI.FILL
  }
  </code></pre>
  
  <p>In my tableview I'm adding 2 rows with images and 20 rows more with info obtained from a web service, this rows contain images too, at the start. Every time the user clicks a load more view, 20 rows more are added with info obtained from the web service.</p>
  
  <p>Each row I add is composed of different views, depending on the content received from the web service, the only constant being a view in a xml called Stream_Item that looks like this:</p>
  
  <pre><code>&lt;Alloy&gt;
      &lt;View id="ContainerView" class="container"&gt;
  
          &lt;View id="GlobalWorkspace"&gt;
  
  
              &lt;View id="Workspace"&gt;
  
                  &lt;View id="WorkspacePersonalInfo"&gt;
                      &lt;View id="WorkspaceProfileImage"&gt;
                          &lt;ImageView id="WorkspaceProfileImageView" image="/images/emptyPicture.png"&gt;&lt;/ImageView&gt;
                      &lt;/View&gt;
                      &lt;View id="WorkspaceNameAndDateHolder"&gt;
                          &lt;View id="WorkspaceNameTargetHolder"&gt;
                              &lt;Label id="WorkspaceNameLastName"&gt;&lt;/Label&gt;
                              &lt;ImageView id="TargetImageView"&gt;&lt;/ImageView&gt;
                              &lt;Label id="WorkspaceTarget"&gt;&lt;/Label&gt;
                          &lt;/View&gt;
                          &lt;Label id="WorkspaceDate"&gt;&lt;/Label&gt;
                      &lt;/View&gt;
                  &lt;/View&gt;
  
  
                  &lt;View id="WorkspaceContent"&gt;&lt;/View&gt;
              &lt;/View&gt;
  
          &lt;/View&gt;
  
          &lt;View id="WorkspaceDecorator"&gt;&lt;/View&gt;
      &lt;/View&gt;
  &lt;/Alloy&gt;
  </code></pre>
  
  <p><strong>EDIT 1</strong>
  And the code for this controller look like this:
      //get arguments passed to controller
      var args = arguments[0] || {};
      //load script to handle image cache
      var cacheImage = require('cacheImage');
      //is this a simple or complex entry
      var simple = args.simple || 0;</p>
  
  <pre><code>//set values for this controller
  $.WorkspaceNameLastName.text = args.user.name;
  var created_at = args.created_at;
  created_at = created_at.substring(0, created_at.indexOf('T'));
  created_at = created_at.replace('-', '/');
  created_at = created_at.replace('-', '/');
  $.WorkspaceDate.text = created_at;
  try
  {
      cacheImage('cachedImages_UserImages_Thumb', args.user.avatar.thumb, $.WorkspaceProfileImageView, null);
  }
  catch(error)
  {
      Ti.API.info(error);
  }
  //set correct width based on device screen
  var workspaceWidth = Ti.Platform.displayCaps.platformWidth;
  $.GlobalWorkspace.setWidth(workspaceWidth);
  //if user pic is tapped, open profile
  $.WorkspaceProfileImage.addEventListener("singletap", function(e) {
      Alloy.createController("UserProfile", {
          peopleObject:args.user
      });
  });
  //arguments for like/dislike controller
  var likenessArgs = {
      data : args
  };
  //arguments for text controller
  var textArgs = {
      id : args.id,
      text : L(args.action),
      simple : true,
      index : args.index,
      simple : simple
  };
  //function to get the correct name of the class according to internationalization
  function getClass(e)
  {
      switch(e)
      {
          case "Group":
              return L('group_string');
          case "Event":
              return L('event_string');
          case "Todo":
              return L('todo_list_string');
          case "TodoTask":
              return L('todo_string');
          case "Album":
              return L('album_string');
          case "AlbumPhoto":
              return L('photo_string');
          case "User":
              return L('user_string');
  
      }
  }
  //hide target user arrow image 
  $.TargetImageView.setOpacity(0);
  //function to open viewer according to class
  function openViewer(type)
  {
      switch(type)
      {
          case "User":
              Alloy.createController("UserProfile", {
                  peopleObject : {
                      veramiko_id : args.target.veramiko_id
                  }
              }); 
          break;
          case "Event":
              Alloy.createController("Event_Details", {
                  evento : {
                      veramiko_id : args.target.veramiko_id
                  }
              }); 
          break;
          case "Group":
              Alloy.createController("Group_Profile", {
                  groupItem : {
                      veramiko_id : args.target.veramiko_id
                  }
              }); 
          break;
          case "Album":
              Alloy.createController("PhotoAlbumViewer", {
                  album : {
                      veramiko_id : args.target.veramiko_id
                  }
              }); 
          break;
      }
  }
  //arguments for comments controller
  var wall_posts_count = {};
  wall_posts_count.wall_posts_count = parseInt(args.wall_posts_count);
  wall_posts_count.id = args.id;
  //variable to store attachment controller
  var streamItemImageView;
  //check if data received has a secondary target
  if ( typeof args.secondary_target !== 'undefined') 
  {
      //check if secondary target has a comment
      if ( typeof args.secondary_target.comment !== 'undefined') 
      {
              //construct the comment according to the context
          var comment = args.secondary_target.comment;
          textArgs.text = comment;
              //check if user target is the same as logged user
          if(args.target.veramiko_id !== Ti.App.Properties.getObject('loggedUser').user.veramiko_id)
          {
              if(args.target.veramiko_id !== args.user.veramiko_id)
              {
                              //since there's a target display the necessary info for this target and the arrow target image
                  $.WorkspaceTarget.text = args.target.name;
                  $.WorkspaceTarget.width = $.WorkspaceTarget.toImage().width;
                  $.TargetImageView.image = Ti.Filesystem.resourcesDirectory + 'images/targetArrow.png';
                  $.TargetImageView.setOpacity(1);
                  $.WorkspaceTarget.addEventListener('singletap', function(e){
                      openViewer(args.target.class);
                  });
              }
          }
      }
      //check if there's an attachment
      if ( typeof args.secondary_target.attachment !== 'undefined') 
      {
              //create attachment controller
          streamItemImageView = Alloy.createController("Stream_Item_Image", {
              attachment : args.secondary_target.attachment,
              comment : args.secondary_target.comment,
              created_at : args.created_at,
              updated_at : args.updated_at
          }).getView();
          $.WorkspaceContent.add(streamItemImageView);
      }
  }
  // since there's no secondary target, it's a text post
  else
  {
      var comment = args.target.name;
      comment = "\"" + comment + "\"";
      textArgs.text = textArgs.text + ': ' + comment;
  }
  // create like/dislike controller
  var streamItemLikeDislikeView = Alloy.createController("Stream_Item_LikeDislike", likenessArgs);
  // variable for user mentions in post
  var streamItemMentions;
  // check if there's any user mentions in this post
  var mentionsArgs;
  if(typeof args.mentions !== 'undefined')
  {
      // since there're user mentions, create the controller and the arguments for it
      mentionsArgs = {
          mentions : args.mentions
      };
      streamItemMentions = Alloy.createController("Stream_Item_Mentions", mentionsArgs);  
  }
  // create comments controller
  var streamItemCommentsView = Alloy.createController("Stream_Item_Comments", wall_posts_count);
  // callback object with functions that update the comments count
  var callbackObject = {};
  callbackObject.increaseNumberOfComments = streamItemCommentsView.increaseNumberOfComments;
  textArgs.callbacks = callbackObject;
  // create text controller
  var streamItemTextView = Alloy.createController("Stream_Item_Text", textArgs).getView();
  $.WorkspaceContent.add(streamItemTextView);
  //if there're user mentions, add the controller
  if(typeof args.mentions !== 'undefined')
  {
      $.WorkspaceContent.add(streamItemMentions.getView());
  }
  // if it's a simple entry add the comments controller
  if(args.simple)
  {
      $.WorkspaceContent.add(streamItemCommentsView.getView());
  }
  $.WorkspaceContent.add(streamItemLikeDislikeView.getView());
  </code></pre>
  
  <p><strong>END EDIT 1</strong></p>
  
  <p>In the view WorkspaceContent more views are added depending if comments had been made or images had been attached to the entry.</p>
  
  <p><strong>EDIT 2</strong>
  Also every Stream_Item controller contains a text controller and a like/dislike controller, depending on whether the entry has user mentions, an attachment and/or is a simple entry, additional controllers are added.</p>
  
  <p>I must make clear that this Stream_Item controller and its added controllers work just fine in other Windows, but on this specific kind of Window it gets buggy.</p>
  
  <p><strong>END EDIT 2</strong></p>
  
  <p>The problem I'm having in Android is that the touch events on the views contained on the rows are blocked for some reason, I have known about the performance issues on Android when dealing with TableViews and after testing in several devices, that unless the element that has the touch event has finished rendering the user will be allowed to interact with the views with touch events. This is only a guess but I think is somehow accurate to my problem. Another problem I experience is the images contained in the table rows sometimes disappear while scrolling or interacting with other elements of the table.</p>
  
  <p>I switched from using ScrollView mainly because the methods for adding elements to a table view are way more practical than the ones found in ScrollView (i.e. the ability to insert rows at an specified position and such behaviour not found in ScrollView). I tried List View but the implementation in Alloy is so confusing and the documentation isn't clear on how to change the contents dynamically using other Alloy views.</p>
  
  <p>Is there a way to workaround these issues? How can I make the images to stop disappearing? How can I bypass this touch block?</p>