The reason it’s not included is a resource issue as well as a divergence issue. When we did the tutorial for widget.newTextField() a few months ago, it was very iOS like. It would add a lot of complexity to skin it for Android as well. We are a small company with limited engineering resources. There is only so much bandwidth.
So we did what we thought was the next best thing, we wrote a tutorial to show you how to make your own widget.newTextField() that was almost drop-in ready code. I have a file called widget-extras.lua that I put these in and build up my library of my own widgets add ons.
I probably should finish cleaning it up after this discovery and drop it in the community code for people to have access too. But here is the current version:
function widget.newTextField(options) local customOptions = options or {} local opt = {} opt.left = customOptions.left or 0 opt.top = customOptions.top or 0 opt.x = customOptions.x or 0 opt.y = customOptions.y or 0 opt.width = customOptions.width or (display.contentWidth \* 0.75) opt.height = customOptions.height or 20 opt.id = customOptions.id opt.listener = customOptions.listener or nil opt.text = customOptions.text or "" opt.inputType = customOptions.inputType or "default" opt.isSecure = customOptions.isSecure or false opt.font = customOptions.font or native.systemFont opt.fontSize = customOptions.fontSize or opt.height \* 0.67 opt.fontColor = customOptions.fontColor or { 0.25, 0.25, 0.25 } opt.placeholder = customOptions.placeholder or nil opt.label = customOptions.label or "" opt.labelWidth = customOptions.labelWidth or opt.width \* 0.10 opt.labelFont = customOptions.labelFont or native.systemFontBold opt.labelFontSize = customOptions.labelFontSize or opt.fontSize opt.labelFontColor = customOptions.labelFontColor or { 0, 0, 0 } -- Vector options opt.strokeWidth = customOptions.strokeWidth or 2 opt.cornerRadius = customOptions.cornerRadius or opt.height \* 0.33 or 10 opt.strokeColor = customOptions.strokeColor or {0, 0, 0} opt.backgroundColor = customOptions.backgroundColor or { 1, 1, 1 } local field = display.newGroup() local deviceScale = (display.pixelWidth / display.contentWidth) \* 0.5 local bgWidth = opt.width local background = display.newRoundedRect( 0, 0, bgWidth, opt.height, opt.cornerRadius ) background:setFillColor(unpack(opt.backgroundColor)) background.strokeWidth = opt.strokeWidth background.stroke = opt.strokeColor field:insert(background) if opt.left then field.x = opt.left + opt.width \* 0.5 elseif opt.left then field.x = opt.x end if opt.top then field.y = opt.top + opt.height \* 0.5 elseif opt.top then field.y = opt.y end -- -- Support adding a label. -- iOS 6 and earlier and Android draw the Label above the field. -- iOS 7 draws it in the field. local labelParameters = { x = 0, y = 0, text = opt.label, width = opt.labelWidth, height = 0, font = opt.labelFont, fontSize = opt.labelFontSize, align = "left" } local fieldLabel = display.newText(labelParameters) fieldLabel:setFillColor(unpack(opt.labelFontColor)) fieldLabel.x = background.x - bgWidth / 2 + opt.cornerRadius + opt.labelWidth \* 0.5 fieldLabel.y = background.y if not widget.isSeven() then fieldLabel.y = background.y + opt.height + 5 end field:insert(fieldLabel) -- create the native.newTextField to handle the input local tHeight = opt.height - opt.strokeWidth \* 2 - 4 if "Android" == system.getInfo("platformName") then -- -- Older Android devices have extra "chrome" that needs to be compesnated for. -- tHeight = tHeight + 10 end local labelPadding = 0 if widget.isSeven() then labelPadding = opt.labelWidth end field.textField = native.newTextField(0, 0, opt.width - opt.cornerRadius - labelPadding, tHeight ) field.textField.x = 0 + labelPadding field.textField.y = 0 -- tHeight / 2 field.textField.anchorX = 0.5 field.textField.hasBackground = false field.textField.inputType = opt.inputType field.textField.text = opt.text field.textField.isSecure = opt.isSecure print(field.x, field.y, field.textField.x, field.textField.y) print(opt.listener, type(opt.listener)) if opt.listener and type(opt.listener) == "function" then field.textField.\_listener = opt.listener end field.textField.placeholder = opt.placeholder field:insert(field.textField) -- Function to listen for textbox events function field.textField:\_inputListener( event ) local phase = event.phase if "began" == phase then -- make sure we are in a display group -- the trick is our master object is a group, so we need to make sure our -- grandparent isn't the stage if display.getCurrentStage() ~= self.parent.parent then -- make a guess at the keyboard height. local kbHeight = 0.5 \* display.contentHeight local fieldLoc = 0.25 \* display.contentHeight -- scroll into view if self.y \> kbHeight then self.yOrig = self.y transition.to(self.parent.parent, {time=500, y = fieldLoc}) end end elseif "submitted" == phase or "ended" == phase then -- Hide keyboard if self.yOrig ~= self.y then -- we have been scrolled transition.to(self.parent.parent, {time=500, y = self.yOrig}) end end -- If there is a listener defined, execute it if self.\_listener then self.\_listener( event ) end end field.textField.userInput = field.textField.\_inputListener field.textField:addEventListener( "userInput" ) print(opt.font, opt.fontSize, deviceScale) field.textField.font = native.newFont( opt.font, opt.fontSize \* deviceScale ) field.textField.size = opt.fontSize \* deviceScale function field.finalize( event ) Runtime:removeEventListener( "enterFrame", syncFields ) field.textField:removeSelf() field.textField = nil end field:addEventListener( "finalize" ) return field end
And to invoke it:
addressField = widget.newTextField({ width = display.contentWidth, height = 30, cornerRadius = 6, strokeWidth = 0, text = "", fontSize = 14, placeholder = "Address", font = "HelveticaNeue-Light", labelFont = "HelveticaNeue", labelFontSize = 14, labelWidth = 60, listener = textFieldHandler, label = "Address" }) -- Hide the native part of this until we need to show it on the screen. addressField.x = display.contentCenterX addressField.y = navBar.height + 15 sceneGroup:insert(addressField)